From 07ab8bb5c24bf5d42a0490f97358541eb20299b3 Mon Sep 17 00:00:00 2001 From: heister Date: Mon, 3 Dec 2012 19:18:03 +0000 Subject: [PATCH] merge indent commit and indent manually git-svn-id: https://svn.dealii.org/branches/branch_bigger_global_dof_indices_3@27732 0785d39b-7218-0410-832d-ea1e28bc413d --- .../examples/doxygen/block_matrix_array.cc | 28 +- .../compressed_block_sparsity_pattern.cc | 32 +- deal.II/examples/doxygen/product_matrix.cc | 12 +- .../examples/doxygen/theta_timestepping.cc | 66 +- deal.II/examples/step-1/step-1.cc | 514 +- deal.II/examples/step-10/step-10.cc | 894 +- deal.II/examples/step-11/step-11.cc | 992 +- deal.II/examples/step-12/step-12.cc | 1000 +- deal.II/examples/step-13/step-13.cc | 2880 +-- deal.II/examples/step-14/step-14.cc | 4957 ++-- deal.II/examples/step-15/step-15.cc | 1192 +- deal.II/examples/step-16/step-16.cc | 1146 +- deal.II/examples/step-17/step-17.cc | 1668 +- deal.II/examples/step-18/step-18.cc | 4538 ++-- deal.II/examples/step-19/step-19.cc | 864 +- deal.II/examples/step-2/step-2.cc | 682 +- deal.II/examples/step-20/step-20.cc | 1498 +- deal.II/examples/step-21/step-21.cc | 1131 +- deal.II/examples/step-22/step-22.cc | 1638 +- deal.II/examples/step-23/step-23.cc | 796 +- deal.II/examples/step-24/step-24.cc | 663 +- deal.II/examples/step-25/step-25.cc | 1060 +- deal.II/examples/step-26/step-26.cc | 808 +- deal.II/examples/step-27/step-27.cc | 1126 +- deal.II/examples/step-28/step-28.cc | 2737 +-- deal.II/examples/step-29/step-29.cc | 1910 +- deal.II/examples/step-3/step-3.cc | 1420 +- deal.II/examples/step-30/step-30.cc | 934 +- deal.II/examples/step-31/step-31.cc | 3546 +-- deal.II/examples/step-32/step-32.cc | 5186 ++--- deal.II/examples/step-33/step-33.cc | 3696 +-- deal.II/examples/step-34/step-34.cc | 1812 +- deal.II/examples/step-35/step-35.cc | 1470 +- deal.II/examples/step-36/step-36.cc | 692 +- deal.II/examples/step-37/step-37.cc | 1774 +- deal.II/examples/step-38/step-38.cc | 600 +- deal.II/examples/step-39/step-39.cc | 1450 +- deal.II/examples/step-4/step-4.cc | 1010 +- deal.II/examples/step-40/step-40.cc | 1180 +- deal.II/examples/step-41/step-41.cc | 858 +- deal.II/examples/step-42/step-42.cc | 444 +- deal.II/examples/step-43/step-43.cc | 2158 +- deal.II/examples/step-44/step-44.cc | 3139 +-- deal.II/examples/step-45/step-45.cc | 488 +- deal.II/examples/step-46/step-46.cc | 1012 +- deal.II/examples/step-47/step-47.cc | 302 +- deal.II/examples/step-48/step-48.cc | 708 +- deal.II/examples/step-5/step-5.cc | 1396 +- deal.II/examples/step-6/step-6.cc | 1688 +- deal.II/examples/step-7/step-7.cc | 2861 +-- deal.II/examples/step-8/step-8.cc | 1574 +- deal.II/examples/step-9/step-9.cc | 2930 +-- deal.II/include/deal.II/algorithms/newton.h | 336 +- .../deal.II/algorithms/newton.templates.h | 63 +- deal.II/include/deal.II/algorithms/operator.h | 180 +- .../deal.II/algorithms/operator.templates.h | 40 +- .../deal.II/algorithms/theta_timestepping.h | 572 +- .../algorithms/theta_timestepping.templates.h | 34 +- .../deal.II/algorithms/timestep_control.h | 374 +- .../deal.II/base/auto_derivative_function.h | 360 +- .../deal.II/base/conditional_ostream.h | 154 +- .../include/deal.II/base/convergence_table.h | 387 +- deal.II/include/deal.II/base/data_out_base.h | 5274 ++--- .../include/deal.II/base/derivative_form.h | 252 +- deal.II/include/deal.II/base/event.h | 312 +- deal.II/include/deal.II/base/exceptions.h | 1238 +- deal.II/include/deal.II/base/flow_function.h | 504 +- deal.II/include/deal.II/base/function.h | 1032 +- .../include/deal.II/base/function_bessel.h | 46 +- .../deal.II/base/function_derivative.h | 234 +- deal.II/include/deal.II/base/function_lib.h | 1948 +- .../include/deal.II/base/function_parser.h | 516 +- deal.II/include/deal.II/base/function_time.h | 60 +- deal.II/include/deal.II/base/geometry_info.h | 3298 +-- deal.II/include/deal.II/base/index_set.h | 1172 +- deal.II/include/deal.II/base/job_identifier.h | 96 +- deal.II/include/deal.II/base/logstream.h | 1118 +- .../include/deal.II/base/memory_consumption.h | 508 +- .../include/deal.II/base/mg_level_object.h | 182 +- deal.II/include/deal.II/base/mpi.h | 490 +- .../include/deal.II/base/multithread_info.h | 152 +- deal.II/include/deal.II/base/named_data.h | 358 +- deal.II/include/deal.II/base/numbers.h | 390 +- deal.II/include/deal.II/base/parallel.h | 1100 +- .../include/deal.II/base/parameter_handler.h | 3545 +-- .../include/deal.II/base/parsed_function.h | 448 +- deal.II/include/deal.II/base/partitioner.h | 732 +- deal.II/include/deal.II/base/path_search.h | 432 +- deal.II/include/deal.II/base/point.h | 323 +- deal.II/include/deal.II/base/polynomial.h | 1322 +- .../include/deal.II/base/polynomial_space.h | 370 +- .../include/deal.II/base/polynomials_abf.h | 238 +- .../include/deal.II/base/polynomials_adini.h | 222 +- .../include/deal.II/base/polynomials_bdm.h | 234 +- .../deal.II/base/polynomials_nedelec.h | 198 +- deal.II/include/deal.II/base/polynomials_p.h | 108 +- .../deal.II/base/polynomials_raviart_thomas.h | 208 +- deal.II/include/deal.II/base/qprojector.h | 746 +- deal.II/include/deal.II/base/quadrature.h | 478 +- deal.II/include/deal.II/base/quadrature_lib.h | 408 +- .../deal.II/base/quadrature_selector.h | 120 +- deal.II/include/deal.II/base/smartpointer.h | 430 +- deal.II/include/deal.II/base/subscriptor.h | 414 +- .../include/deal.II/base/symmetric_tensor.h | 2362 +- .../deal.II/base/synchronous_iterator.h | 140 +- deal.II/include/deal.II/base/table.h | 2954 +-- deal.II/include/deal.II/base/table_handler.h | 1142 +- deal.II/include/deal.II/base/table_indices.h | 464 +- .../deal.II/base/template_constraints.h | 174 +- deal.II/include/deal.II/base/tensor.h | 953 +- deal.II/include/deal.II/base/tensor_base.h | 1228 +- .../include/deal.II/base/tensor_function.h | 148 +- .../deal.II/base/tensor_product_polynomials.h | 798 +- .../deal.II/base/thread_local_storage.h | 16 +- .../include/deal.II/base/thread_management.h | 6129 ++--- deal.II/include/deal.II/base/timer.h | 726 +- deal.II/include/deal.II/base/types.h | 292 +- deal.II/include/deal.II/base/utilities.h | 1034 +- deal.II/include/deal.II/base/vector_slice.h | 176 +- deal.II/include/deal.II/base/vectorization.h | 1733 +- deal.II/include/deal.II/base/work_stream.h | 774 +- .../deal.II/distributed/grid_refinement.h | 162 +- .../deal.II/distributed/solution_transfer.h | 448 +- deal.II/include/deal.II/distributed/tria.h | 1600 +- deal.II/include/deal.II/dofs/block_info.h | 322 +- deal.II/include/deal.II/dofs/dof_accessor.h | 3928 ++-- .../deal.II/dofs/dof_accessor.templates.h | 5130 ++-- deal.II/include/deal.II/dofs/dof_faces.h | 236 +- deal.II/include/deal.II/dofs/dof_handler.h | 1624 +- .../include/deal.II/dofs/dof_handler_policy.h | 162 +- .../deal.II/dofs/dof_iterator_selector.h | 168 +- deal.II/include/deal.II/dofs/dof_levels.h | 136 +- deal.II/include/deal.II/dofs/dof_objects.h | 304 +- .../include/deal.II/dofs/dof_renumbering.h | 1624 +- deal.II/include/deal.II/dofs/dof_tools.h | 4184 ++-- deal.II/include/deal.II/dofs/function_map.h | 20 +- deal.II/include/deal.II/dofs/number_cache.h | 164 +- deal.II/include/deal.II/fe/block_mask.h | 26 +- deal.II/include/deal.II/fe/component_mask.h | 26 +- deal.II/include/deal.II/fe/fe.h | 4936 ++-- deal.II/include/deal.II/fe/fe_abf.h | 470 +- deal.II/include/deal.II/fe/fe_base.h | 1318 +- deal.II/include/deal.II/fe/fe_bdm.h | 134 +- deal.II/include/deal.II/fe/fe_dg_vector.h | 258 +- .../deal.II/fe/fe_dg_vector.templates.h | 34 +- deal.II/include/deal.II/fe/fe_dgp.h | 532 +- deal.II/include/deal.II/fe/fe_dgp_monomial.h | 498 +- .../include/deal.II/fe/fe_dgp_nonparametric.h | 1064 +- deal.II/include/deal.II/fe/fe_dgq.h | 698 +- deal.II/include/deal.II/fe/fe_face.h | 86 +- deal.II/include/deal.II/fe/fe_nedelec.h | 464 +- deal.II/include/deal.II/fe/fe_nothing.h | 530 +- deal.II/include/deal.II/fe/fe_poly.h | 582 +- .../include/deal.II/fe/fe_poly.templates.h | 158 +- deal.II/include/deal.II/fe/fe_poly_face.h | 510 +- .../deal.II/fe/fe_poly_face.templates.h | 112 +- deal.II/include/deal.II/fe/fe_poly_tensor.h | 396 +- deal.II/include/deal.II/fe/fe_q.h | 742 +- .../include/deal.II/fe/fe_q_hierarchical.h | 622 +- .../include/deal.II/fe/fe_raviart_thomas.h | 534 +- deal.II/include/deal.II/fe/fe_system.h | 1968 +- deal.II/include/deal.II/fe/fe_tools.h | 2414 +- deal.II/include/deal.II/fe/fe_update_flags.h | 396 +- deal.II/include/deal.II/fe/fe_values.h | 7451 +++--- .../include/deal.II/fe/fe_values_extractors.h | 230 +- deal.II/include/deal.II/fe/mapping.h | 1404 +- deal.II/include/deal.II/fe/mapping_c1.h | 142 +- .../include/deal.II/fe/mapping_cartesian.h | 336 +- deal.II/include/deal.II/fe/mapping_q.h | 1050 +- deal.II/include/deal.II/fe/mapping_q1.h | 1410 +- .../include/deal.II/fe/mapping_q1_eulerian.h | 188 +- .../include/deal.II/fe/mapping_q_eulerian.h | 282 +- .../include/deal.II/grid/filtered_iterator.h | 978 +- deal.II/include/deal.II/grid/grid_generator.h | 1870 +- deal.II/include/deal.II/grid/grid_in.h | 668 +- deal.II/include/deal.II/grid/grid_out.h | 2942 +-- .../include/deal.II/grid/grid_refinement.h | 422 +- .../include/deal.II/grid/grid_reordering.h | 94 +- .../deal.II/grid/grid_reordering_internal.h | 1464 +- deal.II/include/deal.II/grid/grid_tools.h | 1714 +- deal.II/include/deal.II/grid/intergrid_map.h | 220 +- .../include/deal.II/grid/persistent_tria.h | 354 +- deal.II/include/deal.II/grid/tria.h | 5396 ++--- deal.II/include/deal.II/grid/tria_accessor.h | 5500 ++--- .../deal.II/grid/tria_accessor.templates.h | 1885 +- deal.II/include/deal.II/grid/tria_boundary.h | 1026 +- .../include/deal.II/grid/tria_boundary_lib.h | 1310 +- deal.II/include/deal.II/grid/tria_faces.h | 204 +- deal.II/include/deal.II/grid/tria_iterator.h | 1586 +- .../deal.II/grid/tria_iterator.templates.h | 264 +- .../include/deal.II/grid/tria_iterator_base.h | 20 +- .../deal.II/grid/tria_iterator_selector.h | 242 +- deal.II/include/deal.II/grid/tria_levels.h | 540 +- deal.II/include/deal.II/grid/tria_object.h | 182 +- deal.II/include/deal.II/grid/tria_objects.h | 1348 +- deal.II/include/deal.II/hp/dof_faces.h | 160 +- deal.II/include/deal.II/hp/dof_handler.h | 1613 +- deal.II/include/deal.II/hp/dof_levels.h | 344 +- deal.II/include/deal.II/hp/dof_objects.h | 635 +- deal.II/include/deal.II/hp/fe_collection.h | 832 +- deal.II/include/deal.II/hp/fe_values.h | 1968 +- .../include/deal.II/hp/mapping_collection.h | 258 +- deal.II/include/deal.II/hp/q_collection.h | 268 +- .../include/deal.II/integrators/advection.h | 692 +- .../include/deal.II/integrators/divergence.h | 382 +- .../include/deal.II/integrators/elasticity.h | 238 +- deal.II/include/deal.II/integrators/l2.h | 172 +- deal.II/include/deal.II/integrators/laplace.h | 720 +- deal.II/include/deal.II/integrators/maxwell.h | 698 +- deal.II/include/deal.II/integrators/patches.h | 26 +- deal.II/include/deal.II/lac/arpack_solver.h | 500 +- deal.II/include/deal.II/lac/block_indices.h | 454 +- deal.II/include/deal.II/lac/block_list.h | 706 +- deal.II/include/deal.II/lac/block_matrix.h | 84 +- .../include/deal.II/lac/block_matrix_array.h | 950 +- .../include/deal.II/lac/block_matrix_base.h | 2654 +-- .../include/deal.II/lac/block_sparse_matrix.h | 814 +- .../lac/block_sparse_matrix.templates.h | 28 +- .../deal.II/lac/block_sparse_matrix_ez.h | 566 +- .../lac/block_sparse_matrix_ez.templates.h | 44 +- .../deal.II/lac/block_sparsity_pattern.h | 1834 +- deal.II/include/deal.II/lac/block_vector.h | 794 +- .../deal.II/lac/block_vector.templates.h | 22 +- .../include/deal.II/lac/block_vector_base.h | 2375 +- .../include/deal.II/lac/chunk_sparse_matrix.h | 2292 +- .../lac/chunk_sparse_matrix.templates.h | 565 +- .../deal.II/lac/chunk_sparsity_pattern.h | 1614 +- .../lac/compressed_set_sparsity_pattern.h | 594 +- .../lac/compressed_simple_sparsity_pattern.h | 732 +- .../deal.II/lac/compressed_sparsity_pattern.h | 782 +- .../include/deal.II/lac/constraint_matrix.h | 3424 +-- .../deal.II/lac/constraint_matrix.templates.h | 2003 +- deal.II/include/deal.II/lac/eigen.h | 356 +- deal.II/include/deal.II/lac/exceptions.h | 54 +- deal.II/include/deal.II/lac/filtered_matrix.h | 978 +- deal.II/include/deal.II/lac/full_matrix.h | 2726 +-- .../deal.II/lac/full_matrix.templates.h | 940 +- deal.II/include/deal.II/lac/householder.h | 268 +- deal.II/include/deal.II/lac/identity_matrix.h | 194 +- .../include/deal.II/lac/iterative_inverse.h | 114 +- .../include/deal.II/lac/lapack_full_matrix.h | 1276 +- deal.II/include/deal.II/lac/lapack_support.h | 200 +- deal.II/include/deal.II/lac/matrix_block.h | 1204 +- deal.II/include/deal.II/lac/matrix_iterator.h | 232 +- deal.II/include/deal.II/lac/matrix_lib.h | 820 +- .../deal.II/lac/matrix_lib.templates.h | 66 +- deal.II/include/deal.II/lac/matrix_out.h | 502 +- .../deal.II/lac/parallel_block_vector.h | 490 +- deal.II/include/deal.II/lac/parallel_vector.h | 2174 +- .../deal.II/lac/parallel_vector.templates.h | 184 +- .../deal.II/lac/petsc_block_sparse_matrix.h | 518 +- .../include/deal.II/lac/petsc_block_vector.h | 596 +- .../include/deal.II/lac/petsc_full_matrix.h | 66 +- .../include/deal.II/lac/petsc_matrix_base.h | 2548 +- .../include/deal.II/lac/petsc_matrix_free.h | 642 +- .../lac/petsc_parallel_block_sparse_matrix.h | 488 +- .../deal.II/lac/petsc_parallel_block_vector.h | 606 +- .../lac/petsc_parallel_sparse_matrix.h | 866 +- .../deal.II/lac/petsc_parallel_vector.h | 874 +- .../include/deal.II/lac/petsc_precondition.h | 1908 +- deal.II/include/deal.II/lac/petsc_solver.h | 2266 +- .../include/deal.II/lac/petsc_sparse_matrix.h | 534 +- deal.II/include/deal.II/lac/petsc_vector.h | 482 +- .../include/deal.II/lac/petsc_vector_base.h | 1718 +- deal.II/include/deal.II/lac/pointer_matrix.h | 994 +- deal.II/include/deal.II/lac/precondition.h | 1460 +- .../include/deal.II/lac/precondition_block.h | 1718 +- .../lac/precondition_block.templates.h | 296 +- .../deal.II/lac/precondition_block_base.h | 804 +- .../deal.II/lac/precondition_selector.h | 142 +- .../include/deal.II/lac/relaxation_block.h | 798 +- .../deal.II/lac/relaxation_block.templates.h | 88 +- deal.II/include/deal.II/lac/schur_matrix.h | 278 +- deal.II/include/deal.II/lac/shifted_matrix.h | 214 +- deal.II/include/deal.II/lac/slepc_solver.h | 1170 +- .../lac/slepc_spectral_transformation.h | 530 +- deal.II/include/deal.II/lac/solver.h | 136 +- deal.II/include/deal.II/lac/solver_bicgstab.h | 438 +- deal.II/include/deal.II/lac/solver_cg.h | 523 +- deal.II/include/deal.II/lac/solver_control.h | 927 +- deal.II/include/deal.II/lac/solver_gmres.h | 793 +- deal.II/include/deal.II/lac/solver_minres.h | 272 +- deal.II/include/deal.II/lac/solver_qmrs.h | 363 +- .../include/deal.II/lac/solver_relaxation.h | 108 +- .../include/deal.II/lac/solver_richardson.h | 346 +- deal.II/include/deal.II/lac/solver_selector.h | 290 +- .../deal.II/lac/sparse_decomposition.h | 628 +- .../lac/sparse_decomposition.templates.h | 117 +- deal.II/include/deal.II/lac/sparse_direct.h | 2022 +- deal.II/include/deal.II/lac/sparse_ilu.h | 256 +- .../deal.II/lac/sparse_ilu.templates.h | 256 +- deal.II/include/deal.II/lac/sparse_matrix.h | 4164 ++-- .../deal.II/lac/sparse_matrix.templates.h | 661 +- .../include/deal.II/lac/sparse_matrix_ez.h | 2139 +- .../deal.II/lac/sparse_matrix_ez.templates.h | 134 +- deal.II/include/deal.II/lac/sparse_mic.h | 262 +- .../deal.II/lac/sparse_mic.templates.h | 121 +- deal.II/include/deal.II/lac/sparse_vanka.h | 591 +- .../deal.II/lac/sparse_vanka.templates.h | 514 +- .../include/deal.II/lac/sparsity_pattern.h | 3128 +-- deal.II/include/deal.II/lac/sparsity_tools.h | 404 +- .../include/deal.II/lac/swappable_vector.h | 434 +- .../deal.II/lac/swappable_vector.templates.h | 138 +- .../include/deal.II/lac/transpose_matrix.h | 204 +- .../include/deal.II/lac/tridiagonal_matrix.h | 596 +- .../lac/trilinos_block_sparse_matrix.h | 1164 +- .../deal.II/lac/trilinos_block_vector.h | 786 +- .../lac/trilinos_parallel_block_vector.h | 724 +- .../deal.II/lac/trilinos_precondition.h | 2780 +-- deal.II/include/deal.II/lac/trilinos_solver.h | 1152 +- .../deal.II/lac/trilinos_sparse_matrix.h | 4688 ++-- .../deal.II/lac/trilinos_sparsity_pattern.h | 2604 +-- deal.II/include/deal.II/lac/trilinos_vector.h | 1218 +- .../deal.II/lac/trilinos_vector_base.h | 2124 +- deal.II/include/deal.II/lac/vector.h | 1938 +- .../include/deal.II/lac/vector.templates.h | 294 +- deal.II/include/deal.II/lac/vector_memory.h | 572 +- deal.II/include/deal.II/lac/vector_view.h | 282 +- .../include/deal.II/matrix_free/dof_info.h | 1120 +- .../deal.II/matrix_free/dof_info.templates.h | 3651 +-- .../deal.II/matrix_free/fe_evaluation.h | 4699 ++-- .../deal.II/matrix_free/helper_functions.h | 300 +- .../deal.II/matrix_free/mapping_info.h | 756 +- .../matrix_free/mapping_info.templates.h | 1702 +- .../include/deal.II/matrix_free/matrix_free.h | 2064 +- .../matrix_free/matrix_free.templates.h | 723 +- .../include/deal.II/matrix_free/shape_info.h | 300 +- .../matrix_free/shape_info.templates.h | 268 +- .../include/deal.II/meshworker/assembler.h | 1706 +- deal.II/include/deal.II/meshworker/dof_info.h | 554 +- .../deal.II/meshworker/dof_info.templates.h | 20 +- .../include/deal.II/meshworker/functional.h | 326 +- .../deal.II/meshworker/integration_info.h | 1550 +- .../meshworker/integration_info.templates.h | 82 +- .../deal.II/meshworker/local_integrator.h | 146 +- .../deal.II/meshworker/local_results.h | 606 +- deal.II/include/deal.II/meshworker/loop.h | 416 +- deal.II/include/deal.II/meshworker/output.h | 282 +- deal.II/include/deal.II/meshworker/simple.h | 1402 +- .../include/deal.II/meshworker/vector_info.h | 184 +- .../deal.II/meshworker/vector_selector.h | 858 +- .../meshworker/vector_selector.templates.h | 128 +- deal.II/include/deal.II/multigrid/mg_base.h | 232 +- .../deal.II/multigrid/mg_block_smoother.h | 294 +- deal.II/include/deal.II/multigrid/mg_coarse.h | 264 +- .../deal.II/multigrid/mg_constrained_dofs.h | 352 +- .../deal.II/multigrid/mg_dof_accessor.h | 1262 +- .../deal.II/multigrid/mg_dof_handler.h | 878 +- .../multigrid/mg_dof_iterator_selector.h | 132 +- deal.II/include/deal.II/multigrid/mg_matrix.h | 376 +- .../include/deal.II/multigrid/mg_smoother.h | 1154 +- deal.II/include/deal.II/multigrid/mg_tools.h | 314 +- .../include/deal.II/multigrid/mg_transfer.h | 412 +- .../deal.II/multigrid/mg_transfer.templates.h | 260 +- .../deal.II/multigrid/mg_transfer_block.h | 828 +- .../multigrid/mg_transfer_block.templates.h | 64 +- .../deal.II/multigrid/mg_transfer_component.h | 702 +- .../mg_transfer_component.templates.h | 118 +- deal.II/include/deal.II/multigrid/multigrid.h | 1156 +- .../deal.II/multigrid/multigrid.templates.h | 232 +- .../numerics/data_component_interpretation.h | 82 +- deal.II/include/deal.II/numerics/data_out.h | 1952 +- .../include/deal.II/numerics/data_out_faces.h | 382 +- .../deal.II/numerics/data_out_rotation.h | 244 +- .../include/deal.II/numerics/data_out_stack.h | 654 +- .../deal.II/numerics/data_postprocessor.h | 442 +- .../numerics/derivative_approximation.h | 1022 +- .../deal.II/numerics/dof_output_operator.h | 16 +- .../numerics/dof_output_operator.templates.h | 4 +- .../deal.II/numerics/dof_print_solver_step.h | 92 +- .../deal.II/numerics/error_estimator.h | 1106 +- .../deal.II/numerics/fe_field_function.h | 756 +- .../numerics/fe_field_function.templates.h | 215 +- deal.II/include/deal.II/numerics/histogram.h | 357 +- .../include/deal.II/numerics/matrix_tools.h | 818 +- .../deal.II/numerics/point_value_history.h | 1264 +- .../deal.II/numerics/solution_transfer.h | 534 +- .../include/deal.II/numerics/time_dependent.h | 2981 +-- .../include/deal.II/numerics/vector_tools.h | 3298 +-- .../deal.II/numerics/vector_tools.templates.h | 5402 ++--- .../source/base/auto_derivative_function.cc | 409 +- deal.II/source/base/conditional_ostream.cc | 6 +- deal.II/source/base/config.cc | 16 +- deal.II/source/base/convergence_table.cc | 116 +- deal.II/source/base/data_out_base.cc | 4803 ++-- deal.II/source/base/event.cc | 14 +- deal.II/source/base/exceptions.cc | 392 +- deal.II/source/base/flow_function.cc | 284 +- deal.II/source/base/function.cc | 131 +- deal.II/source/base/function_derivative.cc | 186 +- deal.II/source/base/function_lib.cc | 1096 +- deal.II/source/base/function_lib_cutoff.cc | 44 +- deal.II/source/base/function_parser.cc | 174 +- deal.II/source/base/function_time.cc | 4 +- deal.II/source/base/geometry_info.cc | 1645 +- deal.II/source/base/index_set.cc | 163 +- deal.II/source/base/job_identifier.cc | 2 +- deal.II/source/base/logstream.cc | 220 +- deal.II/source/base/mpi.cc | 214 +- deal.II/source/base/multithread_info.cc | 26 +- deal.II/source/base/parallel.cc | 42 +- deal.II/source/base/parameter_handler.cc | 1596 +- deal.II/source/base/parsed_function.cc | 57 +- deal.II/source/base/partitioner.cc | 572 +- deal.II/source/base/path_search.cc | 66 +- deal.II/source/base/polynomial.cc | 825 +- deal.II/source/base/polynomial_space.cc | 76 +- deal.II/source/base/polynomials_abf.cc | 58 +- deal.II/source/base/polynomials_adini.cc | 36 +- deal.II/source/base/polynomials_bdm.cc | 118 +- deal.II/source/base/polynomials_nedelec.cc | 2262 +- deal.II/source/base/polynomials_p.cc | 54 +- .../source/base/polynomials_raviart_thomas.cc | 78 +- deal.II/source/base/quadrature.cc | 1263 +- deal.II/source/base/quadrature_lib.cc | 537 +- deal.II/source/base/quadrature_selector.cc | 16 +- deal.II/source/base/subscriptor.cc | 96 +- deal.II/source/base/symmetric_tensor.cc | 54 +- deal.II/source/base/table_handler.cc | 201 +- deal.II/source/base/tensor_function.cc | 4 +- .../source/base/tensor_product_polynomials.cc | 84 +- deal.II/source/base/thread_management.cc | 80 +- deal.II/source/base/timer.cc | 194 +- deal.II/source/base/utilities.cc | 180 +- deal.II/source/distributed/grid_refinement.cc | 558 +- .../source/distributed/solution_transfer.cc | 80 +- deal.II/source/distributed/tria.cc | 2596 +-- deal.II/source/dofs/block_info.cc | 56 +- deal.II/source/dofs/dof_accessor.cc | 227 +- deal.II/source/dofs/dof_handler.cc | 1631 +- deal.II/source/dofs/dof_handler_policy.cc | 1478 +- deal.II/source/dofs/dof_renumbering.cc | 1112 +- deal.II/source/dofs/dof_tools.cc | 4859 ++-- deal.II/source/dofs/number_cache.cc | 6 +- deal.II/source/fe/block_mask.cc | 4 +- deal.II/source/fe/component_mask.cc | 4 +- deal.II/source/fe/fe.cc | 556 +- deal.II/source/fe/fe_abf.cc | 402 +- deal.II/source/fe/fe_bdm.cc | 200 +- deal.II/source/fe/fe_data.cc | 134 +- deal.II/source/fe/fe_dg_vector.cc | 36 +- deal.II/source/fe/fe_dgp.cc | 94 +- deal.II/source/fe/fe_dgp_monomial.cc | 156 +- deal.II/source/fe/fe_dgp_nonparametric.cc | 230 +- deal.II/source/fe/fe_dgq.cc | 488 +- deal.II/source/fe/fe_face.cc | 26 +- deal.II/source/fe/fe_nedelec.cc | 8822 ++++--- deal.II/source/fe/fe_nothing.cc | 68 +- deal.II/source/fe/fe_poly.cc | 20 +- deal.II/source/fe/fe_poly_tensor.cc | 974 +- deal.II/source/fe/fe_q.cc | 1627 +- deal.II/source/fe/fe_q_hierarchical.cc | 1993 +- deal.II/source/fe/fe_raviart_thomas.cc | 364 +- deal.II/source/fe/fe_raviart_thomas_nodal.cc | 472 +- deal.II/source/fe/fe_system.cc | 1901 +- deal.II/source/fe/fe_tools.cc | 1889 +- deal.II/source/fe/fe_values.cc | 2620 +-- deal.II/source/fe/mapping.cc | 16 +- deal.II/source/fe/mapping_c1.cc | 177 +- deal.II/source/fe/mapping_cartesian.cc | 595 +- deal.II/source/fe/mapping_q.cc | 1248 +- deal.II/source/fe/mapping_q1.cc | 850 +- deal.II/source/fe/mapping_q1_eulerian.cc | 80 +- deal.II/source/fe/mapping_q_eulerian.cc | 138 +- deal.II/source/grid/grid_generator.cc | 2430 +- deal.II/source/grid/grid_in.cc | 1540 +- deal.II/source/grid/grid_out.cc | 1549 +- deal.II/source/grid/grid_refinement.cc | 294 +- deal.II/source/grid/grid_reordering.cc | 1031 +- deal.II/source/grid/grid_tools.cc | 1802 +- deal.II/source/grid/intergrid_map.cc | 105 +- deal.II/source/grid/persistent_tria.cc | 56 +- deal.II/source/grid/tria.cc | 19414 ++++++++-------- deal.II/source/grid/tria_accessor.cc | 1623 +- deal.II/source/grid/tria_boundary.cc | 363 +- deal.II/source/grid/tria_boundary_lib.cc | 601 +- deal.II/source/grid/tria_levels.cc | 64 +- deal.II/source/grid/tria_objects.cc | 178 +- deal.II/source/hp/dof_handler.cc | 4688 ++-- deal.II/source/hp/dof_objects.cc | 2 +- deal.II/source/hp/fe_collection.cc | 54 +- deal.II/source/hp/fe_values.cc | 284 +- deal.II/source/hp/mapping_collection.cc | 34 +- deal.II/source/lac/block_matrix_array.cc | 144 +- deal.II/source/lac/block_sparsity_pattern.cc | 318 +- deal.II/source/lac/chunk_sparsity_pattern.cc | 212 +- .../lac/compressed_set_sparsity_pattern.cc | 70 +- .../lac/compressed_simple_sparsity_pattern.cc | 253 +- .../source/lac/compressed_sparsity_pattern.cc | 260 +- deal.II/source/lac/constraint_matrix.cc | 1708 +- deal.II/source/lac/lapack_full_matrix.cc | 582 +- deal.II/source/lac/matrix_lib.cc | 92 +- deal.II/source/lac/matrix_out.cc | 8 +- .../source/lac/petsc_block_sparse_matrix.cc | 12 +- deal.II/source/lac/petsc_matrix_base.cc | 112 +- deal.II/source/lac/petsc_matrix_free.cc | 60 +- .../lac/petsc_parallel_block_sparse_matrix.cc | 12 +- .../lac/petsc_parallel_sparse_matrix.cc | 274 +- deal.II/source/lac/petsc_parallel_vector.cc | 86 +- deal.II/source/lac/petsc_precondition.cc | 146 +- deal.II/source/lac/petsc_solver.cc | 794 +- deal.II/source/lac/petsc_sparse_matrix.cc | 122 +- deal.II/source/lac/petsc_vector.cc | 30 +- deal.II/source/lac/petsc_vector_base.cc | 298 +- deal.II/source/lac/slepc_solver.cc | 111 +- .../lac/slepc_spectral_transformation.cc | 23 +- deal.II/source/lac/solver_control.cc | 106 +- deal.II/source/lac/sparse_decomposition.cc | 16 +- deal.II/source/lac/sparse_direct.cc | 1326 +- deal.II/source/lac/sparsity_pattern.cc | 688 +- deal.II/source/lac/sparsity_tools.cc | 348 +- deal.II/source/lac/timestep_control.cc | 24 +- deal.II/source/lac/tridiagonal_matrix.cc | 63 +- .../lac/trilinos_block_sparse_matrix.cc | 64 +- deal.II/source/lac/trilinos_block_vector.cc | 20 +- deal.II/source/lac/trilinos_precondition.cc | 254 +- deal.II/source/lac/trilinos_solver.cc | 328 +- deal.II/source/lac/trilinos_sparse_matrix.cc | 814 +- .../source/lac/trilinos_sparsity_pattern.cc | 194 +- deal.II/source/lac/trilinos_vector.cc | 132 +- deal.II/source/lac/trilinos_vector_base.cc | 166 +- deal.II/source/lac/vector_memory.cc | 40 +- deal.II/source/multigrid/mg_dof_accessor.cc | 248 +- deal.II/source/multigrid/mg_dof_handler.cc | 1592 +- deal.II/source/multigrid/mg_tools.cc | 2990 +-- deal.II/source/multigrid/mg_transfer_block.cc | 374 +- .../source/multigrid/mg_transfer_component.cc | 412 +- .../source/multigrid/mg_transfer_prebuilt.cc | 700 +- deal.II/source/multigrid/multigrid.cc | 130 +- deal.II/source/numerics/data_out.cc | 834 +- deal.II/source/numerics/data_out_faces.cc | 266 +- deal.II/source/numerics/data_out_rotation.cc | 493 +- deal.II/source/numerics/data_out_stack.cc | 258 +- deal.II/source/numerics/data_postprocessor.cc | 26 +- .../numerics/derivative_approximation.cc | 551 +- deal.II/source/numerics/error_estimator.cc | 1277 +- deal.II/source/numerics/histogram.cc | 227 +- deal.II/source/numerics/matrix_tools.cc | 1760 +- deal.II/source/numerics/mesh_worker.cc | 28 +- .../source/numerics/point_value_history.cc | 838 +- deal.II/source/numerics/solution_transfer.cc | 445 +- deal.II/source/numerics/time_dependent.cc | 823 +- 542 files changed, 249698 insertions(+), 248882 deletions(-) diff --git a/deal.II/examples/doxygen/block_matrix_array.cc b/deal.II/examples/doxygen/block_matrix_array.cc index f24b85e8bb..624264773a 100644 --- a/deal.II/examples/doxygen/block_matrix_array.cc +++ b/deal.II/examples/doxygen/block_matrix_array.cc @@ -28,30 +28,30 @@ using namespace dealii; double Adata[] = { - 4., .5, .1, 0., - .5, 4., .5, .1, - .1, .5, 4., .5, - 0., .1, .5, 4. + 4., .5, .1, 0., + .5, 4., .5, .1, + .1, .5, 4., .5, + 0., .1, .5, 4. }; double B1data[] = { - .5, .1, - .4, .2, - .3, .3, - .2, .4 + .5, .1, + .4, .2, + .3, .3, + .2, .4 }; double B2data[] = { - .3, 0., -.3, 0., - -.3, 0., .3, 0. + .3, 0., -.3, 0., + -.3, 0., .3, 0. }; double Cdata[] = { - 8., 1., - 1., 8. + 8., 1., + 1., 8. }; int main () @@ -85,7 +85,7 @@ int main () BlockVector result(block_sizes); BlockVector x(block_sizes); BlockVector y(block_sizes); - for (unsigned int i=0;i - precondition(2, simple_mem); + precondition(2, simple_mem); precondition.enter(Ainv,0,0,.5); precondition.enter(Cinv,1,1); diff --git a/deal.II/examples/doxygen/compressed_block_sparsity_pattern.cc b/deal.II/examples/doxygen/compressed_block_sparsity_pattern.cc index f8c3aa0e7d..5d242e9bf9 100644 --- a/deal.II/examples/doxygen/compressed_block_sparsity_pattern.cc +++ b/deal.II/examples/doxygen/compressed_block_sparsity_pattern.cc @@ -38,22 +38,22 @@ int main() FE_Q<2> fe1(1); FE_Q<2> fe2(2); FESystem<2> fe(fe1, 2, fe2, 1); - + DoFHandler<2> dof(tr); dof.distribute_dofs(fe); DoFRenumbering::Cuthill_McKee(dof); DoFRenumbering::component_wise(dof); - + ConstraintMatrix constraints; DoFTools::make_hanging_node_constraints(dof, constraints); constraints.close(); - + std::vector dofs_per_block(fe.n_blocks()); DoFTools::count_dofs_per_block(dof, dofs_per_block); - + BlockCompressedSparsityPattern c_sparsity(fe.n_blocks(), fe.n_blocks()); - for (unsigned int i=0;i > mem; - + ProductMatrix > AB(A,B,mem); Vector u(2); diff --git a/deal.II/examples/doxygen/theta_timestepping.cc b/deal.II/examples/doxygen/theta_timestepping.cc index bf7525dc28..dcc6310e70 100644 --- a/deal.II/examples/doxygen/theta_timestepping.cc +++ b/deal.II/examples/doxygen/theta_timestepping.cc @@ -29,32 +29,32 @@ using namespace Algorithms; class Explicit : public Operator > { - public: - Explicit(const FullMatrix& matrix); - void operator() (NamedData*>& out, - const NamedData*>& in); - - void initialize_timestep_data(const TimestepData&); - private: - const TimestepData* timestep_data; - SmartPointer, Explicit> matrix; - FullMatrix m; +public: + Explicit(const FullMatrix &matrix); + void operator() (NamedData*> &out, + const NamedData*> &in); + + void initialize_timestep_data(const TimestepData &); +private: + const TimestepData *timestep_data; + SmartPointer, Explicit> matrix; + FullMatrix m; }; class Implicit : public Operator > { - public: - Implicit(const FullMatrix& matrix); - void operator() (NamedData*>& out, - const NamedData*>& in); - - void initialize_timestep_data(const TimestepData&); - private: - const TimestepData* timestep_data; - SmartPointer, Implicit> matrix; - FullMatrix m; +public: + Implicit(const FullMatrix &matrix); + void operator() (NamedData*> &out, + const NamedData*> &in); + + void initialize_timestep_data(const TimestepData &); +private: + const TimestepData *timestep_data; + SmartPointer, Implicit> matrix; + FullMatrix m; }; // End of declarations @@ -81,7 +81,7 @@ int main() value(0) = 1.; NamedData*> indata; NamedData*> outdata; - Vector* p = &value; + Vector *p = &value; outdata.add(p, "value"); solver.notify(Events::initial); @@ -89,28 +89,28 @@ int main() } -Explicit::Explicit(const FullMatrix& M) - : - matrix(&M) +Explicit::Explicit(const FullMatrix &M) + : + matrix(&M) { m.reinit(M.m(), M.n()); } void -Explicit::initialize_timestep_data(const TimestepData& t) +Explicit::initialize_timestep_data(const TimestepData &t) { timestep_data = &t; } void -Explicit::operator() (NamedData*>& out, const NamedData*>& in) +Explicit::operator() (NamedData*> &out, const NamedData*> &in) { if (this->notifications.test(Events::initial) || this->notifications.test(Events::new_timestep_size)) { m.equ(-timestep_data->step, *matrix); - for (unsigned int i=0;inotifications.clear(); @@ -119,28 +119,28 @@ Explicit::operator() (NamedData*>& out, const NamedData& M) - : - matrix(&M) +Implicit::Implicit(const FullMatrix &M) + : + matrix(&M) { m.reinit(M.m(), M.n()); } void -Implicit::initialize_timestep_data(const TimestepData& t) +Implicit::initialize_timestep_data(const TimestepData &t) { timestep_data = &t; } void -Implicit::operator() (NamedData*>& out, const NamedData*>& in) +Implicit::operator() (NamedData*> &out, const NamedData*> &in) { if (this->notifications.test(Events::initial) || this->notifications.test(Events::new_timestep_size)) { m.equ(timestep_data->step, *matrix); - for (unsigned int i=0;i - // We need the following two includes - // for loops over cells and/or faces: +// We need the following two includes +// for loops over cells and/or faces: #include #include - // Here are some functions to - // generate standard grids: +// Here are some functions to +// generate standard grids: #include - // We would like to use boundaries - // which are not straight lines, so - // we import some classes which - // predefine some boundary - // descriptions: +// We would like to use boundaries +// which are not straight lines, so +// we import some classes which +// predefine some boundary +// descriptions: #include - // Output of grids in various - // graphics formats: +// Output of grids in various +// graphics formats: #include - // This is needed for C++ output: +// This is needed for C++ output: #include - // And this for the declarations of the - // `sqrt' and `fabs' functions: +// And this for the declarations of the +// `sqrt' and `fabs' functions: #include - // The final step in importing - // deal.II is this: All deal.II - // functions and classes are in a - // namespace dealii, to - // make sure they don't clash with - // symbols from other libraries you - // may want to use in conjunction - // with deal.II. One could use these - // functions and classes by prefixing - // every use of these names by - // dealii::, but that - // would quickly become cumbersome - // and annoying. Rather, we simply - // import the entire deal.II - // namespace for general use: +// The final step in importing +// deal.II is this: All deal.II +// functions and classes are in a +// namespace dealii, to +// make sure they don't clash with +// symbols from other libraries you +// may want to use in conjunction +// with deal.II. One could use these +// functions and classes by prefixing +// every use of these names by +// dealii::, but that +// would quickly become cumbersome +// and annoying. Rather, we simply +// import the entire deal.II +// namespace for general use: using namespace dealii; - // @sect3{Creating the first mesh} +// @sect3{Creating the first mesh} - // In the following, first function, we - // simply use the unit square as - // domain and produce a globally - // refined grid from it. +// In the following, first function, we +// simply use the unit square as +// domain and produce a globally +// refined grid from it. void first_grid () { - // The first thing to do is to - // define an object for a - // triangulation of a - // two-dimensional domain: + // The first thing to do is to + // define an object for a + // triangulation of a + // two-dimensional domain: Triangulation<2> triangulation; - // Here and in many following - // cases, the string "<2>" after a - // class name indicates that this - // is an object that shall work in - // two space dimensions. Likewise, - // there are versions of the - // triangulation class that are - // working in one ("<1>") and three - // ("<3>") space dimensions. The - // way this works is through some - // template magic that we will - // investigate in some more detail - // in later example programs; - // there, we will also see how to - // write programs in an essentially - // dimension independent way. + // Here and in many following + // cases, the string "<2>" after a + // class name indicates that this + // is an object that shall work in + // two space dimensions. Likewise, + // there are versions of the + // triangulation class that are + // working in one ("<1>") and three + // ("<3>") space dimensions. The + // way this works is through some + // template magic that we will + // investigate in some more detail + // in later example programs; + // there, we will also see how to + // write programs in an essentially + // dimension independent way. - // Next, we want to fill the - // triangulation with a single cell - // for a square domain. The - // triangulation is the refined - // four times, to yield 4^4=256 - // cells in total: + // Next, we want to fill the + // triangulation with a single cell + // for a square domain. The + // triangulation is the refined + // four times, to yield 4^4=256 + // cells in total: GridGenerator::hyper_cube (triangulation); triangulation.refine_global (4); - // Now we want to write a graphical - // representation of the mesh to an - // output file. The GridOut - // class of deal.II can do that in - // a number of different output - // formats; here, we choose - // encapsulated postscript (eps) - // format: + // Now we want to write a graphical + // representation of the mesh to an + // output file. The GridOut + // class of deal.II can do that in + // a number of different output + // formats; here, we choose + // encapsulated postscript (eps) + // format: std::ofstream out ("grid-1.eps"); GridOut grid_out; grid_out.write_eps (triangulation, out); @@ -108,165 +108,165 @@ void first_grid () - // @sect3{Creating the second mesh} +// @sect3{Creating the second mesh} - // The grid in the following, second - // function is slightly more - // complicated in that we use a ring - // domain and refine the result once - // globally. +// The grid in the following, second +// function is slightly more +// complicated in that we use a ring +// domain and refine the result once +// globally. void second_grid () { - // We start again by defining an - // object for a triangulation of a - // two-dimensional domain: + // We start again by defining an + // object for a triangulation of a + // two-dimensional domain: Triangulation<2> triangulation; - // We then fill it with a ring - // domain. The center of the ring - // shall be the point (1,0), and - // inner and outer radius shall be - // 0.5 and 1. The number of - // circumferential cells could be - // adjusted automatically by this - // function, but we choose to set - // it explicitely to 10 as the last - // argument: + // We then fill it with a ring + // domain. The center of the ring + // shall be the point (1,0), and + // inner and outer radius shall be + // 0.5 and 1. The number of + // circumferential cells could be + // adjusted automatically by this + // function, but we choose to set + // it explicitely to 10 as the last + // argument: const Point<2> center (1,0); const double inner_radius = 0.5, outer_radius = 1.0; GridGenerator::hyper_shell (triangulation, center, inner_radius, outer_radius, 10); - // By default, the triangulation - // assumes that all boundaries are - // straight and given by the cells - // of the coarse grid (which we - // just created). It uses this - // information when cells at the - // boundary are refined and new - // points need to be introduced on - // the boundary; if the boundary is - // assumed to be straight, then new - // points will simply be in the - // middle of the surrounding ones. - // - // Here, however, we would like to - // have a curved - // boundary. Fortunately, some good - // soul implemented an object which - // describes the boundary of a ring - // domain; it only needs the center - // of the ring and automatically - // figures out the inner and outer - // radius when needed. Note that we - // associate this boundary object - // with that part of the boundary - // that has the "boundary - // indicator" zero. By default (at - // least in 2d and 3d, the 1d case - // is slightly different), all - // boundary parts have this number, - // but you can change this number - // for some parts of the - // boundary. In that case, the - // curved boundary thus associated - // with number zero will not apply - // on those parts with a non-zero - // boundary indicator, but other - // boundary description objects can - // be associated with those - // non-zero indicators. If no - // boundary description is - // associated with a particular - // boundary indicator, a straight - // boundary is implied. + // By default, the triangulation + // assumes that all boundaries are + // straight and given by the cells + // of the coarse grid (which we + // just created). It uses this + // information when cells at the + // boundary are refined and new + // points need to be introduced on + // the boundary; if the boundary is + // assumed to be straight, then new + // points will simply be in the + // middle of the surrounding ones. + // + // Here, however, we would like to + // have a curved + // boundary. Fortunately, some good + // soul implemented an object which + // describes the boundary of a ring + // domain; it only needs the center + // of the ring and automatically + // figures out the inner and outer + // radius when needed. Note that we + // associate this boundary object + // with that part of the boundary + // that has the "boundary + // indicator" zero. By default (at + // least in 2d and 3d, the 1d case + // is slightly different), all + // boundary parts have this number, + // but you can change this number + // for some parts of the + // boundary. In that case, the + // curved boundary thus associated + // with number zero will not apply + // on those parts with a non-zero + // boundary indicator, but other + // boundary description objects can + // be associated with those + // non-zero indicators. If no + // boundary description is + // associated with a particular + // boundary indicator, a straight + // boundary is implied. const HyperShellBoundary<2> boundary_description(center); triangulation.set_boundary (0, boundary_description); - // In order to demonstrate how to - // write a loop over all cells, we - // will refine the grid in five - // steps towards the inner circle - // of the domain: + // In order to demonstrate how to + // write a loop over all cells, we + // will refine the grid in five + // steps towards the inner circle + // of the domain: for (unsigned int step=0; step<5; ++step) { - // Next, we need an iterator - // which points to a cell and - // which we will move over all - // active cells one by one - // (active cells are those that - // are not further refined, and - // the only ones that can be - // marked for further - // refinement, obviously). By - // convention, we almost always - // use the names cell and - // endc for the iterator - // pointing to the present cell - // and to the - // one-past-the-end - // iterator: + // Next, we need an iterator + // which points to a cell and + // which we will move over all + // active cells one by one + // (active cells are those that + // are not further refined, and + // the only ones that can be + // marked for further + // refinement, obviously). By + // convention, we almost always + // use the names cell and + // endc for the iterator + // pointing to the present cell + // and to the + // one-past-the-end + // iterator: Triangulation<2>::active_cell_iterator - cell = triangulation.begin_active(), - endc = triangulation.end(); + cell = triangulation.begin_active(), + endc = triangulation.end(); - // The loop over all cells is - // then rather trivial, and - // looks like any loop - // involving pointers instead - // of iterators: + // The loop over all cells is + // then rather trivial, and + // looks like any loop + // involving pointers instead + // of iterators: for (; cell!=endc; ++cell) - // Next, we want to loop over - // all vertices of the - // cells. Since we are in 2d, - // we know that each cell has - // exactly four - // vertices. However, instead - // of penning down a 4 in the - // loop bound, we make a - // first attempt at writing - // it in a - // dimension-independent way - // by which we find out about - // the number of vertices of - // a cell. Using the - // GeometryInfo class, we - // will later have an easier - // time getting the program - // to also run in 3d: we only - // have to change all - // occurrences of <2> to - // <3>, and do not have - // to audit our code for the - // hidden appearance of magic - // numbers like a 4 that - // needs to be replaced by an - // 8: + // Next, we want to loop over + // all vertices of the + // cells. Since we are in 2d, + // we know that each cell has + // exactly four + // vertices. However, instead + // of penning down a 4 in the + // loop bound, we make a + // first attempt at writing + // it in a + // dimension-independent way + // by which we find out about + // the number of vertices of + // a cell. Using the + // GeometryInfo class, we + // will later have an easier + // time getting the program + // to also run in 3d: we only + // have to change all + // occurrences of <2> to + // <3>, and do not have + // to audit our code for the + // hidden appearance of magic + // numbers like a 4 that + // needs to be replaced by an + // 8: for (unsigned int v=0; v < GeometryInfo<2>::vertices_per_cell; ++v) { - // If this cell is at the - // inner boundary, then - // at least one of its - // vertices must sit on - // the inner ring and - // therefore have a - // radial distance from - // the center of exactly - // 0.5, up to floating - // point - // accuracy. Compute this - // distance, and if we - // have found a vertex - // with this property - // flag this cell for - // later refinement. We - // can then also break - // the loop over all - // vertices and move on - // to the next cell. + // If this cell is at the + // inner boundary, then + // at least one of its + // vertices must sit on + // the inner ring and + // therefore have a + // radial distance from + // the center of exactly + // 0.5, up to floating + // point + // accuracy. Compute this + // distance, and if we + // have found a vertex + // with this property + // flag this cell for + // later refinement. We + // can then also break + // the loop over all + // vertices and move on + // to the next cell. const double distance_from_center = center.distance (cell->vertex(v)); @@ -277,70 +277,70 @@ void second_grid () } } - // Now that we have marked all - // the cells that we want - // refined, we let the - // triangulation actually do - // this refinement. The - // function that does so owes - // its long name to the fact - // that one can also mark cells - // for coarsening, and the - // function does coarsening and - // refinement all at once: + // Now that we have marked all + // the cells that we want + // refined, we let the + // triangulation actually do + // this refinement. The + // function that does so owes + // its long name to the fact + // that one can also mark cells + // for coarsening, and the + // function does coarsening and + // refinement all at once: triangulation.execute_coarsening_and_refinement (); } - // Finally, after these five - // iterations of refinement, we - // want to again write the - // resulting mesh to a file, again - // in eps format. This works just - // as above: + // Finally, after these five + // iterations of refinement, we + // want to again write the + // resulting mesh to a file, again + // in eps format. This works just + // as above: std::ofstream out ("grid-2.eps"); GridOut grid_out; grid_out.write_eps (triangulation, out); - // At this point, all objects - // created in this function will be - // destroyed in reverse - // order. Unfortunately, we defined - // the boundary object after the - // triangulation, which still has a - // pointer to it and the library - // will produce an error if the - // boundary object is destroyed - // before the triangulation. We - // therefore have to release it, - // which can be done as - // follows. Note that this sets the - // boundary object used for part - // "0" of the boundary back to a - // default object, over which the - // triangulation has full control. + // At this point, all objects + // created in this function will be + // destroyed in reverse + // order. Unfortunately, we defined + // the boundary object after the + // triangulation, which still has a + // pointer to it and the library + // will produce an error if the + // boundary object is destroyed + // before the triangulation. We + // therefore have to release it, + // which can be done as + // follows. Note that this sets the + // boundary object used for part + // "0" of the boundary back to a + // default object, over which the + // triangulation has full control. triangulation.set_boundary (0); - // An alternative to doing so, and - // one that is frequently more - // convenient, would have been to - // declare the boundary object - // before the triangulation - // object. In that case, the - // triangulation would have let - // lose of the boundary object upon - // its destruction, and everything - // would have been fine. + // An alternative to doing so, and + // one that is frequently more + // convenient, would have been to + // declare the boundary object + // before the triangulation + // object. In that case, the + // triangulation would have let + // lose of the boundary object upon + // its destruction, and everything + // would have been fine. } - // @sect3{The main function} +// @sect3{The main function} - // Finally, the main function. There - // isn't much to do here, only to - // call the two subfunctions, which - // produce the two grids. +// Finally, the main function. There +// isn't much to do here, only to +// call the two subfunctions, which +// produce the two grids. int main () { first_grid (); diff --git a/deal.II/examples/step-10/step-10.cc b/deal.II/examples/step-10/step-10.cc index 5548d521a8..dc39cdca28 100644 --- a/deal.II/examples/step-10/step-10.cc +++ b/deal.II/examples/step-10/step-10.cc @@ -9,10 +9,10 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // The first of the following include - // files are probably well-known by - // now and need no further - // explanation. +// The first of the following include +// files are probably well-known by +// now and need no further +// explanation. #include #include #include @@ -26,326 +26,326 @@ #include #include - // This is the only new one: in it, - // we declare the MappingQ class - // which we will use for polynomial - // mappings of arbitrary order: +// This is the only new one: in it, +// we declare the MappingQ class +// which we will use for polynomial +// mappings of arbitrary order: #include - // And this again is C++: +// And this again is C++: #include #include #include - // The last step is as in previous - // programs: +// The last step is as in previous +// programs: namespace Step10 { using namespace dealii; - // Now, as we want to compute the - // value of $\pi$, we have to compare to - // somewhat. These are the first few - // digits of $\pi$, which we define - // beforehand for later use. Since we - // would like to compute the - // difference between two numbers - // which are quite accurate, with the - // accuracy of the computed - // approximation to $\pi$ being in the - // range of the number of digits - // which a double variable can hold, - // we rather declare the reference - // value as a long double and - // give it a number of extra digits: + // Now, as we want to compute the + // value of $\pi$, we have to compare to + // somewhat. These are the first few + // digits of $\pi$, which we define + // beforehand for later use. Since we + // would like to compute the + // difference between two numbers + // which are quite accurate, with the + // accuracy of the computed + // approximation to $\pi$ being in the + // range of the number of digits + // which a double variable can hold, + // we rather declare the reference + // value as a long double and + // give it a number of extra digits: const long double pi = 3.141592653589793238462643; - // Then, the first task will be to - // generate some output. Since this - // program is so small, we do not - // employ object oriented techniques - // in it and do not declare classes - // (although, of course, we use the - // object oriented features of the - // library). Rather, we just pack the - // functionality into separate - // functions. We make these functions - // templates on the number of space - // dimensions to conform to usual - // practice when using deal.II, - // although we will only use them for - // two space dimensions. - // - // The first of these functions just - // generates a triangulation of a - // circle (hyperball) and outputs the - // Qp mapping of its cells for - // different values of p. Then, - // we refine the grid once and do so - // again. + // Then, the first task will be to + // generate some output. Since this + // program is so small, we do not + // employ object oriented techniques + // in it and do not declare classes + // (although, of course, we use the + // object oriented features of the + // library). Rather, we just pack the + // functionality into separate + // functions. We make these functions + // templates on the number of space + // dimensions to conform to usual + // practice when using deal.II, + // although we will only use them for + // two space dimensions. + // + // The first of these functions just + // generates a triangulation of a + // circle (hyperball) and outputs the + // Qp mapping of its cells for + // different values of p. Then, + // we refine the grid once and do so + // again. template void gnuplot_output() { std::cout << "Output of grids into gnuplot files:" << std::endl << "===================================" << std::endl; - // So first generate a coarse - // triangulation of the circle and - // associate a suitable boundary - // description to it. Note that the - // default values of the - // HyperBallBoundary constructor - // are a center at the origin and a - // radius equals one. + // So first generate a coarse + // triangulation of the circle and + // associate a suitable boundary + // description to it. Note that the + // default values of the + // HyperBallBoundary constructor + // are a center at the origin and a + // radius equals one. Triangulation triangulation; GridGenerator::hyper_ball (triangulation); static const HyperBallBoundary boundary; triangulation.set_boundary (0, boundary); - // Next generate output for this - // grid and for a once refined - // grid. Note that we have hidden - // the mesh refinement in the loop - // header, which might be uncommon - // but nevertheless works. Also it - // is strangely consistent with - // incrementing the loop index - // denoting the refinement level. + // Next generate output for this + // grid and for a once refined + // grid. Note that we have hidden + // the mesh refinement in the loop + // header, which might be uncommon + // but nevertheless works. Also it + // is strangely consistent with + // incrementing the loop index + // denoting the refinement level. for (unsigned int refinement=0; refinement<2; ++refinement, triangulation.refine_global(1)) { std::cout << "Refinement level: " << refinement << std::endl; - // Then have a string which - // denotes the base part of the - // names of the files into - // which we write the - // output. Note that in the - // parentheses in the - // initializer we do arithmetic - // on characters, which assumes - // that first the characters - // denoting numbers are placed - // consecutively (which is - // probably true for all - // reasonable character sets - // nowadays), but also assumes - // that the increment - // refinement is less than - // ten. This is therefore more - // a quick hack if we know - // exactly the values which the - // increment can assume. A - // better implementation would - // use the - // std::istringstream - // class to generate a name. + // Then have a string which + // denotes the base part of the + // names of the files into + // which we write the + // output. Note that in the + // parentheses in the + // initializer we do arithmetic + // on characters, which assumes + // that first the characters + // denoting numbers are placed + // consecutively (which is + // probably true for all + // reasonable character sets + // nowadays), but also assumes + // that the increment + // refinement is less than + // ten. This is therefore more + // a quick hack if we know + // exactly the values which the + // increment can assume. A + // better implementation would + // use the + // std::istringstream + // class to generate a name. std::string filename_base = "ball"; filename_base += '0'+refinement; - // Then output the present grid - // for Q1, Q2, and Q3 mappings: + // Then output the present grid + // for Q1, Q2, and Q3 mappings: for (unsigned int degree=1; degree<4; ++degree) { std::cout << "Degree = " << degree << std::endl; - // For this, first set up - // an object describing the - // mapping. This is done - // using the MappingQ - // class, which takes as - // argument to the - // constructor the - // polynomial degree which - // it shall use. + // For this, first set up + // an object describing the + // mapping. This is done + // using the MappingQ + // class, which takes as + // argument to the + // constructor the + // polynomial degree which + // it shall use. const MappingQ mapping (degree); - // We note one interesting - // fact: if you want a - // piecewise linear - // mapping, then you could - // give a value of 1 to - // the - // constructor. However, - // for linear mappings, so - // many things can be - // generated simpler that - // there is another class, - // called MappingQ1 - // which does exactly the - // same is if you gave an - // degree of 1 to the - // MappingQ class, but - // does so significantly - // faster. MappingQ1 is - // also the class that is - // implicitly used - // throughout the library - // in many functions and - // classes if you do not - // specify another mapping - // explicitly. - - - // In degree to actually - // write out the present - // grid with this mapping, - // we set up an object - // which we will use for - // output. We will generate - // Gnuplot output, which - // consists of a set of - // lines describing the - // mapped triangulation. By - // default, only one line - // is drawn for each face - // of the triangulation, - // but since we want to - // explicitely see the - // effect of the mapping, - // we want to have the - // faces in more - // detail. This can be done - // by passing the output - // object a structure which - // contains some flags. In - // the present case, since - // Gnuplot can only draw - // straight lines, we - // output a number of - // additional points on the - // faces so that each face - // is drawn by 30 small - // lines instead of only - // one. This is sufficient - // to give us the - // impression of seeing a - // curved line, rather than - // a set of straight lines. + // We note one interesting + // fact: if you want a + // piecewise linear + // mapping, then you could + // give a value of 1 to + // the + // constructor. However, + // for linear mappings, so + // many things can be + // generated simpler that + // there is another class, + // called MappingQ1 + // which does exactly the + // same is if you gave an + // degree of 1 to the + // MappingQ class, but + // does so significantly + // faster. MappingQ1 is + // also the class that is + // implicitly used + // throughout the library + // in many functions and + // classes if you do not + // specify another mapping + // explicitly. + + + // In degree to actually + // write out the present + // grid with this mapping, + // we set up an object + // which we will use for + // output. We will generate + // Gnuplot output, which + // consists of a set of + // lines describing the + // mapped triangulation. By + // default, only one line + // is drawn for each face + // of the triangulation, + // but since we want to + // explicitely see the + // effect of the mapping, + // we want to have the + // faces in more + // detail. This can be done + // by passing the output + // object a structure which + // contains some flags. In + // the present case, since + // Gnuplot can only draw + // straight lines, we + // output a number of + // additional points on the + // faces so that each face + // is drawn by 30 small + // lines instead of only + // one. This is sufficient + // to give us the + // impression of seeing a + // curved line, rather than + // a set of straight lines. GridOut grid_out; GridOutFlags::Gnuplot gnuplot_flags(false, 30); grid_out.set_flags(gnuplot_flags); - // Finally, generate a - // filename and a file for - // output using the same - // evil hack as above: + // Finally, generate a + // filename and a file for + // output using the same + // evil hack as above: std::string filename = filename_base+"_mapping_q"; filename += ('0'+degree); filename += ".dat"; std::ofstream gnuplot_file (filename.c_str()); - // Then write out the - // triangulation to this - // file. The last argument - // of the function is a - // pointer to a mapping - // object. This argument - // has a default value, and - // if no value is given a - // simple MappingQ1 - // object is taken, which - // we briefly described - // above. This would then - // result in a piecewise - // linear approximation of - // the true boundary in the - // output. + // Then write out the + // triangulation to this + // file. The last argument + // of the function is a + // pointer to a mapping + // object. This argument + // has a default value, and + // if no value is given a + // simple MappingQ1 + // object is taken, which + // we briefly described + // above. This would then + // result in a piecewise + // linear approximation of + // the true boundary in the + // output. grid_out.write_gnuplot (triangulation, gnuplot_file, &mapping); } std::cout << std::endl; } } - // Now we proceed with the main part - // of the code, the approximation of - // $\pi$. The area of a circle is of - // course given by $\pi r^2$, so - // having a circle of radius 1, the - // area represents just the number - // that is searched for. The - // numerical computation of the area - // is performed by integrating the - // constant function of value 1 over - // the whole computational domain, - // i.e. by computing the areas - // $\int_K 1 dx=\int_{\hat K} 1 - // \ \textrm{det}\ J(\hat x) d\hat x - // \approx \sum_i \textrm{det} - // \ J(\hat x_i)w(\hat x_i)$, where the - // sum extends over all quadrature - // points on all active cells in the - // triangulation, with $w(x_i)$ being - // the weight of quadrature point - // $x_i$. The integrals on each cell - // are approximated by numerical - // quadrature, hence the only - // additional ingredient we need is - // to set up a FEValues object that - // provides the corresponding `JxW' - // values of each cell. (Note that - // `JxW' is meant to abbreviate - // Jacobian determinant times - // weight; since in numerical - // quadrature the two factors always - // occur at the same places, we only - // offer the combined quantity, - // rather than two separate ones.) We - // note that here we won't use the - // FEValues object in its original - // purpose, i.e. for the computation - // of values of basis functions of a - // specific finite element at certain - // quadrature points. Rather, we use - // it only to gain the `JxW' at the - // quadrature points, irrespective of - // the (dummy) finite element we will - // give to the constructor of the - // FEValues object. The actual finite - // element given to the FEValues - // object is not used at all, so we - // could give any. + // Now we proceed with the main part + // of the code, the approximation of + // $\pi$. The area of a circle is of + // course given by $\pi r^2$, so + // having a circle of radius 1, the + // area represents just the number + // that is searched for. The + // numerical computation of the area + // is performed by integrating the + // constant function of value 1 over + // the whole computational domain, + // i.e. by computing the areas + // $\int_K 1 dx=\int_{\hat K} 1 + // \ \textrm{det}\ J(\hat x) d\hat x + // \approx \sum_i \textrm{det} + // \ J(\hat x_i)w(\hat x_i)$, where the + // sum extends over all quadrature + // points on all active cells in the + // triangulation, with $w(x_i)$ being + // the weight of quadrature point + // $x_i$. The integrals on each cell + // are approximated by numerical + // quadrature, hence the only + // additional ingredient we need is + // to set up a FEValues object that + // provides the corresponding `JxW' + // values of each cell. (Note that + // `JxW' is meant to abbreviate + // Jacobian determinant times + // weight; since in numerical + // quadrature the two factors always + // occur at the same places, we only + // offer the combined quantity, + // rather than two separate ones.) We + // note that here we won't use the + // FEValues object in its original + // purpose, i.e. for the computation + // of values of basis functions of a + // specific finite element at certain + // quadrature points. Rather, we use + // it only to gain the `JxW' at the + // quadrature points, irrespective of + // the (dummy) finite element we will + // give to the constructor of the + // FEValues object. The actual finite + // element given to the FEValues + // object is not used at all, so we + // could give any. template void compute_pi_by_area () { std::cout << "Computation of Pi by the area:" << std::endl << "==============================" << std::endl; - // For the numerical quadrature on - // all cells we employ a quadrature - // rule of sufficiently high - // degree. We choose QGauss that - // is of order 8 (4 points), to be sure that - // the errors due to numerical - // quadrature are of higher order - // than the order (maximal 6) that - // will occur due to the order of - // the approximation of the - // boundary, i.e. the order of the - // mappings employed. Note that the - // integrand, the Jacobian - // determinant, is not a polynomial - // function (rather, it is a - // rational one), so we do not use - // Gauss quadrature in order to get - // the exact value of the integral - // as done often in finite element - // computations, but could as well - // have used any quadrature formula - // of like order instead. + // For the numerical quadrature on + // all cells we employ a quadrature + // rule of sufficiently high + // degree. We choose QGauss that + // is of order 8 (4 points), to be sure that + // the errors due to numerical + // quadrature are of higher order + // than the order (maximal 6) that + // will occur due to the order of + // the approximation of the + // boundary, i.e. the order of the + // mappings employed. Note that the + // integrand, the Jacobian + // determinant, is not a polynomial + // function (rather, it is a + // rational one), so we do not use + // Gauss quadrature in order to get + // the exact value of the integral + // as done often in finite element + // computations, but could as well + // have used any quadrature formula + // of like order instead. const QGauss quadrature(4); - // Now start by looping over - // polynomial mapping degrees=1..4: + // Now start by looping over + // polynomial mapping degrees=1..4: for (unsigned int degree=1; degree<5; ++degree) { std::cout << "Degree = " << degree << std::endl; - // First generate the - // triangulation, the boundary - // and the mapping object as - // already seen. + // First generate the + // triangulation, the boundary + // and the mapping object as + // already seen. Triangulation triangulation; GridGenerator::hyper_ball (triangulation); @@ -354,130 +354,130 @@ namespace Step10 const MappingQ mapping (degree); - // We now create a dummy finite - // element. Here we could - // choose any finite element, - // as we are only interested in - // the `JxW' values provided by - // the FEValues object - // below. Nevertheless, we have - // to provide a finite element - // since in this example we - // abuse the FEValues class a - // little in that we only ask - // it to provide us with the - // weights of certain - // quadrature points, in - // contrast to the usual - // purpose (and name) of the - // FEValues class which is to - // provide the values of finite - // elements at these points. + // We now create a dummy finite + // element. Here we could + // choose any finite element, + // as we are only interested in + // the `JxW' values provided by + // the FEValues object + // below. Nevertheless, we have + // to provide a finite element + // since in this example we + // abuse the FEValues class a + // little in that we only ask + // it to provide us with the + // weights of certain + // quadrature points, in + // contrast to the usual + // purpose (and name) of the + // FEValues class which is to + // provide the values of finite + // elements at these points. const FE_Q dummy_fe (1); - // Likewise, we need to create - // a DoFHandler object. We do - // not actually use it, but it - // will provide us with - // `active_cell_iterators' that - // are needed to reinitialize - // the FEValues object on each - // cell of the triangulation. + // Likewise, we need to create + // a DoFHandler object. We do + // not actually use it, but it + // will provide us with + // `active_cell_iterators' that + // are needed to reinitialize + // the FEValues object on each + // cell of the triangulation. DoFHandler dof_handler (triangulation); - // Now we set up the FEValues - // object, giving the Mapping, - // the dummy finite element and - // the quadrature object to the - // constructor, together with - // the update flags asking for - // the `JxW' values at the - // quadrature points only. This - // tells the FEValues object - // that it needs not compute - // other quantities upon - // calling the reinit - // function, thus saving - // computation time. - // - // The most important - // difference in the - // construction of the FEValues - // object compared to previous - // example programs is that we - // pass a mapping object as - // first argument, which is to - // be used in the computation - // of the mapping from unit to - // real cell. In previous - // examples, this argument was - // omitted, resulting in the - // implicit use of an object of - // type MappingQ1. + // Now we set up the FEValues + // object, giving the Mapping, + // the dummy finite element and + // the quadrature object to the + // constructor, together with + // the update flags asking for + // the `JxW' values at the + // quadrature points only. This + // tells the FEValues object + // that it needs not compute + // other quantities upon + // calling the reinit + // function, thus saving + // computation time. + // + // The most important + // difference in the + // construction of the FEValues + // object compared to previous + // example programs is that we + // pass a mapping object as + // first argument, which is to + // be used in the computation + // of the mapping from unit to + // real cell. In previous + // examples, this argument was + // omitted, resulting in the + // implicit use of an object of + // type MappingQ1. FEValues fe_values (mapping, dummy_fe, quadrature, update_JxW_values); - // We employ an object of the - // ConvergenceTable class to - // store all important data - // like the approximated values - // for $\pi$ and the error with - // respect to the true value of - // $\pi$. We will also use - // functions provided by the - // ConvergenceTable class to - // compute convergence rates of - // the approximations to $\pi$. + // We employ an object of the + // ConvergenceTable class to + // store all important data + // like the approximated values + // for $\pi$ and the error with + // respect to the true value of + // $\pi$. We will also use + // functions provided by the + // ConvergenceTable class to + // compute convergence rates of + // the approximations to $\pi$. ConvergenceTable table; - // Now we loop over several - // refinement steps of the - // triangulation. + // Now we loop over several + // refinement steps of the + // triangulation. for (unsigned int refinement=0; refinement<6; ++refinement, triangulation.refine_global (1)) { - // In this loop we first - // add the number of active - // cells of the current - // triangulation to the - // table. This function - // automatically creates a - // table column with - // superscription `cells', - // in case this column was - // not created before. + // In this loop we first + // add the number of active + // cells of the current + // triangulation to the + // table. This function + // automatically creates a + // table column with + // superscription `cells', + // in case this column was + // not created before. table.add_value("cells", triangulation.n_active_cells()); - // Then we distribute the - // degrees of freedom for - // the dummy finite - // element. Strictly - // speaking we do not need - // this function call in - // our special case but we - // call it to make the - // DoFHandler happy -- - // otherwise it would throw - // an assertion in the - // FEValues::reinit - // function below. + // Then we distribute the + // degrees of freedom for + // the dummy finite + // element. Strictly + // speaking we do not need + // this function call in + // our special case but we + // call it to make the + // DoFHandler happy -- + // otherwise it would throw + // an assertion in the + // FEValues::reinit + // function below. dof_handler.distribute_dofs (dummy_fe); - // We define the variable - // area as `long double' - // like we did for the pi - // variable before. + // We define the variable + // area as `long double' + // like we did for the pi + // variable before. long double area = 0; - // Now we loop over all - // cells, reinitialize the - // FEValues object for each - // cell, and add up all the - // `JxW' values for this - // cell to `area'... + // Now we loop over all + // cells, reinitialize the + // FEValues object for each + // cell, and add up all the + // `JxW' values for this + // cell to `area'... typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { fe_values.reinit (cell); @@ -485,50 +485,50 @@ namespace Step10 area += fe_values.JxW (i); }; - // ...and store the - // resulting area values - // and the errors in the - // table. We need a static - // cast to double as there - // is no add_value(string, - // long double) function - // implemented. Note that - // this also concerns the - // second call as the fabs - // function in the std - // namespace is overloaded on - // its argument types, so there - // exists a version taking - // and returning a long double, - // in contrast to the global - // namespace where only one such - // function is declared (which - // takes and returns a double). + // ...and store the + // resulting area values + // and the errors in the + // table. We need a static + // cast to double as there + // is no add_value(string, + // long double) function + // implemented. Note that + // this also concerns the + // second call as the fabs + // function in the std + // namespace is overloaded on + // its argument types, so there + // exists a version taking + // and returning a long double, + // in contrast to the global + // namespace where only one such + // function is declared (which + // takes and returns a double). table.add_value("eval.pi", static_cast (area)); table.add_value("error", static_cast (std::fabs(area-pi))); }; - // We want to compute - // the convergence rates of the - // `error' column. Therefore we - // need to omit the other - // columns from the convergence - // rate evaluation before - // calling - // `evaluate_all_convergence_rates' + // We want to compute + // the convergence rates of the + // `error' column. Therefore we + // need to omit the other + // columns from the convergence + // rate evaluation before + // calling + // `evaluate_all_convergence_rates' table.omit_column_from_convergence_rate_evaluation("cells"); table.omit_column_from_convergence_rate_evaluation("eval.pi"); table.evaluate_all_convergence_rates(ConvergenceTable::reduction_rate_log2); - // Finally we set the precision - // and scientific mode for - // output of some of the - // quantities... + // Finally we set the precision + // and scientific mode for + // output of some of the + // quantities... table.set_precision("eval.pi", 16); table.set_scientific("error", true); - // ...and write the whole table - // to std::cout. + // ...and write the whole table + // to std::cout. table.write_text(std::cout); std::cout << std::endl; @@ -536,33 +536,33 @@ namespace Step10 } - // The following, second function also - // computes an approximation of $\pi$ - // but this time via the perimeter - // $2\pi r$ of the domain instead - // of the area. This function is only - // a variation of the previous - // function. So we will mainly give - // documentation for the differences. + // The following, second function also + // computes an approximation of $\pi$ + // but this time via the perimeter + // $2\pi r$ of the domain instead + // of the area. This function is only + // a variation of the previous + // function. So we will mainly give + // documentation for the differences. template void compute_pi_by_perimeter () { std::cout << "Computation of Pi by the perimeter:" << std::endl << "===================================" << std::endl; - // We take the same order of - // quadrature but this time a - // `dim-1' dimensional quadrature - // as we will integrate over - // (boundary) lines rather than - // over cells. + // We take the same order of + // quadrature but this time a + // `dim-1' dimensional quadrature + // as we will integrate over + // (boundary) lines rather than + // over cells. const QGauss quadrature(4); - // We loop over all degrees, create - // the triangulation, the boundary, - // the mapping, the dummy - // finite element and the DoFHandler - // object as seen before. + // We loop over all degrees, create + // the triangulation, the boundary, + // the mapping, the dummy + // finite element and the DoFHandler + // object as seen before. for (unsigned int degree=1; degree<5; ++degree) { std::cout << "Degree = " << degree << std::endl; @@ -577,12 +577,12 @@ namespace Step10 DoFHandler dof_handler (triangulation); - // Then we create a - // FEFaceValues object instead - // of a FEValues object as in - // the previous - // function. Again, we pass a - // mapping as first argument. + // Then we create a + // FEFaceValues object instead + // of a FEValues object as in + // the previous + // function. Again, we pass a + // mapping as first argument. FEFaceValues fe_face_values (mapping, fe, quadrature, update_JxW_values); ConvergenceTable table; @@ -594,40 +594,40 @@ namespace Step10 dof_handler.distribute_dofs (fe); - // Now we run over all - // cells and over all faces - // of each cell. Only the - // contributions of the - // `JxW' values on boundary - // faces are added to the - // long double variable - // `perimeter'. + // Now we run over all + // cells and over all faces + // of each cell. Only the + // contributions of the + // `JxW' values on boundary + // faces are added to the + // long double variable + // `perimeter'. typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); long double perimeter = 0; for (; cell!=endc; ++cell) for (unsigned int face_no=0; face_no::faces_per_cell; ++face_no) if (cell->face(face_no)->at_boundary()) { - // We reinit the - // FEFaceValues - // object with the - // cell iterator - // and the number - // of the face. + // We reinit the + // FEFaceValues + // object with the + // cell iterator + // and the number + // of the face. fe_face_values.reinit (cell, face_no); for (unsigned int i=0; i (perimeter/2.)); table.add_value("error", static_cast (std::fabs(perimeter/2.-pi))); }; - // ...and end this function as - // we did in the previous one: + // ...and end this function as + // we did in the previous one: table.omit_column_from_convergence_rate_evaluation("cells"); table.omit_column_from_convergence_rate_evaluation("eval.pi"); table.evaluate_all_convergence_rates(ConvergenceTable::reduction_rate_log2); @@ -643,11 +643,11 @@ namespace Step10 } - // The following main function just calls the - // above functions in the order of their - // appearance. Apart from this, it looks just - // like the main functions of previous - // tutorial programs. +// The following main function just calls the +// above functions in the order of their +// appearance. Apart from this, it looks just +// like the main functions of previous +// tutorial programs. int main () { try diff --git a/deal.II/examples/step-11/step-11.cc b/deal.II/examples/step-11/step-11.cc index 649cfb1fe0..3d79aeb487 100644 --- a/deal.II/examples/step-11/step-11.cc +++ b/deal.II/examples/step-11/step-11.cc @@ -9,10 +9,10 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // As usual, the program starts with - // a rather long list of include - // files which you are probably - // already used to by now: +// As usual, the program starts with +// a rather long list of include +// files which you are probably +// already used to by now: #include #include #include @@ -36,96 +36,96 @@ #include #include - // Just this one is new: it declares - // a class - // CompressedSparsityPattern, - // which we will use and explain - // further down below. +// Just this one is new: it declares +// a class +// CompressedSparsityPattern, +// which we will use and explain +// further down below. #include - // We will make use of the std::find - // algorithm of the C++ standard - // library, so we have to include the - // following file for its - // declaration: +// We will make use of the std::find +// algorithm of the C++ standard +// library, so we have to include the +// following file for its +// declaration: #include #include #include #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step11 { using namespace dealii; - // Then we declare a class which - // represents the solution of a - // Laplace problem. As this example - // program is based on step-5, the - // class looks rather the same, with - // the sole structural difference - // that the functions - // assemble_system now calls - // solve itself, and is thus - // called assemble_and_solve, and - // that the output function was - // dropped since the solution - // function is so boring that it is - // not worth being viewed. - // - // The only other noteworthy change - // is that the constructor takes a - // value representing the polynomial - // degree of the mapping to be used - // later on, and that it has another - // member variable representing - // exactly this mapping. In general, - // this variable will occur in real - // applications at the same places - // where the finite element is - // declared or used. + // Then we declare a class which + // represents the solution of a + // Laplace problem. As this example + // program is based on step-5, the + // class looks rather the same, with + // the sole structural difference + // that the functions + // assemble_system now calls + // solve itself, and is thus + // called assemble_and_solve, and + // that the output function was + // dropped since the solution + // function is so boring that it is + // not worth being viewed. + // + // The only other noteworthy change + // is that the constructor takes a + // value representing the polynomial + // degree of the mapping to be used + // later on, and that it has another + // member variable representing + // exactly this mapping. In general, + // this variable will occur in real + // applications at the same places + // where the finite element is + // declared or used. template class LaplaceProblem { - public: - LaplaceProblem (const unsigned int mapping_degree); - void run (); + public: + LaplaceProblem (const unsigned int mapping_degree); + void run (); - private: - void setup_system (); - void assemble_and_solve (); - void solve (); + private: + void setup_system (); + void assemble_and_solve (); + void solve (); - Triangulation triangulation; - FE_Q fe; - DoFHandler dof_handler; - MappingQ mapping; + Triangulation triangulation; + FE_Q fe; + DoFHandler dof_handler; + MappingQ mapping; - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - ConstraintMatrix mean_value_constraints; + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + ConstraintMatrix mean_value_constraints; - Vector solution; - Vector system_rhs; + Vector solution; + Vector system_rhs; - TableHandler output_table; + TableHandler output_table; }; - // Construct such an object, by - // initializing the variables. Here, - // we use linear finite elements (the - // argument to the fe variable - // denotes the polynomial degree), - // and mappings of given order. Print - // to screen what we are about to do. + // Construct such an object, by + // initializing the variables. Here, + // we use linear finite elements (the + // argument to the fe variable + // denotes the polynomial degree), + // and mappings of given order. Print + // to screen what we are about to do. template LaplaceProblem::LaplaceProblem (const unsigned int mapping_degree) : - fe (1), - dof_handler (triangulation), - mapping (mapping_degree) + fe (1), + dof_handler (triangulation), + mapping (mapping_degree) { std::cout << "Using mapping with degree " << mapping_degree << ":" << std::endl @@ -135,101 +135,101 @@ namespace Step11 - // The first task is to set up the - // variables for this problem. This - // includes generating a valid - // DoFHandler object, as well as - // the sparsity patterns for the - // matrix, and the object - // representing the constraints that - // the mean value of the degrees of - // freedom on the boundary be zero. + // The first task is to set up the + // variables for this problem. This + // includes generating a valid + // DoFHandler object, as well as + // the sparsity patterns for the + // matrix, and the object + // representing the constraints that + // the mean value of the degrees of + // freedom on the boundary be zero. template void LaplaceProblem::setup_system () { - // The first task is trivial: - // generate an enumeration of the - // degrees of freedom, and - // initialize solution and right - // hand side vector to their - // correct sizes: + // The first task is trivial: + // generate an enumeration of the + // degrees of freedom, and + // initialize solution and right + // hand side vector to their + // correct sizes: dof_handler.distribute_dofs (fe); solution.reinit (dof_handler.n_dofs()); system_rhs.reinit (dof_handler.n_dofs()); - // Next task is to construct the - // object representing the - // constraint that the mean value - // of the degrees of freedom on the - // boundary shall be zero. For - // this, we first want a list of - // those nodes which are actually - // at the boundary. The - // DoFTools class has a - // function that returns an array - // of boolean values where true - // indicates that the node is at - // the boundary. The second - // argument denotes a mask - // selecting which components of - // vector valued finite elements we - // want to be considered. This sort - // of information is encoded using - // the ComponentMask class (see also - // @ref GlossComponentMask). Since we - // have a scalar finite element - // anyway, this mask in reality should - // have only one entry with a - // true value. However, - // the ComponentMask class has - // semantics that allow it to - // represents a mask of indefinite - // size whose every element equals - // true when one just - // default constructs such an object, - // so this is what we'll do here. + // Next task is to construct the + // object representing the + // constraint that the mean value + // of the degrees of freedom on the + // boundary shall be zero. For + // this, we first want a list of + // those nodes which are actually + // at the boundary. The + // DoFTools class has a + // function that returns an array + // of boolean values where true + // indicates that the node is at + // the boundary. The second + // argument denotes a mask + // selecting which components of + // vector valued finite elements we + // want to be considered. This sort + // of information is encoded using + // the ComponentMask class (see also + // @ref GlossComponentMask). Since we + // have a scalar finite element + // anyway, this mask in reality should + // have only one entry with a + // true value. However, + // the ComponentMask class has + // semantics that allow it to + // represents a mask of indefinite + // size whose every element equals + // true when one just + // default constructs such an object, + // so this is what we'll do here. std::vector boundary_dofs (dof_handler.n_dofs(), false); DoFTools::extract_boundary_dofs (dof_handler, - ComponentMask(), + ComponentMask(), boundary_dofs); - // Now first for the generation of - // the constraints: as mentioned in - // the introduction, we constrain - // one of the nodes on the boundary - // by the values of all other DoFs - // on the boundary. So, let us - // first pick out the first - // boundary node from this list. We - // do that by searching for the - // first true value in the - // array (note that std::find - // returns an iterator to this - // element), and computing its - // distance to the overall first - // element in the array to get its - // index: + // Now first for the generation of + // the constraints: as mentioned in + // the introduction, we constrain + // one of the nodes on the boundary + // by the values of all other DoFs + // on the boundary. So, let us + // first pick out the first + // boundary node from this list. We + // do that by searching for the + // first true value in the + // array (note that std::find + // returns an iterator to this + // element), and computing its + // distance to the overall first + // element in the array to get its + // index: const unsigned int first_boundary_dof = std::distance (boundary_dofs.begin(), std::find (boundary_dofs.begin(), boundary_dofs.end(), true)); - // Then generate a constraints - // object with just this one - // constraint. First clear all - // previous content (which might - // reside there from the previous - // computation on a once coarser - // grid), then add this one line - // constraining the - // first_boundary_dof to the - // sum of other boundary DoFs each - // with weight -1. Finally, close - // the constraints object, i.e. do - // some internal bookkeeping on it - // for faster processing of what is - // to come later: + // Then generate a constraints + // object with just this one + // constraint. First clear all + // previous content (which might + // reside there from the previous + // computation on a once coarser + // grid), then add this one line + // constraining the + // first_boundary_dof to the + // sum of other boundary DoFs each + // with weight -1. Finally, close + // the constraints object, i.e. do + // some internal bookkeeping on it + // for faster processing of what is + // to come later: mean_value_constraints.clear (); mean_value_constraints.add_line (first_boundary_dof); for (unsigned int i=first_boundary_dof+1; iDoFTools::make_sparsity_pattern - // and condense the result using - // the hanging node constraints. We - // have no hanging node constraints - // here (since we only refine - // globally in this example), but - // we have this global constraint - // on the boundary. This poses one - // severe problem in this context: - // the SparsityPattern class - // wants us to state beforehand the - // maximal number of entries per - // row, either for all rows or for - // each row separately. There are - // functions in the library which - // can tell you this number in case - // you just have hanging node - // constraints (namely - // DoFHandler::max_coupling_between_dofs), - // but how is this for the present - // case? The difficulty arises - // because the elimination of the - // constrained degree of freedom - // requires a number of additional - // entries in the matrix at places - // that are not so simple to - // determine. We would therefore - // have a problem had we to give a - // maximal number of entries per - // row here. - // - // Since this can be so difficult - // that no reasonable answer can be - // given that allows allocation of - // only a reasonable amount of - // memory, there is a class - // CompressedSparsityPattern, - // that can help us out here. It - // does not require that we know in - // advance how many entries rows - // could have, but allows just - // about any length. It is thus - // significantly more flexible in - // case you do not have good - // estimates of row lengths, - // however at the price that - // building up such a pattern is - // also significantly more - // expensive than building up a - // pattern for which you had - // information in - // advance. Nevertheless, as we - // have no other choice here, we'll - // just build such an object by - // initializing it with the - // dimensions of the matrix and - // calling another function - // DoFTools::make_sparsity_pattern - // to get the sparsity pattern due - // to the differential operator, - // then condense it with the - // constraints object which adds - // those positions in the sparsity - // pattern that are required for - // the elimination of the - // constraint. + // Next task is to generate a + // sparsity pattern. This is indeed + // a tricky task here. Usually, we + // just call + // DoFTools::make_sparsity_pattern + // and condense the result using + // the hanging node constraints. We + // have no hanging node constraints + // here (since we only refine + // globally in this example), but + // we have this global constraint + // on the boundary. This poses one + // severe problem in this context: + // the SparsityPattern class + // wants us to state beforehand the + // maximal number of entries per + // row, either for all rows or for + // each row separately. There are + // functions in the library which + // can tell you this number in case + // you just have hanging node + // constraints (namely + // DoFHandler::max_coupling_between_dofs), + // but how is this for the present + // case? The difficulty arises + // because the elimination of the + // constrained degree of freedom + // requires a number of additional + // entries in the matrix at places + // that are not so simple to + // determine. We would therefore + // have a problem had we to give a + // maximal number of entries per + // row here. + // + // Since this can be so difficult + // that no reasonable answer can be + // given that allows allocation of + // only a reasonable amount of + // memory, there is a class + // CompressedSparsityPattern, + // that can help us out here. It + // does not require that we know in + // advance how many entries rows + // could have, but allows just + // about any length. It is thus + // significantly more flexible in + // case you do not have good + // estimates of row lengths, + // however at the price that + // building up such a pattern is + // also significantly more + // expensive than building up a + // pattern for which you had + // information in + // advance. Nevertheless, as we + // have no other choice here, we'll + // just build such an object by + // initializing it with the + // dimensions of the matrix and + // calling another function + // DoFTools::make_sparsity_pattern + // to get the sparsity pattern due + // to the differential operator, + // then condense it with the + // constraints object which adds + // those positions in the sparsity + // pattern that are required for + // the elimination of the + // constraint. CompressedSparsityPattern csp (dof_handler.n_dofs(), dof_handler.n_dofs()); DoFTools::make_sparsity_pattern (dof_handler, csp); mean_value_constraints.condense (csp); - // Finally, once we have the full - // pattern, we can initialize an - // object of type - // SparsityPattern from it and - // in turn initialize the matrix - // with it. Note that this is - // actually necessary, since the - // CompressedSparsityPattern is - // so inefficient compared to the - // SparsityPattern class due to - // the more flexible data - // structures it has to use, that - // we can impossibly base the - // sparse matrix class on it, but - // rather need an object of type - // SparsityPattern, which we - // generate by copying from the - // intermediate object. - // - // As a further sidenote, you will - // notice that we do not explicitly - // have to compress the - // sparsity pattern here. This, of - // course, is due to the fact that - // the copy_from function - // generates a compressed object - // right from the start, to which - // you cannot add new entries - // anymore. The compress call - // is therefore implicit in the - // copy_from call. + // Finally, once we have the full + // pattern, we can initialize an + // object of type + // SparsityPattern from it and + // in turn initialize the matrix + // with it. Note that this is + // actually necessary, since the + // CompressedSparsityPattern is + // so inefficient compared to the + // SparsityPattern class due to + // the more flexible data + // structures it has to use, that + // we can impossibly base the + // sparse matrix class on it, but + // rather need an object of type + // SparsityPattern, which we + // generate by copying from the + // intermediate object. + // + // As a further sidenote, you will + // notice that we do not explicitly + // have to compress the + // sparsity pattern here. This, of + // course, is due to the fact that + // the copy_from function + // generates a compressed object + // right from the start, to which + // you cannot add new entries + // anymore. The compress call + // is therefore implicit in the + // copy_from call. sparsity_pattern.copy_from (csp); system_matrix.reinit (sparsity_pattern); } - // The next function then assembles - // the linear system of equations, - // solves it, and evaluates the - // solution. This then makes three - // actions, and we will put them into - // eight true statements (excluding - // declaration of variables, and - // handling of temporary - // vectors). Thus, this function is - // something for the very - // lazy. Nevertheless, the functions - // called are rather powerful, and - // through them this function uses a - // good deal of the whole - // library. But let's look at each of - // the steps. + // The next function then assembles + // the linear system of equations, + // solves it, and evaluates the + // solution. This then makes three + // actions, and we will put them into + // eight true statements (excluding + // declaration of variables, and + // handling of temporary + // vectors). Thus, this function is + // something for the very + // lazy. Nevertheless, the functions + // called are rather powerful, and + // through them this function uses a + // good deal of the whole + // library. But let's look at each of + // the steps. template void LaplaceProblem::assemble_and_solve () { - // First, we have to assemble the - // matrix and the right hand - // side. In all previous examples, - // we have investigated various - // ways how to do this - // manually. However, since the - // Laplace matrix and simple right - // hand sides appear so frequently - // in applications, the library - // provides functions for actually - // doing this for you, i.e. they - // perform the loop over all cells, - // setting up the local matrices - // and vectors, and putting them - // together for the end result. - // - // The following are the two most - // commonly used ones: creation of - // the Laplace matrix and creation - // of a right hand side vector from - // body or boundary forces. They - // take the mapping object, the - // DoFHandler object - // representing the degrees of - // freedom and the finite element - // in use, a quadrature formula to - // be used, and the output - // object. The function that - // creates a right hand side vector - // also has to take a function - // object describing the - // (continuous) right hand side - // function. - // - // Let us look at the way the - // matrix and body forces are - // integrated: + // First, we have to assemble the + // matrix and the right hand + // side. In all previous examples, + // we have investigated various + // ways how to do this + // manually. However, since the + // Laplace matrix and simple right + // hand sides appear so frequently + // in applications, the library + // provides functions for actually + // doing this for you, i.e. they + // perform the loop over all cells, + // setting up the local matrices + // and vectors, and putting them + // together for the end result. + // + // The following are the two most + // commonly used ones: creation of + // the Laplace matrix and creation + // of a right hand side vector from + // body or boundary forces. They + // take the mapping object, the + // DoFHandler object + // representing the degrees of + // freedom and the finite element + // in use, a quadrature formula to + // be used, and the output + // object. The function that + // creates a right hand side vector + // also has to take a function + // object describing the + // (continuous) right hand side + // function. + // + // Let us look at the way the + // matrix and body forces are + // integrated: const unsigned int gauss_degree = std::max (static_cast(std::ceil(1.*(mapping.get_degree()+1)/2)), 2U); @@ -417,152 +417,152 @@ namespace Step11 QGauss(gauss_degree), ConstantFunction(-2), system_rhs); - // That's quite simple, right? - // - // Two remarks are in order, - // though: First, these functions - // are used in a lot of - // contexts. Maybe you want to - // create a Laplace or mass matrix - // for a vector values finite - // element; or you want to use the - // default Q1 mapping; or you want - // to assembled the matrix with a - // coefficient in the Laplace - // operator. For this reason, there - // are quite a large number of - // variants of these functions in - // the MatrixCreator and - // MatrixTools - // classes. Whenever you need a - // slightly different version of - // these functions than the ones - // called above, it is certainly - // worthwhile to take a look at the - // documentation and to check - // whether something fits your - // needs. - // - // The second remark concerns the - // quadrature formula we use: we - // want to integrate over bilinear - // shape functions, so we know that - // we have to use at least a Gauss2 - // quadrature formula. On the other - // hand, we want to have the - // quadrature rule to have at least - // the order of the boundary - // approximation. Since the order - // of Gauss-r is 2r, and the order - // of the boundary approximation - // using polynomials of degree p is - // p+1, we know that 2r@>=p+1. Since - // r has to be an integer and (as - // mentioned above) has to be at - // least 2, this makes up for the - // formula above computing - // gauss_degree. - // - // Since the generation of the body - // force contributions to the right - // hand side vector was so simple, - // we do that all over again for - // the boundary forces as well: - // allocate a vector of the right - // size and call the right - // function. The boundary function - // has constant values, so we can - // generate an object from the - // library on the fly, and we use - // the same quadrature formula as - // above, but this time of lower - // dimension since we integrate - // over faces now instead of cells: + // That's quite simple, right? + // + // Two remarks are in order, + // though: First, these functions + // are used in a lot of + // contexts. Maybe you want to + // create a Laplace or mass matrix + // for a vector values finite + // element; or you want to use the + // default Q1 mapping; or you want + // to assembled the matrix with a + // coefficient in the Laplace + // operator. For this reason, there + // are quite a large number of + // variants of these functions in + // the MatrixCreator and + // MatrixTools + // classes. Whenever you need a + // slightly different version of + // these functions than the ones + // called above, it is certainly + // worthwhile to take a look at the + // documentation and to check + // whether something fits your + // needs. + // + // The second remark concerns the + // quadrature formula we use: we + // want to integrate over bilinear + // shape functions, so we know that + // we have to use at least a Gauss2 + // quadrature formula. On the other + // hand, we want to have the + // quadrature rule to have at least + // the order of the boundary + // approximation. Since the order + // of Gauss-r is 2r, and the order + // of the boundary approximation + // using polynomials of degree p is + // p+1, we know that 2r@>=p+1. Since + // r has to be an integer and (as + // mentioned above) has to be at + // least 2, this makes up for the + // formula above computing + // gauss_degree. + // + // Since the generation of the body + // force contributions to the right + // hand side vector was so simple, + // we do that all over again for + // the boundary forces as well: + // allocate a vector of the right + // size and call the right + // function. The boundary function + // has constant values, so we can + // generate an object from the + // library on the fly, and we use + // the same quadrature formula as + // above, but this time of lower + // dimension since we integrate + // over faces now instead of cells: Vector tmp (system_rhs.size()); VectorTools::create_boundary_right_hand_side (mapping, dof_handler, QGauss(gauss_degree), ConstantFunction(1), tmp); - // Then add the contributions from - // the boundary to those from the - // interior of the domain: + // Then add the contributions from + // the boundary to those from the + // interior of the domain: system_rhs += tmp; - // For assembling the right hand - // side, we had to use two - // different vector objects, and - // later add them together. The - // reason we had to do so is that - // the - // VectorTools::create_right_hand_side - // and - // VectorTools::create_boundary_right_hand_side - // functions first clear the output - // vector, rather than adding up - // their results to previous - // contents. This can reasonably be - // called a design flaw in the - // library made in its infancy, but - // unfortunately things are as they - // are for some time now and it is - // difficult to change such things - // that silently break existing - // code, so we have to live with - // that. - - // Now, the linear system is set - // up, so we can eliminate the one - // degree of freedom which we - // constrained to the other DoFs on - // the boundary for the mean value - // constraint from matrix and right - // hand side vector, and solve the - // system. After that, distribute - // the constraints again, which in - // this case means setting the - // constrained degree of freedom to - // its proper value + // For assembling the right hand + // side, we had to use two + // different vector objects, and + // later add them together. The + // reason we had to do so is that + // the + // VectorTools::create_right_hand_side + // and + // VectorTools::create_boundary_right_hand_side + // functions first clear the output + // vector, rather than adding up + // their results to previous + // contents. This can reasonably be + // called a design flaw in the + // library made in its infancy, but + // unfortunately things are as they + // are for some time now and it is + // difficult to change such things + // that silently break existing + // code, so we have to live with + // that. + + // Now, the linear system is set + // up, so we can eliminate the one + // degree of freedom which we + // constrained to the other DoFs on + // the boundary for the mean value + // constraint from matrix and right + // hand side vector, and solve the + // system. After that, distribute + // the constraints again, which in + // this case means setting the + // constrained degree of freedom to + // its proper value mean_value_constraints.condense (system_matrix); mean_value_constraints.condense (system_rhs); solve (); mean_value_constraints.distribute (solution); - // Finally, evaluate what we got as - // solution. As stated in the - // introduction, we are interested - // in the H1 semi-norm of the - // solution. Here, as well, we have - // a function in the library that - // does this, although in a - // slightly non-obvious way: the - // VectorTools::integrate_difference - // function integrates the norm of - // the difference between a finite - // element function and a - // continuous function. If we - // therefore want the norm of a - // finite element field, we just - // put the continuous function to - // zero. Note that this function, - // just as so many other ones in - // the library as well, has at - // least two versions, one which - // takes a mapping as argument - // (which we make us of here), and - // the one which we have used in - // previous examples which - // implicitly uses MappingQ1. - // Also note that we take a - // quadrature formula of one degree - // higher, in order to avoid - // superconvergence effects where - // the solution happens to be - // especially close to the exact - // solution at certain points (we - // don't know whether this might be - // the case here, but there are - // cases known of this, and we just - // want to make sure): + // Finally, evaluate what we got as + // solution. As stated in the + // introduction, we are interested + // in the H1 semi-norm of the + // solution. Here, as well, we have + // a function in the library that + // does this, although in a + // slightly non-obvious way: the + // VectorTools::integrate_difference + // function integrates the norm of + // the difference between a finite + // element function and a + // continuous function. If we + // therefore want the norm of a + // finite element field, we just + // put the continuous function to + // zero. Note that this function, + // just as so many other ones in + // the library as well, has at + // least two versions, one which + // takes a mapping as argument + // (which we make us of here), and + // the one which we have used in + // previous examples which + // implicitly uses MappingQ1. + // Also note that we take a + // quadrature formula of one degree + // higher, in order to avoid + // superconvergence effects where + // the solution happens to be + // especially close to the exact + // solution at certain points (we + // don't know whether this might be + // the case here, but there are + // cases known of this, and we just + // want to make sure): Vector norm_per_cell (triangulation.n_active_cells()); VectorTools::integrate_difference (mapping, dof_handler, solution, @@ -570,17 +570,17 @@ namespace Step11 norm_per_cell, QGauss(gauss_degree+1), VectorTools::H1_seminorm); - // Then, the function just called - // returns its results as a vector - // of values each of which denotes - // the norm on one cell. To get the - // global norm, a simple - // computation shows that we have - // to take the l2 norm of the - // vector: + // Then, the function just called + // returns its results as a vector + // of values each of which denotes + // the norm on one cell. To get the + // global norm, a simple + // computation shows that we have + // to take the l2 norm of the + // vector: const double norm = norm_per_cell.l2_norm(); - // Last task -- generate output: + // Last task -- generate output: output_table.add_value ("cells", triangulation.n_active_cells()); output_table.add_value ("|u|_1", norm); output_table.add_value ("error", std::fabs(norm-std::sqrt(3.14159265358/2))); @@ -588,10 +588,10 @@ namespace Step11 - // The following function solving the - // linear system of equations is - // copied from step-5 and is - // explained there in some detail: + // The following function solving the + // linear system of equations is + // copied from step-5 and is + // explained there in some detail: template void LaplaceProblem::solve () { @@ -607,34 +607,34 @@ namespace Step11 - // Finally the main function - // controlling the different steps to - // be performed. Its content is - // rather straightforward, generating - // a triangulation of a circle, - // associating a boundary to it, and - // then doing several cycles on - // subsequently finer grids. Note - // again that we have put mesh - // refinement into the loop header; - // this may be something for a test - // program, but for real applications - // you should consider that this - // implies that the mesh is refined - // after the loop is executed the - // last time since the increment - // clause (the last part of the - // three-parted loop header) is - // executed before the comparison - // part (the second one), which may - // be rather costly if the mesh is - // already quite refined. In that - // case, you should arrange code such - // that the mesh is not further - // refined after the last loop run - // (or you should do it at the - // beginning of each run except for - // the first one). + // Finally the main function + // controlling the different steps to + // be performed. Its content is + // rather straightforward, generating + // a triangulation of a circle, + // associating a boundary to it, and + // then doing several cycles on + // subsequently finer grids. Note + // again that we have put mesh + // refinement into the loop header; + // this may be something for a test + // program, but for real applications + // you should consider that this + // implies that the mesh is refined + // after the loop is executed the + // last time since the increment + // clause (the last part of the + // three-parted loop header) is + // executed before the comparison + // part (the second one), which may + // be rather costly if the mesh is + // already quite refined. In that + // case, you should arrange code such + // that the mesh is not further + // refined after the last loop run + // (or you should do it at the + // beginning of each run except for + // the first one). template void LaplaceProblem::run () { @@ -648,9 +648,9 @@ namespace Step11 assemble_and_solve (); }; - // After all the data is generated, - // write a table of results to the - // screen: + // After all the data is generated, + // write a table of results to the + // screen: output_table.set_precision("|u|_1", 6); output_table.set_precision("error", 6); output_table.write_text (std::cout); @@ -660,11 +660,11 @@ namespace Step11 - // Finally the main function. It's - // structure is the same as that used - // in several of the previous - // examples, so probably needs no - // more explanation. +// Finally the main function. It's +// structure is the same as that used +// in several of the previous +// examples, so probably needs no +// more explanation. int main () { try @@ -672,18 +672,18 @@ int main () dealii::deallog.depth_console (0); std::cout.precision(5); - // This is the main loop, doing - // the computations with - // mappings of linear through - // cubic mappings. Note that - // since we need the object of - // type LaplaceProblem@<2@> - // only once, we do not even - // name it, but create an - // unnamed such object and call - // the run function of it, - // subsequent to which it is - // immediately destroyed again. + // This is the main loop, doing + // the computations with + // mappings of linear through + // cubic mappings. Note that + // since we need the object of + // type LaplaceProblem@<2@> + // only once, we do not even + // name it, but create an + // unnamed such object and call + // the run function of it, + // subsequent to which it is + // immediately destroyed again. for (unsigned int mapping_degree=1; mapping_degree<=3; ++mapping_degree) Step11::LaplaceProblem<2>(mapping_degree).run (); } diff --git a/deal.II/examples/step-12/step-12.cc b/deal.II/examples/step-12/step-12.cc index 68d703d6a7..392eacf1be 100644 --- a/deal.II/examples/step-12/step-12.cc +++ b/deal.II/examples/step-12/step-12.cc @@ -9,10 +9,10 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // The first few files have already - // been covered in previous examples - // and will thus not be further - // commented on: +// The first few files have already +// been covered in previous examples +// and will thus not be further +// commented on: #include #include #include @@ -30,66 +30,66 @@ #include #include #include - // Here the discontinuous finite elements are - // defined. They are used in the same way as - // all other finite elements, though -- as - // you have seen in previous tutorial - // programs -- there isn't much user - // interaction with finite element classes at - // all: the are passed to - // DoFHandler and - // FEValues objects, and that is - // about it. +// Here the discontinuous finite elements are +// defined. They are used in the same way as +// all other finite elements, though -- as +// you have seen in previous tutorial +// programs -- there isn't much user +// interaction with finite element classes at +// all: the are passed to +// DoFHandler and +// FEValues objects, and that is +// about it. #include - // We are going to use the simplest - // possible solver, called Richardson - // iteration, that represents a - // simple defect correction. This, in - // combination with a block SSOR - // preconditioner (defined in - // precondition_block.h), that uses - // the special block matrix structure - // of system matrices arising from DG - // discretizations. +// We are going to use the simplest +// possible solver, called Richardson +// iteration, that represents a +// simple defect correction. This, in +// combination with a block SSOR +// preconditioner (defined in +// precondition_block.h), that uses +// the special block matrix structure +// of system matrices arising from DG +// discretizations. #include #include - // We are going to use gradients as - // refinement indicator. +// We are going to use gradients as +// refinement indicator. #include - // Here come the new include files - // for using the MeshWorker - // framework. The first contains the - // class - // MeshWorker::DoFInfo, - // which provides local integrators - // with a mapping between local and - // global degrees of freedom. It - // stores the results of local - // integrals as well in its base - // class Meshworker::LocalResults. - // In the second of these files, we - // find an object of type - // MeshWorker::IntegrationInfo, which - // is mostly a wrapper around a group - // of FEValues objects. The file - // meshworker/simple.h - // contains classes assembling - // locally integrated data into a - // global system containing only a - // single matrix. Finally, we will - // need the file that runs the loop - // over all mesh cells and faces. +// Here come the new include files +// for using the MeshWorker +// framework. The first contains the +// class +// MeshWorker::DoFInfo, +// which provides local integrators +// with a mapping between local and +// global degrees of freedom. It +// stores the results of local +// integrals as well in its base +// class Meshworker::LocalResults. +// In the second of these files, we +// find an object of type +// MeshWorker::IntegrationInfo, which +// is mostly a wrapper around a group +// of FEValues objects. The file +// meshworker/simple.h +// contains classes assembling +// locally integrated data into a +// global system containing only a +// single matrix. Finally, we will +// need the file that runs the loop +// over all mesh cells and faces. #include #include #include #include - // Like in all programs, we finish - // this section by including the - // needed C++ headers and declaring - // we want to use objects in the - // dealii namespace without prefix. +// Like in all programs, we finish +// this section by including the +// needed C++ headers and declaring +// we want to use objects in the +// dealii namespace without prefix. #include #include @@ -98,32 +98,32 @@ namespace Step12 { using namespace dealii; - // @sect3{Equation data} - // - // First, we define a class - // describing the inhomogeneous - // boundary data. Since only its - // values are used, we implement - // value_list(), but leave all other - // functions of Function undefined. + // @sect3{Equation data} + // + // First, we define a class + // describing the inhomogeneous + // boundary data. Since only its + // values are used, we implement + // value_list(), but leave all other + // functions of Function undefined. template class BoundaryValues: public Function { - public: - BoundaryValues () {}; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component=0) const; + public: + BoundaryValues () {}; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component=0) const; }; - // Given the flow direction, the inflow - // boundary of the unit square $[0,1]^2$ are - // the right and the lower boundaries. We - // prescribe discontinuous boundary values 1 - // and 0 on the x-axis and value 0 on the - // right boundary. The values of this - // function on the outflow boundaries will - // not be used within the DG scheme. + // Given the flow direction, the inflow + // boundary of the unit square $[0,1]^2$ are + // the right and the lower boundaries. We + // prescribe discontinuous boundary values 1 + // and 0 on the x-axis and value 0 on the + // right boundary. The values of this + // function on the outflow boundaries will + // not be used within the DG scheme. template void BoundaryValues::value_list(const std::vector > &points, std::vector &values, @@ -140,321 +140,321 @@ namespace Step12 values[i]=0.; } } - // @sect3{The AdvectionProblem class} - // - // After this preparations, we - // proceed with the main class of - // this program, - // called AdvectionProblem. It is basically - // the main class of step-6. We do - // not have a ConstraintMatrix, - // because there are no hanging node - // constraints in DG discretizations. - - // Major differences will only come - // up in the implementation of the - // assemble functions, since here, we - // not only need to cover the flux - // integrals over faces, we also use - // the MeshWorker interface to - // simplify the loops involved. + // @sect3{The AdvectionProblem class} + // + // After this preparations, we + // proceed with the main class of + // this program, + // called AdvectionProblem. It is basically + // the main class of step-6. We do + // not have a ConstraintMatrix, + // because there are no hanging node + // constraints in DG discretizations. + + // Major differences will only come + // up in the implementation of the + // assemble functions, since here, we + // not only need to cover the flux + // integrals over faces, we also use + // the MeshWorker interface to + // simplify the loops involved. template class AdvectionProblem { - public: - AdvectionProblem (); - void run (); - - private: - void setup_system (); - void assemble_system (); - void solve (Vector &solution); - void refine_grid (); - void output_results (const unsigned int cycle) const; - - Triangulation triangulation; - const MappingQ1 mapping; - - // Furthermore we want to use DG - // elements of degree 1 (but this - // is only specified in the - // constructor). If you want to - // use a DG method of a different - // degree the whole program stays - // the same, only replace 1 in - // the constructor by the desired - // polynomial degree. - FE_DGQ fe; - DoFHandler dof_handler; - - // The next four members represent the - // linear system to be - // solved. system_matrix and - // right_hand_side are - // generated by - // assemble_system(), the - // solution is computed in - // solve(). The - // sparsity_pattern is used - // to determine the location of nonzero - // elements in - // system_matrix. - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - - Vector solution; - Vector right_hand_side; - - // Finally, we have to provide - // functions that assemble the - // cell, boundary, and inner face - // terms. Within the MeshWorker - // framework, the loop over all - // cells and much of the setup of - // operations will be done - // outside this class, so all we - // have to provide are these - // three operations. They will - // then work on intermediate - // objects for which first, we - // here define typedefs to the - // info objects handed to the - // local integration functions in - // order to make our life easier - // below. - typedef MeshWorker::DoFInfo DoFInfo; - typedef MeshWorker::IntegrationInfo CellInfo; - - // The following three functions - // are then the ones that get called - // inside the generic loop over all - // cells and faces. They are the - // ones doing the actual - // integration. - // - // In our code below, these - // functions do not access member - // variables of the current - // class, so we can mark them as - // static and simply - // pass pointers to these - // functions to the MeshWorker - // framework. If, however, these - // functions would want to access - // member variables (or needed - // additional arguments beyond - // the ones specified below), we - // could use the facilities of - // boost::bind (or std::bind, - // respectively) to provide the - // MeshWorker framework with - // objects that act as if they - // had the required number and - // types of arguments, but have - // in fact other arguments - // already bound. - static void integrate_cell_term (DoFInfo& dinfo, - CellInfo& info); - static void integrate_boundary_term (DoFInfo& dinfo, - CellInfo& info); - static void integrate_face_term (DoFInfo& dinfo1, - DoFInfo& dinfo2, - CellInfo& info1, - CellInfo& info2); + public: + AdvectionProblem (); + void run (); + + private: + void setup_system (); + void assemble_system (); + void solve (Vector &solution); + void refine_grid (); + void output_results (const unsigned int cycle) const; + + Triangulation triangulation; + const MappingQ1 mapping; + + // Furthermore we want to use DG + // elements of degree 1 (but this + // is only specified in the + // constructor). If you want to + // use a DG method of a different + // degree the whole program stays + // the same, only replace 1 in + // the constructor by the desired + // polynomial degree. + FE_DGQ fe; + DoFHandler dof_handler; + + // The next four members represent the + // linear system to be + // solved. system_matrix and + // right_hand_side are + // generated by + // assemble_system(), the + // solution is computed in + // solve(). The + // sparsity_pattern is used + // to determine the location of nonzero + // elements in + // system_matrix. + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + + Vector solution; + Vector right_hand_side; + + // Finally, we have to provide + // functions that assemble the + // cell, boundary, and inner face + // terms. Within the MeshWorker + // framework, the loop over all + // cells and much of the setup of + // operations will be done + // outside this class, so all we + // have to provide are these + // three operations. They will + // then work on intermediate + // objects for which first, we + // here define typedefs to the + // info objects handed to the + // local integration functions in + // order to make our life easier + // below. + typedef MeshWorker::DoFInfo DoFInfo; + typedef MeshWorker::IntegrationInfo CellInfo; + + // The following three functions + // are then the ones that get called + // inside the generic loop over all + // cells and faces. They are the + // ones doing the actual + // integration. + // + // In our code below, these + // functions do not access member + // variables of the current + // class, so we can mark them as + // static and simply + // pass pointers to these + // functions to the MeshWorker + // framework. If, however, these + // functions would want to access + // member variables (or needed + // additional arguments beyond + // the ones specified below), we + // could use the facilities of + // boost::bind (or std::bind, + // respectively) to provide the + // MeshWorker framework with + // objects that act as if they + // had the required number and + // types of arguments, but have + // in fact other arguments + // already bound. + static void integrate_cell_term (DoFInfo &dinfo, + CellInfo &info); + static void integrate_boundary_term (DoFInfo &dinfo, + CellInfo &info); + static void integrate_face_term (DoFInfo &dinfo1, + DoFInfo &dinfo2, + CellInfo &info1, + CellInfo &info2); }; - // We start with the constructor. The 1 in - // the constructor call of fe is - // the polynomial degree. + // We start with the constructor. The 1 in + // the constructor call of fe is + // the polynomial degree. template AdvectionProblem::AdvectionProblem () - : - mapping (), - fe (1), - dof_handler (triangulation) + : + mapping (), + fe (1), + dof_handler (triangulation) {} template void AdvectionProblem::setup_system () { - // In the function that sets up the usual - // finite element data structures, we first - // need to distribute the DoFs. + // In the function that sets up the usual + // finite element data structures, we first + // need to distribute the DoFs. dof_handler.distribute_dofs (fe); - // We start by generating the sparsity - // pattern. To this end, we first fill an - // intermediate object of type - // CompressedSparsityPattern with the - // couplings appearing in the system. After - // building the pattern, this object is - // copied to sparsity_pattern - // and can be discarded. - - // To build the sparsity pattern for DG - // discretizations, we can call the - // function analogue to - // DoFTools::make_sparsity_pattern, which - // is called - // DoFTools::make_flux_sparsity_pattern: + // We start by generating the sparsity + // pattern. To this end, we first fill an + // intermediate object of type + // CompressedSparsityPattern with the + // couplings appearing in the system. After + // building the pattern, this object is + // copied to sparsity_pattern + // and can be discarded. + + // To build the sparsity pattern for DG + // discretizations, we can call the + // function analogue to + // DoFTools::make_sparsity_pattern, which + // is called + // DoFTools::make_flux_sparsity_pattern: CompressedSparsityPattern c_sparsity(dof_handler.n_dofs()); DoFTools::make_flux_sparsity_pattern (dof_handler, c_sparsity); sparsity_pattern.copy_from(c_sparsity); - // Finally, we set up the structure - // of all components of the linear system. + // Finally, we set up the structure + // of all components of the linear system. system_matrix.reinit (sparsity_pattern); solution.reinit (dof_handler.n_dofs()); right_hand_side.reinit (dof_handler.n_dofs()); } - // @sect4{The assemble_system function} + // @sect4{The assemble_system function} - // Here we see the major difference to - // assembling by hand. Instead of writing - // loops over cells and faces, we leave all - // this to the MeshWorker framework. In order - // to do so, we just have to define local - // integration functions and use one of the - // classes in namespace MeshWorker::Assembler - // to build the global system. + // Here we see the major difference to + // assembling by hand. Instead of writing + // loops over cells and faces, we leave all + // this to the MeshWorker framework. In order + // to do so, we just have to define local + // integration functions and use one of the + // classes in namespace MeshWorker::Assembler + // to build the global system. template void AdvectionProblem::assemble_system () { - // This is the magic object, which - // knows everything about the data - // structures and local - // integration. This is the object - // doing the work in the function - // MeshWorker::loop(), which is - // implicitly called by - // MeshWorker::integration_loop() - // below. After the functions to - // which we provide pointers did - // the local integration, the - // MeshWorker::Assembler::SystemSimple - // object distributes these into - // the global sparse matrix and the - // right hand side vector. + // This is the magic object, which + // knows everything about the data + // structures and local + // integration. This is the object + // doing the work in the function + // MeshWorker::loop(), which is + // implicitly called by + // MeshWorker::integration_loop() + // below. After the functions to + // which we provide pointers did + // the local integration, the + // MeshWorker::Assembler::SystemSimple + // object distributes these into + // the global sparse matrix and the + // right hand side vector. MeshWorker::IntegrationInfoBox info_box; - // First, we initialize the - // quadrature formulae and the - // update flags in the worker base - // class. For quadrature, we play - // safe and use a QGauss formula - // with number of points one higher - // than the polynomial degree - // used. Since the quadratures for - // cells, boundary and interior - // faces can be selected - // independently, we have to hand - // over this value three times. + // First, we initialize the + // quadrature formulae and the + // update flags in the worker base + // class. For quadrature, we play + // safe and use a QGauss formula + // with number of points one higher + // than the polynomial degree + // used. Since the quadratures for + // cells, boundary and interior + // faces can be selected + // independently, we have to hand + // over this value three times. const unsigned int n_gauss_points = dof_handler.get_fe().degree+1; info_box.initialize_gauss_quadrature(n_gauss_points, n_gauss_points, n_gauss_points); - // These are the types of values we - // need for integrating our - // system. They are added to the - // flags used on cells, boundary - // and interior faces, as well as - // interior neighbor faces, which is - // forced by the four @p true - // values. + // These are the types of values we + // need for integrating our + // system. They are added to the + // flags used on cells, boundary + // and interior faces, as well as + // interior neighbor faces, which is + // forced by the four @p true + // values. info_box.initialize_update_flags(); UpdateFlags update_flags = update_quadrature_points | update_values | update_gradients; info_box.add_update_flags(update_flags, true, true, true, true); - // After preparing all data in - // info_box, we initialize - // the FEValues objects in there. + // After preparing all data in + // info_box, we initialize + // the FEValues objects in there. info_box.initialize(fe, mapping); - // The object created so far helps - // us do the local integration on - // each cell and face. Now, we need - // an object which receives the - // integrated (local) data and - // forwards them to the assembler. + // The object created so far helps + // us do the local integration on + // each cell and face. Now, we need + // an object which receives the + // integrated (local) data and + // forwards them to the assembler. MeshWorker::DoFInfo dof_info(dof_handler); - // Now, we have to create the - // assembler object and tell it, - // where to put the local - // data. These will be our system - // matrix and the right hand side. + // Now, we have to create the + // assembler object and tell it, + // where to put the local + // data. These will be our system + // matrix and the right hand side. MeshWorker::Assembler::SystemSimple, Vector > - assembler; + assembler; assembler.initialize(system_matrix, right_hand_side); - // Finally, the integration loop - // over all active cells - // (determined by the first - // argument, which is an active - // iterator). - // - // As noted in the discussion when - // declaring the local integration - // functions in the class - // declaration, the arguments - // expected by the assembling - // integrator class are not - // actually function - // pointers. Rather, they are - // objects that can be called like - // functions with a certain number - // of arguments. Consequently, we - // could also pass objects with - // appropriate operator() - // implementations here, or the - // result of std::bind if the local - // integrators were, for example, - // non-static member functions. + // Finally, the integration loop + // over all active cells + // (determined by the first + // argument, which is an active + // iterator). + // + // As noted in the discussion when + // declaring the local integration + // functions in the class + // declaration, the arguments + // expected by the assembling + // integrator class are not + // actually function + // pointers. Rather, they are + // objects that can be called like + // functions with a certain number + // of arguments. Consequently, we + // could also pass objects with + // appropriate operator() + // implementations here, or the + // result of std::bind if the local + // integrators were, for example, + // non-static member functions. MeshWorker::integration_loop - (dof_handler.begin_active(), dof_handler.end(), - dof_info, info_box, - &AdvectionProblem::integrate_cell_term, - &AdvectionProblem::integrate_boundary_term, - &AdvectionProblem::integrate_face_term, - assembler, true); + (dof_handler.begin_active(), dof_handler.end(), + dof_info, info_box, + &AdvectionProblem::integrate_cell_term, + &AdvectionProblem::integrate_boundary_term, + &AdvectionProblem::integrate_face_term, + assembler, true); } - // @sect4{The local integrators} + // @sect4{The local integrators} - // These are the functions given to - // the MeshWorker::integration_loop() - // called just above. They compute - // the local contributions to the - // system matrix and right hand side - // on cells and faces. + // These are the functions given to + // the MeshWorker::integration_loop() + // called just above. They compute + // the local contributions to the + // system matrix and right hand side + // on cells and faces. template - void AdvectionProblem::integrate_cell_term (DoFInfo& dinfo, - CellInfo& info) + void AdvectionProblem::integrate_cell_term (DoFInfo &dinfo, + CellInfo &info) { - // First, let us retrieve some of - // the objects used here from - // @p info. Note that these objects - // can handle much more complex - // structures, thus the access here - // looks more complicated than - // might seem necessary. - const FEValuesBase& fe_v = info.fe_values(); - FullMatrix& local_matrix = dinfo.matrix(0).matrix; + // First, let us retrieve some of + // the objects used here from + // @p info. Note that these objects + // can handle much more complex + // structures, thus the access here + // looks more complicated than + // might seem necessary. + const FEValuesBase &fe_v = info.fe_values(); + FullMatrix &local_matrix = dinfo.matrix(0).matrix; const std::vector &JxW = fe_v.get_JxW_values (); - // With these objects, we continue - // local integration like - // always. First, we loop over the - // quadrature points and compute - // the advection vector in the - // current point. + // With these objects, we continue + // local integration like + // always. First, we loop over the + // quadrature points and compute + // the advection vector in the + // current point. for (unsigned int point=0; point beta; @@ -462,12 +462,12 @@ namespace Step12 beta(1) = fe_v.quadrature_point(point)(0); beta /= beta.norm(); - // We solve a homogeneous - // equation, thus no right - // hand side shows up in - // the cell term. - // What's left is - // integrating the matrix entries. + // We solve a homogeneous + // equation, thus no right + // hand side shows up in + // the cell term. + // What's left is + // integrating the matrix entries. for (unsigned int i=0; i - void AdvectionProblem::integrate_boundary_term (DoFInfo& dinfo, - CellInfo& info) + void AdvectionProblem::integrate_boundary_term (DoFInfo &dinfo, + CellInfo &info) { - const FEValuesBase& fe_v = info.fe_values(); - FullMatrix& local_matrix = dinfo.matrix(0).matrix; - Vector& local_vector = dinfo.vector(0).block(0); + const FEValuesBase &fe_v = info.fe_values(); + FullMatrix &local_matrix = dinfo.matrix(0).matrix; + Vector &local_vector = dinfo.vector(0).block(0); const std::vector &JxW = fe_v.get_JxW_values (); const std::vector > &normals = fe_v.get_normal_vectors (); @@ -521,55 +521,55 @@ namespace Step12 } } - // Finally, the interior face - // terms. The difference here is that - // we receive two info objects, one - // for each cell adjacent to the face - // and we assemble four matrices, one - // for each cell and two for coupling - // back and forth. + // Finally, the interior face + // terms. The difference here is that + // we receive two info objects, one + // for each cell adjacent to the face + // and we assemble four matrices, one + // for each cell and two for coupling + // back and forth. template - void AdvectionProblem::integrate_face_term (DoFInfo& dinfo1, - DoFInfo& dinfo2, - CellInfo& info1, - CellInfo& info2) + void AdvectionProblem::integrate_face_term (DoFInfo &dinfo1, + DoFInfo &dinfo2, + CellInfo &info1, + CellInfo &info2) { - // For quadrature points, weights, - // etc., we use the - // FEValuesBase object of the - // first argument. - const FEValuesBase& fe_v = info1.fe_values(); - - // For additional shape functions, - // we have to ask the neighbors - // FEValuesBase. - const FEValuesBase& fe_v_neighbor = info2.fe_values(); - - // Then we get references to the - // four local matrices. The letters - // u and v refer to trial and test - // functions, respectively. The - // %numbers indicate the cells - // provided by info1 and info2. By - // convention, the two matrices in - // each info object refer to the - // test functions on the respective - // cell. The first matrix contains the - // interior couplings of that cell, - // while the second contains the - // couplings between cells. - FullMatrix& u1_v1_matrix = dinfo1.matrix(0,false).matrix; - FullMatrix& u2_v1_matrix = dinfo1.matrix(0,true).matrix; - FullMatrix& u1_v2_matrix = dinfo2.matrix(0,true).matrix; - FullMatrix& u2_v2_matrix = dinfo2.matrix(0,false).matrix; - - // Here, following the previous - // functions, we would have the - // local right hand side - // vectors. Fortunately, the - // interface terms only involve the - // solution and the right hand side - // does not receive any contributions. + // For quadrature points, weights, + // etc., we use the + // FEValuesBase object of the + // first argument. + const FEValuesBase &fe_v = info1.fe_values(); + + // For additional shape functions, + // we have to ask the neighbors + // FEValuesBase. + const FEValuesBase &fe_v_neighbor = info2.fe_values(); + + // Then we get references to the + // four local matrices. The letters + // u and v refer to trial and test + // functions, respectively. The + // %numbers indicate the cells + // provided by info1 and info2. By + // convention, the two matrices in + // each info object refer to the + // test functions on the respective + // cell. The first matrix contains the + // interior couplings of that cell, + // while the second contains the + // couplings between cells. + FullMatrix &u1_v1_matrix = dinfo1.matrix(0,false).matrix; + FullMatrix &u2_v1_matrix = dinfo1.matrix(0,true).matrix; + FullMatrix &u1_v2_matrix = dinfo2.matrix(0,true).matrix; + FullMatrix &u2_v2_matrix = dinfo2.matrix(0,false).matrix; + + // Here, following the previous + // functions, we would have the + // local right hand side + // vectors. Fortunately, the + // interface terms only involve the + // solution and the right hand side + // does not receive any contributions. const std::vector &JxW = fe_v.get_JxW_values (); const std::vector > &normals = fe_v.get_normal_vectors (); @@ -584,8 +584,8 @@ namespace Step12 const double beta_n=beta * normals[point]; if (beta_n>0) { - // This term we've already - // seen: + // This term we've already + // seen: for (unsigned int i=0; i void AdvectionProblem::solve (Vector &solution) { SolverControl solver_control (1000, 1e-12); SolverRichardson<> solver (solver_control); - // Here we create the - // preconditioner, + // Here we create the + // preconditioner, PreconditionBlockSSOR > preconditioner; - // then assign the matrix to it and - // set the right block size: + // then assign the matrix to it and + // set the right block size: preconditioner.initialize(system_matrix, fe.dofs_per_cell); - // After these preparations we are - // ready to start the linear solver. + // After these preparations we are + // ready to start the linear solver. solver.solve (system_matrix, solution, right_hand_side, preconditioner); } - // We refine the grid according to a - // very simple refinement criterion, - // namely an approximation to the - // gradient of the solution. As here - // we consider the DG(1) method - // (i.e. we use piecewise bilinear - // shape functions) we could simply - // compute the gradients on each - // cell. But we do not want to base - // our refinement indicator on the - // gradients on each cell only, but - // want to base them also on jumps of - // the discontinuous solution - // function over faces between - // neighboring cells. The simplest - // way of doing that is to compute - // approximative gradients by - // difference quotients including the - // cell under consideration and its - // neighbors. This is done by the - // DerivativeApproximation class - // that computes the approximate - // gradients in a way similar to the - // GradientEstimation described - // in step-9 of this tutorial. In - // fact, the - // DerivativeApproximation class - // was developed following the - // GradientEstimation class of - // step-9. Relating to the - // discussion in step-9, here we - // consider $h^{1+d/2}|\nabla_h - // u_h|$. Furthermore we note that we - // do not consider approximate second - // derivatives because solutions to - // the linear advection equation are - // in general not in $H^2$ but in $H^1$ - // (to be more precise, in $H^1_\beta$) - // only. + // We refine the grid according to a + // very simple refinement criterion, + // namely an approximation to the + // gradient of the solution. As here + // we consider the DG(1) method + // (i.e. we use piecewise bilinear + // shape functions) we could simply + // compute the gradients on each + // cell. But we do not want to base + // our refinement indicator on the + // gradients on each cell only, but + // want to base them also on jumps of + // the discontinuous solution + // function over faces between + // neighboring cells. The simplest + // way of doing that is to compute + // approximative gradients by + // difference quotients including the + // cell under consideration and its + // neighbors. This is done by the + // DerivativeApproximation class + // that computes the approximate + // gradients in a way similar to the + // GradientEstimation described + // in step-9 of this tutorial. In + // fact, the + // DerivativeApproximation class + // was developed following the + // GradientEstimation class of + // step-9. Relating to the + // discussion in step-9, here we + // consider $h^{1+d/2}|\nabla_h + // u_h|$. Furthermore we note that we + // do not consider approximate second + // derivatives because solutions to + // the linear advection equation are + // in general not in $H^2$ but in $H^1$ + // (to be more precise, in $H^1_\beta$) + // only. template void AdvectionProblem::refine_grid () { - // The DerivativeApproximation - // class computes the gradients to - // float precision. This is - // sufficient as they are - // approximate and serve as - // refinement indicators only. + // The DerivativeApproximation + // class computes the gradients to + // float precision. This is + // sufficient as they are + // approximate and serve as + // refinement indicators only. Vector gradient_indicator (triangulation.n_active_cells()); - // Now the approximate gradients - // are computed + // Now the approximate gradients + // are computed DerivativeApproximation::approximate_gradient (mapping, dof_handler, solution, gradient_indicator); - // and they are cell-wise scaled by - // the factor $h^{1+d/2}$ + // and they are cell-wise scaled by + // the factor $h^{1+d/2}$ typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no) gradient_indicator(cell_no)*=std::pow(cell->diameter(), 1+1.0*dim/2); - // Finally they serve as refinement - // indicator. + // Finally they serve as refinement + // indicator. GridRefinement::refine_and_coarsen_fixed_number (triangulation, gradient_indicator, 0.3, 0.1); @@ -745,17 +745,17 @@ namespace Step12 } - // The output of this program - // consists of eps-files of the - // adaptively refined grids and the - // numerical solutions given in - // gnuplot format. This was covered - // in previous examples and will not - // be further commented on. + // The output of this program + // consists of eps-files of the + // adaptively refined grids and the + // numerical solutions given in + // gnuplot format. This was covered + // in previous examples and will not + // be further commented on. template void AdvectionProblem::output_results (const unsigned int cycle) const { - // Write the grid in eps format. + // Write the grid in eps format. std::string filename = "grid-"; filename += ('0' + cycle); Assert (cycle < 10, ExcInternalError()); @@ -767,8 +767,8 @@ namespace Step12 GridOut grid_out; grid_out.write_eps (triangulation, eps_output); - // Output of the solution in - // gnuplot format. + // Output of the solution in + // gnuplot format. filename = "sol-"; filename += ('0' + cycle); Assert (cycle < 10, ExcInternalError()); @@ -787,8 +787,8 @@ namespace Step12 } - // The following run function is - // similar to previous examples. + // The following run function is + // similar to previous examples. template void AdvectionProblem::run () { @@ -825,9 +825,9 @@ namespace Step12 } - // The following main function is - // similar to previous examples as well, and - // need not be commented on. +// The following main function is +// similar to previous examples as well, and +// need not be commented on. int main () { try diff --git a/deal.II/examples/step-13/step-13.cc b/deal.II/examples/step-13/step-13.cc index e8182e5bd0..63e62cc64b 100644 --- a/deal.II/examples/step-13/step-13.cc +++ b/deal.II/examples/step-13/step-13.cc @@ -10,15 +10,15 @@ /* further information on this license. */ - // As in all programs, we start with - // a list of include files from the - // library, and as usual they are in - // the standard order which is - // base -- lac -- grid -- - // dofs -- fe -- numerics - // (as each of these categories - // roughly builds upon previous - // ones), then C++ standard headers: +// As in all programs, we start with +// a list of include files from the +// library, and as usual they are in +// the standard order which is +// base -- lac -- grid -- +// dofs -- fe -- numerics +// (as each of these categories +// roughly builds upon previous +// ones), then C++ standard headers: #include #include #include @@ -45,145 +45,145 @@ #include #include - // Now for the C++ standard headers: +// Now for the C++ standard headers: #include #include #include #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step13 { using namespace dealii; - // @sect3{Evaluation of the solution} - - // As for the program itself, we - // first define classes that evaluate - // the solutions of a Laplace - // equation. In fact, they can - // evaluate every kind of solution, - // as long as it is described by a - // DoFHandler object, and a - // solution vector. We define them - // here first, even before the - // classes that actually generate the - // solution to be evaluated, since we - // need to declare an abstract base - // class that the solver classes can - // refer to. - // - // From an abstract point of view, we - // declare a pure base class - // that provides an evaluation - // operator() which will - // do the evaluation of the solution - // (whatever derived classes might - // consider an evaluation). Since - // this is the only real function of - // this base class (except for some - // bookkeeping machinery), one - // usually terms such a class that - // only has an operator() a - // functor in C++ terminology, - // since it is used just like a - // function object. - // - // Objects of this functor type will - // then later be passed to the solver - // object, which applies it to the - // solution just computed. The - // evaluation objects may then - // extract any quantity they like - // from the solution. The advantage - // of putting these evaluation - // functions into a separate - // hierarchy of classes is that by - // design they cannot use the - // internals of the solver object and - // are therefore independent of - // changes to the way the solver - // works. Furthermore, it is trivial - // to write another evaluation class - // without modifying the solver - // class, which speeds up programming - // (not being able to use internals - // of another class also means that - // you do not have to worry about - // them -- programming evaluators is - // usually a rather quickly done - // task), as well as compilation (if - // solver and evaluation classes are - // put into different files: the - // solver only needs to see the - // declaration of the abstract base - // class, and therefore does not need - // to be recompiled upon addition of - // a new evaluation class, or - // modification of an old one). - // On a related note, you can reuse - // the evaluation classes for other - // projects, solving different - // equations. - // - // In order to improve separation of - // code into different modules, we - // put the evaluation classes into a - // namespace of their own. This makes - // it easier to actually solve - // different equations in the same - // program, by assembling it from - // existing building blocks. The - // reason for this is that classes - // for similar purposes tend to have - // the same name, although they were - // developed in different - // contexts. In order to be able to - // use them together in one program, - // it is necessary that they are - // placed in different - // namespaces. This we do here: + // @sect3{Evaluation of the solution} + + // As for the program itself, we + // first define classes that evaluate + // the solutions of a Laplace + // equation. In fact, they can + // evaluate every kind of solution, + // as long as it is described by a + // DoFHandler object, and a + // solution vector. We define them + // here first, even before the + // classes that actually generate the + // solution to be evaluated, since we + // need to declare an abstract base + // class that the solver classes can + // refer to. + // + // From an abstract point of view, we + // declare a pure base class + // that provides an evaluation + // operator() which will + // do the evaluation of the solution + // (whatever derived classes might + // consider an evaluation). Since + // this is the only real function of + // this base class (except for some + // bookkeeping machinery), one + // usually terms such a class that + // only has an operator() a + // functor in C++ terminology, + // since it is used just like a + // function object. + // + // Objects of this functor type will + // then later be passed to the solver + // object, which applies it to the + // solution just computed. The + // evaluation objects may then + // extract any quantity they like + // from the solution. The advantage + // of putting these evaluation + // functions into a separate + // hierarchy of classes is that by + // design they cannot use the + // internals of the solver object and + // are therefore independent of + // changes to the way the solver + // works. Furthermore, it is trivial + // to write another evaluation class + // without modifying the solver + // class, which speeds up programming + // (not being able to use internals + // of another class also means that + // you do not have to worry about + // them -- programming evaluators is + // usually a rather quickly done + // task), as well as compilation (if + // solver and evaluation classes are + // put into different files: the + // solver only needs to see the + // declaration of the abstract base + // class, and therefore does not need + // to be recompiled upon addition of + // a new evaluation class, or + // modification of an old one). + // On a related note, you can reuse + // the evaluation classes for other + // projects, solving different + // equations. + // + // In order to improve separation of + // code into different modules, we + // put the evaluation classes into a + // namespace of their own. This makes + // it easier to actually solve + // different equations in the same + // program, by assembling it from + // existing building blocks. The + // reason for this is that classes + // for similar purposes tend to have + // the same name, although they were + // developed in different + // contexts. In order to be able to + // use them together in one program, + // it is necessary that they are + // placed in different + // namespaces. This we do here: namespace Evaluation { - // Now for the abstract base class - // of evaluation classes: its main - // purpose is to declare a pure - // virtual function operator() - // taking a DoFHandler object, - // and the solution vector. In - // order to be able to use pointers - // to this base class only, it also - // has to declare a virtual - // destructor, which however does - // nothing. Besides this, it only - // provides for a little bit of - // bookkeeping: since we usually - // want to evaluate solutions on - // subsequent refinement levels, we - // store the number of the present - // refinement cycle, and provide a - // function to change this number. + // Now for the abstract base class + // of evaluation classes: its main + // purpose is to declare a pure + // virtual function operator() + // taking a DoFHandler object, + // and the solution vector. In + // order to be able to use pointers + // to this base class only, it also + // has to declare a virtual + // destructor, which however does + // nothing. Besides this, it only + // provides for a little bit of + // bookkeeping: since we usually + // want to evaluate solutions on + // subsequent refinement levels, we + // store the number of the present + // refinement cycle, and provide a + // function to change this number. template class EvaluationBase { - public: - virtual ~EvaluationBase (); + public: + virtual ~EvaluationBase (); - void set_refinement_cycle (const unsigned int refinement_cycle); + void set_refinement_cycle (const unsigned int refinement_cycle); - virtual void operator () (const DoFHandler &dof_handler, - const Vector &solution) const = 0; - protected: - unsigned int refinement_cycle; + virtual void operator () (const DoFHandler &dof_handler, + const Vector &solution) const = 0; + protected: + unsigned int refinement_cycle; }; - // After the declaration has been - // discussed above, the - // implementation is rather - // straightforward: + // After the declaration has been + // discussed above, the + // implementation is rather + // straightforward: template EvaluationBase::~EvaluationBase () {} @@ -198,130 +198,130 @@ namespace Step13 } - // @sect4{%Point evaluation} - - // The next thing is to implement - // actual evaluation classes. As - // noted in the introduction, we'd - // like to extract a point value - // from the solution, so the first - // class does this in its - // operator(). The actual point - // is given to this class through - // the constructor, as well as a - // table object into which it will - // put its findings. - // - // Finding out the value of a - // finite element field at an - // arbitrary point is rather - // difficult, if we cannot rely on - // knowing the actual finite - // element used, since then we - // cannot, for example, interpolate - // between nodes. For simplicity, - // we therefore assume here that - // the point at which we want to - // evaluate the field is actually a - // node. If, in the process of - // evaluating the solution, we find - // that we did not encounter this - // point upon looping over all - // vertices, we then have to throw - // an exception in order to signal - // to the calling functions that - // something has gone wrong, rather - // than silently ignore this error. - // - // In the step-9 example program, - // we have already seen how such an - // exception class can be declared, - // using the DeclExceptionN - // macros. We use this mechanism - // here again. - // - // From this, the actual - // declaration of this class should - // be evident. Note that of course - // even if we do not list a - // destructor explicitely, an - // implicit destructor is generated - // from the compiler, and it is - // virtual just as the one of the - // base class. + // @sect4{%Point evaluation} + + // The next thing is to implement + // actual evaluation classes. As + // noted in the introduction, we'd + // like to extract a point value + // from the solution, so the first + // class does this in its + // operator(). The actual point + // is given to this class through + // the constructor, as well as a + // table object into which it will + // put its findings. + // + // Finding out the value of a + // finite element field at an + // arbitrary point is rather + // difficult, if we cannot rely on + // knowing the actual finite + // element used, since then we + // cannot, for example, interpolate + // between nodes. For simplicity, + // we therefore assume here that + // the point at which we want to + // evaluate the field is actually a + // node. If, in the process of + // evaluating the solution, we find + // that we did not encounter this + // point upon looping over all + // vertices, we then have to throw + // an exception in order to signal + // to the calling functions that + // something has gone wrong, rather + // than silently ignore this error. + // + // In the step-9 example program, + // we have already seen how such an + // exception class can be declared, + // using the DeclExceptionN + // macros. We use this mechanism + // here again. + // + // From this, the actual + // declaration of this class should + // be evident. Note that of course + // even if we do not list a + // destructor explicitely, an + // implicit destructor is generated + // from the compiler, and it is + // virtual just as the one of the + // base class. template class PointValueEvaluation : public EvaluationBase { - public: - PointValueEvaluation (const Point &evaluation_point, - TableHandler &results_table); - - virtual void operator () (const DoFHandler &dof_handler, - const Vector &solution) const; - - DeclException1 (ExcEvaluationPointNotFound, - Point, - << "The evaluation point " << arg1 - << " was not found among the vertices of the present grid."); - private: - const Point evaluation_point; - TableHandler &results_table; + public: + PointValueEvaluation (const Point &evaluation_point, + TableHandler &results_table); + + virtual void operator () (const DoFHandler &dof_handler, + const Vector &solution) const; + + DeclException1 (ExcEvaluationPointNotFound, + Point, + << "The evaluation point " << arg1 + << " was not found among the vertices of the present grid."); + private: + const Point evaluation_point; + TableHandler &results_table; }; - // As for the definition, the - // constructor is trivial, just - // taking data and storing it in - // object-local ones: + // As for the definition, the + // constructor is trivial, just + // taking data and storing it in + // object-local ones: template PointValueEvaluation:: PointValueEvaluation (const Point &evaluation_point, TableHandler &results_table) - : - evaluation_point (evaluation_point), - results_table (results_table) + : + evaluation_point (evaluation_point), + results_table (results_table) {} - // Now for the function that is - // mainly of interest in this - // class, the computation of the - // point value: + // Now for the function that is + // mainly of interest in this + // class, the computation of the + // point value: template void PointValueEvaluation:: operator () (const DoFHandler &dof_handler, const Vector &solution) const { - // First allocate a variable that - // will hold the point - // value. Initialize it with a - // value that is clearly bogus, - // so that if we fail to set it - // to a reasonable value, we will - // note at once. This may not be - // necessary in a function as - // small as this one, since we - // can easily see all possible - // paths of execution here, but - // it proved to be helpful for - // more complex cases, and so we - // employ this strategy here as - // well. + // First allocate a variable that + // will hold the point + // value. Initialize it with a + // value that is clearly bogus, + // so that if we fail to set it + // to a reasonable value, we will + // note at once. This may not be + // necessary in a function as + // small as this one, since we + // can easily see all possible + // paths of execution here, but + // it proved to be helpful for + // more complex cases, and so we + // employ this strategy here as + // well. double point_value = 1e20; - // Then loop over all cells and - // all their vertices, and check - // whether a vertex matches the - // evaluation point. If this is - // the case, then extract the - // point value, set a flag that - // we have found the point of - // interest, and exit the loop. + // Then loop over all cells and + // all their vertices, and check + // whether a vertex matches the + // evaluation point. If this is + // the case, then extract the + // point value, set a flag that + // we have found the point of + // interest, and exit the loop. typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); bool evaluation_point_found = false; for (; (cell!=endc) && !evaluation_point_found; ++cell) for (unsigned int vertex=0; @@ -329,172 +329,172 @@ namespace Step13 ++vertex) if (cell->vertex(vertex) == evaluation_point) { - // In order to extract - // the point value from - // the global solution - // vector, pick that - // component that belongs - // to the vertex of - // interest, and, in case - // the solution is - // vector-valued, take - // the first component of - // it: + // In order to extract + // the point value from + // the global solution + // vector, pick that + // component that belongs + // to the vertex of + // interest, and, in case + // the solution is + // vector-valued, take + // the first component of + // it: point_value = solution(cell->vertex_dof_index(vertex,0)); - // Note that by this we - // have made an - // assumption that is not - // valid always and - // should be documented - // in the class - // declaration if this - // were code for a real - // application rather - // than a tutorial - // program: we assume - // that the finite - // element used for the - // solution we try to - // evaluate actually has - // degrees of freedom - // associated with - // vertices. This, for - // example, does not hold - // for discontinuous - // elements, were the - // support points for the - // shape functions - // happen to be located - // at the vertices, but - // are not associated - // with the vertices but - // rather with the cell - // interior, since - // association with - // vertices would imply - // continuity there. It - // would also not hold - // for edge oriented - // elements, and the - // like. - // - // Ideally, we would - // check this at the - // beginning of the - // function, for example - // by a statement like - // Assert - // (dof_handler.get_fe().dofs_per_vertex - // @> 0, - // ExcNotImplemented()), - // which should make it - // quite clear what is - // going wrong when the - // exception is - // triggered. In this - // case, we omit it - // (which is indeed bad - // style), but knowing - // that that does not - // hurt here, since the - // statement - // cell-@>vertex_dof_index(vertex,0) - // would fail if we asked - // it to give us the DoF - // index of a vertex if - // there were none. - // - // We stress again that - // this restriction on - // the allowed finite - // elements should be - // stated in the class - // documentation. - - // Since we found the - // right point, we now - // set the respective - // flag and exit the - // innermost loop. The - // outer loop will the - // also be terminated due - // to the set flag. + // Note that by this we + // have made an + // assumption that is not + // valid always and + // should be documented + // in the class + // declaration if this + // were code for a real + // application rather + // than a tutorial + // program: we assume + // that the finite + // element used for the + // solution we try to + // evaluate actually has + // degrees of freedom + // associated with + // vertices. This, for + // example, does not hold + // for discontinuous + // elements, were the + // support points for the + // shape functions + // happen to be located + // at the vertices, but + // are not associated + // with the vertices but + // rather with the cell + // interior, since + // association with + // vertices would imply + // continuity there. It + // would also not hold + // for edge oriented + // elements, and the + // like. + // + // Ideally, we would + // check this at the + // beginning of the + // function, for example + // by a statement like + // Assert + // (dof_handler.get_fe().dofs_per_vertex + // @> 0, + // ExcNotImplemented()), + // which should make it + // quite clear what is + // going wrong when the + // exception is + // triggered. In this + // case, we omit it + // (which is indeed bad + // style), but knowing + // that that does not + // hurt here, since the + // statement + // cell-@>vertex_dof_index(vertex,0) + // would fail if we asked + // it to give us the DoF + // index of a vertex if + // there were none. + // + // We stress again that + // this restriction on + // the allowed finite + // elements should be + // stated in the class + // documentation. + + // Since we found the + // right point, we now + // set the respective + // flag and exit the + // innermost loop. The + // outer loop will the + // also be terminated due + // to the set flag. evaluation_point_found = true; break; }; - // Finally, we'd like to make - // sure that we have indeed found - // the evaluation point, since if - // that were not so we could not - // give a reasonable value of the - // solution there and the rest of - // the computations were useless - // anyway. So make sure through - // the AssertThrow macro - // already used in the step-9 - // program that we have indeed - // found this point. If this is - // not so, the macro throws an - // exception of the type that is - // given to it as second - // argument, but compared to a - // straightforward throw - // statement, it fills the - // exception object with a set of - // additional information, for - // example the source file and - // line number where the - // exception was generated, and - // the condition that failed. If - // you have a catch clause in - // your main function (as this - // program has), you will catch - // all exceptions that are not - // caught somewhere in between - // and thus already handled, and - // this additional information - // will help you find out what - // happened and where it went - // wrong. + // Finally, we'd like to make + // sure that we have indeed found + // the evaluation point, since if + // that were not so we could not + // give a reasonable value of the + // solution there and the rest of + // the computations were useless + // anyway. So make sure through + // the AssertThrow macro + // already used in the step-9 + // program that we have indeed + // found this point. If this is + // not so, the macro throws an + // exception of the type that is + // given to it as second + // argument, but compared to a + // straightforward throw + // statement, it fills the + // exception object with a set of + // additional information, for + // example the source file and + // line number where the + // exception was generated, and + // the condition that failed. If + // you have a catch clause in + // your main function (as this + // program has), you will catch + // all exceptions that are not + // caught somewhere in between + // and thus already handled, and + // this additional information + // will help you find out what + // happened and where it went + // wrong. AssertThrow (evaluation_point_found, ExcEvaluationPointNotFound(evaluation_point)); - // Note that we have used the - // Assert macro in other - // example programs as well. It - // differed from the - // AssertThrow macro used - // here in that it simply aborts - // the program, rather than - // throwing an exception, and - // that it did so only in debug - // mode. It was the right macro - // to use to check about the size - // of vectors passed as arguments - // to functions, and the like. - // - // However, here the situation is - // different: whether we find the - // evaluation point or not may - // change from refinement to - // refinement (for example, if - // the four cells around point - // are coarsened away, then the - // point may vanish after - // refinement and - // coarsening). This is something - // that cannot be predicted from - // a few number of runs of the - // program in debug mode, but - // should be checked always, also - // in production runs. Thus the - // use of the AssertThrow - // macro here. - - // Now, if we are sure that we - // have found the evaluation - // point, we can add the results - // into the table of results: + // Note that we have used the + // Assert macro in other + // example programs as well. It + // differed from the + // AssertThrow macro used + // here in that it simply aborts + // the program, rather than + // throwing an exception, and + // that it did so only in debug + // mode. It was the right macro + // to use to check about the size + // of vectors passed as arguments + // to functions, and the like. + // + // However, here the situation is + // different: whether we find the + // evaluation point or not may + // change from refinement to + // refinement (for example, if + // the four cells around point + // are coarsened away, then the + // point may vanish after + // refinement and + // coarsening). This is something + // that cannot be predicted from + // a few number of runs of the + // program in debug mode, but + // should be checked always, also + // in production runs. Thus the + // use of the AssertThrow + // macro here. + + // Now, if we are sure that we + // have found the evaluation + // point, we can add the results + // into the table of results: results_table.add_value ("DoFs", dof_handler.n_dofs()); results_table.add_value ("u(x_0)", point_value); } @@ -502,123 +502,123 @@ namespace Step13 - // @sect4{Generating output} - - // A different, maybe slightly odd - // kind of evaluation of a - // solution is to output it to a - // file in a graphical - // format. Since in the evaluation - // functions we are given a - // DoFHandler object and the - // solution vector, we have all we - // need to do this, so we can do it - // in an evaluation class. The - // reason for actually doing so - // instead of putting it into the - // class that computed the solution - // is that this way we have more - // flexibility: if we choose to - // only output certain aspects of - // it, or not output it at all. In - // any case, we do not need to - // modify the solver class, we just - // have to modify one of the - // modules out of which we build - // this program. This form of - // encapsulation, as above, helps - // us to keep each part of the - // program rather simple as the - // interfaces are kept simple, and - // no access to hidden data is - // possible. - // - // Since this class which generates - // the output is derived from the - // common EvaluationBase base - // class, its main interface is the - // operator() - // function. Furthermore, it has a - // constructor taking a string that - // will be used as the base part of - // the file name to which output - // will be sent (we will augment it - // by a number indicating the - // number of the refinement cycle - // -- the base class has this - // information at hand --, and a - // suffix), and the constructor - // also takes a value that - // indicates which format is - // requested, i.e. for which - // graphics program we shall - // generate output (from this we - // will then also generate the - // suffix of the filename to which - // we write). - // - // Regarding the output format, the - // DataOutInterface class - // (which is a base class of - // DataOut through which we - // will access its fields) provides - // an enumeration field - // OutputFormat, which lists - // names for all supported output - // formats. At the time of writing - // of this program, the supported - // graphics formats are represented - // by the enum values ucd, - // gnuplot, povray, - // eps, gmv, tecplot, - // tecplot_binary, dx, and - // vtk, but this list will - // certainly grow over time. Now, - // within various functions of that - // base class, you can use values - // of this type to get information - // about these graphics formats - // (for example the default suffix - // used for files of each format), - // and you can call a generic - // write function, which then - // branches to the - // write_gnuplot, - // write_ucd, etc functions - // which we have used in previous - // examples already, based on the - // value of a second argument given - // to it denoting the required - // output format. This mechanism - // makes it simple to write an - // extensible program that can - // decide which output format to - // use at runtime, and it also - // makes it rather simple to write - // the program in a way such that - // it takes advantage of newly - // implemented output formats, - // without the need to change the - // application program. - // - // Of these two fields, the base - // name and the output format - // descriptor, the constructor - // takes values and stores them for - // later use by the actual - // evaluation function. + // @sect4{Generating output} + + // A different, maybe slightly odd + // kind of evaluation of a + // solution is to output it to a + // file in a graphical + // format. Since in the evaluation + // functions we are given a + // DoFHandler object and the + // solution vector, we have all we + // need to do this, so we can do it + // in an evaluation class. The + // reason for actually doing so + // instead of putting it into the + // class that computed the solution + // is that this way we have more + // flexibility: if we choose to + // only output certain aspects of + // it, or not output it at all. In + // any case, we do not need to + // modify the solver class, we just + // have to modify one of the + // modules out of which we build + // this program. This form of + // encapsulation, as above, helps + // us to keep each part of the + // program rather simple as the + // interfaces are kept simple, and + // no access to hidden data is + // possible. + // + // Since this class which generates + // the output is derived from the + // common EvaluationBase base + // class, its main interface is the + // operator() + // function. Furthermore, it has a + // constructor taking a string that + // will be used as the base part of + // the file name to which output + // will be sent (we will augment it + // by a number indicating the + // number of the refinement cycle + // -- the base class has this + // information at hand --, and a + // suffix), and the constructor + // also takes a value that + // indicates which format is + // requested, i.e. for which + // graphics program we shall + // generate output (from this we + // will then also generate the + // suffix of the filename to which + // we write). + // + // Regarding the output format, the + // DataOutInterface class + // (which is a base class of + // DataOut through which we + // will access its fields) provides + // an enumeration field + // OutputFormat, which lists + // names for all supported output + // formats. At the time of writing + // of this program, the supported + // graphics formats are represented + // by the enum values ucd, + // gnuplot, povray, + // eps, gmv, tecplot, + // tecplot_binary, dx, and + // vtk, but this list will + // certainly grow over time. Now, + // within various functions of that + // base class, you can use values + // of this type to get information + // about these graphics formats + // (for example the default suffix + // used for files of each format), + // and you can call a generic + // write function, which then + // branches to the + // write_gnuplot, + // write_ucd, etc functions + // which we have used in previous + // examples already, based on the + // value of a second argument given + // to it denoting the required + // output format. This mechanism + // makes it simple to write an + // extensible program that can + // decide which output format to + // use at runtime, and it also + // makes it rather simple to write + // the program in a way such that + // it takes advantage of newly + // implemented output formats, + // without the need to change the + // application program. + // + // Of these two fields, the base + // name and the output format + // descriptor, the constructor + // takes values and stores them for + // later use by the actual + // evaluation function. template class SolutionOutput : public EvaluationBase { - public: - SolutionOutput (const std::string &output_name_base, - const typename DataOut::OutputFormat output_format); - - virtual void operator () (const DoFHandler &dof_handler, - const Vector &solution) const; - private: - const std::string output_name_base; - const typename DataOut::OutputFormat output_format; + public: + SolutionOutput (const std::string &output_name_base, + const typename DataOut::OutputFormat output_format); + + virtual void operator () (const DoFHandler &dof_handler, + const Vector &solution) const; + private: + const std::string output_name_base; + const typename DataOut::OutputFormat output_format; }; @@ -626,43 +626,43 @@ namespace Step13 SolutionOutput:: SolutionOutput (const std::string &output_name_base, const typename DataOut::OutputFormat output_format) - : - output_name_base (output_name_base), - output_format (output_format) + : + output_name_base (output_name_base), + output_format (output_format) {} - // After the description above, the - // function generating the actual - // output is now relatively - // straightforward. The only - // particularly interesting feature - // over previous example programs - // is the use of the - // DataOut::default_suffix - // function, returning the usual - // suffix for files of a given - // format (e.g. ".eps" for - // encapsulated postscript files, - // ".gnuplot" for Gnuplot files), - // and of the generic - // DataOut::write function with - // a second argument, which - // branches to the actual output - // functions for the different - // graphics formats, based on the - // value of the format descriptor - // passed as second argument. - // - // Also note that we have to prefix - // this-@> to access a member - // variable of the template - // dependent base class. The reason - // here, and further down in the - // program is the same as the one - // described in the step-7 example - // program (look for two-stage - // name lookup there). + // After the description above, the + // function generating the actual + // output is now relatively + // straightforward. The only + // particularly interesting feature + // over previous example programs + // is the use of the + // DataOut::default_suffix + // function, returning the usual + // suffix for files of a given + // format (e.g. ".eps" for + // encapsulated postscript files, + // ".gnuplot" for Gnuplot files), + // and of the generic + // DataOut::write function with + // a second argument, which + // branches to the actual output + // functions for the different + // graphics formats, based on the + // value of the format descriptor + // passed as second argument. + // + // Also note that we have to prefix + // this-@> to access a member + // variable of the template + // dependent base class. The reason + // here, and further down in the + // program is the same as the one + // described in the step-7 example + // program (look for two-stage + // name lookup there). template void SolutionOutput::operator () (const DoFHandler &dof_handler, @@ -685,184 +685,184 @@ namespace Step13 - // @sect4{Other evaluations} + // @sect4{Other evaluations} - // In practical applications, one - // would add here a list of other - // possible evaluation classes, - // representing quantities that one - // may be interested in. For this - // example, that much shall be - // sufficient, so we close the - // namespace. + // In practical applications, one + // would add here a list of other + // possible evaluation classes, + // representing quantities that one + // may be interested in. For this + // example, that much shall be + // sufficient, so we close the + // namespace. } - // @sect3{The Laplace solver classes} - - // After defining what we want to - // know of the solution, we should - // now care how to get at it. We will - // pack everything we need into a - // namespace of its own, for much the - // same reasons as for the - // evaluations above. - // - // Since we have discussed Laplace - // solvers already in considerable - // detail in previous examples, there - // is not much new stuff - // following. Rather, we have to a - // great extent cannibalized previous - // examples and put them, in slightly - // different form, into this example - // program. We will therefore mostly - // be concerned with discussing the - // differences to previous examples. - // - // Basically, as already said in the - // introduction, the lack of new - // stuff in this example is - // deliberate, as it is more to - // demonstrate software design - // practices, rather than - // mathematics. The emphasis in - // explanations below will therefore - // be more on the actual - // implementation. + // @sect3{The Laplace solver classes} + + // After defining what we want to + // know of the solution, we should + // now care how to get at it. We will + // pack everything we need into a + // namespace of its own, for much the + // same reasons as for the + // evaluations above. + // + // Since we have discussed Laplace + // solvers already in considerable + // detail in previous examples, there + // is not much new stuff + // following. Rather, we have to a + // great extent cannibalized previous + // examples and put them, in slightly + // different form, into this example + // program. We will therefore mostly + // be concerned with discussing the + // differences to previous examples. + // + // Basically, as already said in the + // introduction, the lack of new + // stuff in this example is + // deliberate, as it is more to + // demonstrate software design + // practices, rather than + // mathematics. The emphasis in + // explanations below will therefore + // be more on the actual + // implementation. namespace LaplaceSolver { - // @sect4{An abstract base class} - - // In defining a Laplace solver, we - // start out by declaring an - // abstract base class, that has no - // functionality itself except for - // taking and storing a pointer to - // the triangulation to be used - // later. - // - // This base class is very general, - // and could as well be used for - // any other stationary problem. It - // provides declarations of - // functions that shall, in derived - // classes, solve a problem, - // postprocess the solution with a - // list of evaluation objects, and - // refine the grid, - // respectively. None of these - // functions actually does - // something itself in the base - // class. - // - // Due to the lack of actual - // functionality, the programming - // style of declaring very abstract - // base classes reminds of the - // style used in Smalltalk or Java - // programs, where all classes are - // derived from entirely abstract - // classes Object, even number - // representations. The author - // admits that he does not - // particularly like the use of - // such a style in C++, as it puts - // style over reason. Furthermore, - // it promotes the use of virtual - // functions for everything (for - // example, in Java, all functions - // are virtual per se), which, - // however, has proven to be rather - // inefficient in many applications - // where functions are often only - // accessing data, not doing - // computations, and therefore - // quickly return; the overhead of - // virtual functions can then be - // significant. The opinion of the - // author is to have abstract base - // classes wherever at least some - // part of the code of actual - // implementations can be shared - // and thus separated into the base - // class. - // - // Besides all these theoretical - // questions, we here have a good - // reason, which will become - // clearer to the reader - // below. Basically, we want to be - // able to have a family of - // different Laplace solvers that - // differ so much that no larger - // common subset of functionality - // could be found. We therefore - // just declare such an abstract - // base class, taking a pointer to - // a triangulation in the - // constructor and storing it - // henceforth. Since this - // triangulation will be used - // throughout all computations, we - // have to make sure that the - // triangulation exists until the - // destructor exits. We do this by - // keeping a SmartPointer to - // this triangulation, which uses a - // counter in the triangulation - // class to denote the fact that - // there is still an object out - // there using this triangulation, - // thus leading to an abort in case - // the triangulation is attempted - // to be destructed while this - // object still uses it. - // - // Note that while the pointer - // itself is declared constant - // (i.e. throughout the lifetime of - // this object, the pointer points - // to the same object), it is not - // declared as a pointer to a - // constant triangulation. In fact, - // by this we allow that derived - // classes refine or coarsen the - // triangulation within the - // refine_grid function. - // - // Finally, we have a function - // n_dofs is only a tool for - // the driver functions to decide - // whether we want to go on with - // mesh refinement or not. It - // returns the number of degrees of - // freedom the present simulation - // has. + // @sect4{An abstract base class} + + // In defining a Laplace solver, we + // start out by declaring an + // abstract base class, that has no + // functionality itself except for + // taking and storing a pointer to + // the triangulation to be used + // later. + // + // This base class is very general, + // and could as well be used for + // any other stationary problem. It + // provides declarations of + // functions that shall, in derived + // classes, solve a problem, + // postprocess the solution with a + // list of evaluation objects, and + // refine the grid, + // respectively. None of these + // functions actually does + // something itself in the base + // class. + // + // Due to the lack of actual + // functionality, the programming + // style of declaring very abstract + // base classes reminds of the + // style used in Smalltalk or Java + // programs, where all classes are + // derived from entirely abstract + // classes Object, even number + // representations. The author + // admits that he does not + // particularly like the use of + // such a style in C++, as it puts + // style over reason. Furthermore, + // it promotes the use of virtual + // functions for everything (for + // example, in Java, all functions + // are virtual per se), which, + // however, has proven to be rather + // inefficient in many applications + // where functions are often only + // accessing data, not doing + // computations, and therefore + // quickly return; the overhead of + // virtual functions can then be + // significant. The opinion of the + // author is to have abstract base + // classes wherever at least some + // part of the code of actual + // implementations can be shared + // and thus separated into the base + // class. + // + // Besides all these theoretical + // questions, we here have a good + // reason, which will become + // clearer to the reader + // below. Basically, we want to be + // able to have a family of + // different Laplace solvers that + // differ so much that no larger + // common subset of functionality + // could be found. We therefore + // just declare such an abstract + // base class, taking a pointer to + // a triangulation in the + // constructor and storing it + // henceforth. Since this + // triangulation will be used + // throughout all computations, we + // have to make sure that the + // triangulation exists until the + // destructor exits. We do this by + // keeping a SmartPointer to + // this triangulation, which uses a + // counter in the triangulation + // class to denote the fact that + // there is still an object out + // there using this triangulation, + // thus leading to an abort in case + // the triangulation is attempted + // to be destructed while this + // object still uses it. + // + // Note that while the pointer + // itself is declared constant + // (i.e. throughout the lifetime of + // this object, the pointer points + // to the same object), it is not + // declared as a pointer to a + // constant triangulation. In fact, + // by this we allow that derived + // classes refine or coarsen the + // triangulation within the + // refine_grid function. + // + // Finally, we have a function + // n_dofs is only a tool for + // the driver functions to decide + // whether we want to go on with + // mesh refinement or not. It + // returns the number of degrees of + // freedom the present simulation + // has. template class Base { - public: - Base (Triangulation &coarse_grid); - virtual ~Base (); + public: + Base (Triangulation &coarse_grid); + virtual ~Base (); - virtual void solve_problem () = 0; - virtual void postprocess (const Evaluation::EvaluationBase &postprocessor) const = 0; - virtual void refine_grid () = 0; - virtual unsigned int n_dofs () const = 0; + virtual void solve_problem () = 0; + virtual void postprocess (const Evaluation::EvaluationBase &postprocessor) const = 0; + virtual void refine_grid () = 0; + virtual unsigned int n_dofs () const = 0; - protected: - const SmartPointer > triangulation; + protected: + const SmartPointer > triangulation; }; - // The implementation of the only - // two non-abstract functions is - // then rather boring: + // The implementation of the only + // two non-abstract functions is + // then rather boring: template Base::Base (Triangulation &coarse_grid) - : - triangulation (&coarse_grid) + : + triangulation (&coarse_grid) {} @@ -871,210 +871,210 @@ namespace Step13 {} - // @sect4{A general solver class} - - // Following now the main class - // that implements assembling the - // matrix of the linear system, - // solving it, and calling the - // postprocessor objects on the - // solution. It implements the - // solve_problem and - // postprocess functions - // declared in the base class. It - // does not, however, implement the - // refine_grid method, as mesh - // refinement will be implemented - // in a number of derived classes. - // - // It also declares a new abstract - // virtual function, - // assemble_rhs, that needs to - // be overloaded in subclasses. The - // reason is that we will implement - // two different classes that will - // implement different methods to - // assemble the right hand side - // vector. This function might also - // be interesting in cases where - // the right hand side depends not - // simply on a continuous function, - // but on something else as well, - // for example the solution of - // another discretized problem, - // etc. The latter happens - // frequently in non-linear - // problems. - // - // As we mentioned previously, the - // actual content of this class is - // not new, but a mixture of - // various techniques already used - // in previous examples. We will - // therefore not discuss them in - // detail, but refer the reader to - // these programs. - // - // Basically, in a few words, the - // constructor of this class takes - // pointers to a triangulation, a - // finite element, and a function - // object representing the boundary - // values. These are either passed - // down to the base class's - // constructor, or are stored and - // used to generate a - // DoFHandler object - // later. Since finite elements and - // quadrature formula should match, - // it is also passed a quadrature - // object. - // - // The solve_problem sets up - // the data structures for the - // actual solution, calls the - // functions to assemble the linear - // system, and solves it. - // - // The postprocess function - // finally takes an evaluation - // object and applies it to the - // computed solution. - // - // The n_dofs function finally - // implements the pure virtual - // function of the base class. + // @sect4{A general solver class} + + // Following now the main class + // that implements assembling the + // matrix of the linear system, + // solving it, and calling the + // postprocessor objects on the + // solution. It implements the + // solve_problem and + // postprocess functions + // declared in the base class. It + // does not, however, implement the + // refine_grid method, as mesh + // refinement will be implemented + // in a number of derived classes. + // + // It also declares a new abstract + // virtual function, + // assemble_rhs, that needs to + // be overloaded in subclasses. The + // reason is that we will implement + // two different classes that will + // implement different methods to + // assemble the right hand side + // vector. This function might also + // be interesting in cases where + // the right hand side depends not + // simply on a continuous function, + // but on something else as well, + // for example the solution of + // another discretized problem, + // etc. The latter happens + // frequently in non-linear + // problems. + // + // As we mentioned previously, the + // actual content of this class is + // not new, but a mixture of + // various techniques already used + // in previous examples. We will + // therefore not discuss them in + // detail, but refer the reader to + // these programs. + // + // Basically, in a few words, the + // constructor of this class takes + // pointers to a triangulation, a + // finite element, and a function + // object representing the boundary + // values. These are either passed + // down to the base class's + // constructor, or are stored and + // used to generate a + // DoFHandler object + // later. Since finite elements and + // quadrature formula should match, + // it is also passed a quadrature + // object. + // + // The solve_problem sets up + // the data structures for the + // actual solution, calls the + // functions to assemble the linear + // system, and solves it. + // + // The postprocess function + // finally takes an evaluation + // object and applies it to the + // computed solution. + // + // The n_dofs function finally + // implements the pure virtual + // function of the base class. template class Solver : public virtual Base { - public: - Solver (Triangulation &triangulation, - const FiniteElement &fe, - const Quadrature &quadrature, - const Function &boundary_values); - virtual - ~Solver (); - - virtual - void - solve_problem (); - - virtual - void - postprocess (const Evaluation::EvaluationBase &postprocessor) const; - - virtual - unsigned int - n_dofs () const; - - // In the protected section of - // this class, we first have a - // number of member variables, - // of which the use should be - // clear from the previous - // examples: - protected: - const SmartPointer > fe; - const SmartPointer > quadrature; - DoFHandler dof_handler; - Vector solution; - const SmartPointer > boundary_values; - - // Then we declare an abstract - // function that will be used - // to assemble the right hand - // side. As explained above, - // there are various cases for - // which this action differs - // strongly in what is - // necessary, so we defer this - // to derived classes: - virtual void assemble_rhs (Vector &rhs) const = 0; - - // Next, in the private - // section, we have a small - // class which represents an - // entire linear system, i.e. a - // matrix, a right hand side, - // and a solution vector, as - // well as the constraints that - // are applied to it, such as - // those due to hanging - // nodes. Its constructor - // initializes the various - // subobjects, and there is a - // function that implements a - // conjugate gradient method as - // solver. - private: - struct LinearSystem - { - LinearSystem (const DoFHandler &dof_handler); + public: + Solver (Triangulation &triangulation, + const FiniteElement &fe, + const Quadrature &quadrature, + const Function &boundary_values); + virtual + ~Solver (); + + virtual + void + solve_problem (); + + virtual + void + postprocess (const Evaluation::EvaluationBase &postprocessor) const; + + virtual + unsigned int + n_dofs () const; + + // In the protected section of + // this class, we first have a + // number of member variables, + // of which the use should be + // clear from the previous + // examples: + protected: + const SmartPointer > fe; + const SmartPointer > quadrature; + DoFHandler dof_handler; + Vector solution; + const SmartPointer > boundary_values; + + // Then we declare an abstract + // function that will be used + // to assemble the right hand + // side. As explained above, + // there are various cases for + // which this action differs + // strongly in what is + // necessary, so we defer this + // to derived classes: + virtual void assemble_rhs (Vector &rhs) const = 0; + + // Next, in the private + // section, we have a small + // class which represents an + // entire linear system, i.e. a + // matrix, a right hand side, + // and a solution vector, as + // well as the constraints that + // are applied to it, such as + // those due to hanging + // nodes. Its constructor + // initializes the various + // subobjects, and there is a + // function that implements a + // conjugate gradient method as + // solver. + private: + struct LinearSystem + { + LinearSystem (const DoFHandler &dof_handler); - void solve (Vector &solution) const; + void solve (Vector &solution) const; - ConstraintMatrix hanging_node_constraints; - SparsityPattern sparsity_pattern; - SparseMatrix matrix; - Vector rhs; - }; + ConstraintMatrix hanging_node_constraints; + SparsityPattern sparsity_pattern; + SparseMatrix matrix; + Vector rhs; + }; - // Finally, there is a pair of - // functions which will be used - // to assemble the actual - // system matrix. It calls the - // virtual function assembling - // the right hand side, and - // installs a number threads - // each running the second - // function which assembles - // part of the system - // matrix. The mechanism for - // doing so is the same as in - // the step-9 example program. - void - assemble_linear_system (LinearSystem &linear_system); - - void - assemble_matrix (LinearSystem &linear_system, - const typename DoFHandler::active_cell_iterator &begin_cell, - const typename DoFHandler::active_cell_iterator &end_cell, - Threads::ThreadMutex &mutex) const; + // Finally, there is a pair of + // functions which will be used + // to assemble the actual + // system matrix. It calls the + // virtual function assembling + // the right hand side, and + // installs a number threads + // each running the second + // function which assembles + // part of the system + // matrix. The mechanism for + // doing so is the same as in + // the step-9 example program. + void + assemble_linear_system (LinearSystem &linear_system); + + void + assemble_matrix (LinearSystem &linear_system, + const typename DoFHandler::active_cell_iterator &begin_cell, + const typename DoFHandler::active_cell_iterator &end_cell, + Threads::ThreadMutex &mutex) const; }; - // Now here comes the constructor - // of the class. It does not do - // much except store pointers to - // the objects given, and generate - // DoFHandler object - // initialized with the given - // pointer to a triangulation. This - // causes the DoF handler to store - // that pointer, but does not - // already generate a finite - // element numbering (we only ask - // for that in the - // solve_problem function). + // Now here comes the constructor + // of the class. It does not do + // much except store pointers to + // the objects given, and generate + // DoFHandler object + // initialized with the given + // pointer to a triangulation. This + // causes the DoF handler to store + // that pointer, but does not + // already generate a finite + // element numbering (we only ask + // for that in the + // solve_problem function). template Solver::Solver (Triangulation &triangulation, const FiniteElement &fe, const Quadrature &quadrature, const Function &boundary_values) - : - Base (triangulation), - fe (&fe), - quadrature (&quadrature), - dof_handler (triangulation), - boundary_values (&boundary_values) + : + Base (triangulation), + fe (&fe), + quadrature (&quadrature), + dof_handler (triangulation), + boundary_values (&boundary_values) {} - // The destructor is simple, it - // only clears the information - // stored in the DoF handler object - // to release the memory. + // The destructor is simple, it + // only clears the information + // stored in the DoF handler object + // to release the memory. template Solver::~Solver () { @@ -1082,19 +1082,19 @@ namespace Step13 } - // The next function is the one - // which delegates the main work in - // solving the problem: it sets up - // the DoF handler object with the - // finite element given to the - // constructor of this object, the - // creates an object that denotes - // the linear system (i.e. the - // matrix, the right hand side - // vector, and the solution - // vector), calls the function to - // assemble it, and finally solves - // it: + // The next function is the one + // which delegates the main work in + // solving the problem: it sets up + // the DoF handler object with the + // finite element given to the + // constructor of this object, the + // creates an object that denotes + // the linear system (i.e. the + // matrix, the right hand side + // vector, and the solution + // vector), calls the function to + // assemble it, and finally solves + // it: template void Solver::solve_problem () @@ -1108,14 +1108,14 @@ namespace Step13 } - // As stated above, the - // postprocess function takes - // an evaluation object, and - // applies it to the computed - // solution. This function may be - // called multiply, once for each - // evaluation of the solution which - // the user required. + // As stated above, the + // postprocess function takes + // an evaluation object, and + // applies it to the computed + // solution. This function may be + // called multiply, once for each + // evaluation of the solution which + // the user required. template void Solver:: @@ -1125,8 +1125,8 @@ namespace Step13 } - // The n_dofs function should - // be self-explanatory: + // The n_dofs function should + // be self-explanatory: template unsigned int Solver::n_dofs () const @@ -1135,54 +1135,54 @@ namespace Step13 } - // The following function assembles matrix - // and right hand side of the linear system - // to be solved in each step. It goes along - // the same lines as used in previous - // examples, so we explain it only - // briefly. Note that we do a number of - // things in parallel, a process described - // in more detail in the @ref threads - // module. + // The following function assembles matrix + // and right hand side of the linear system + // to be solved in each step. It goes along + // the same lines as used in previous + // examples, so we explain it only + // briefly. Note that we do a number of + // things in parallel, a process described + // in more detail in the @ref threads + // module. template void Solver::assemble_linear_system (LinearSystem &linear_system) { - // First define a convenience - // abbreviation for these lengthy - // iterator names... + // First define a convenience + // abbreviation for these lengthy + // iterator names... typedef - typename DoFHandler::active_cell_iterator - active_cell_iterator; - - // ... and use it to split up the - // set of cells into a number of - // pieces of equal size. The - // number of blocks is set to the - // default number of threads to - // be used, which by default is - // set to the number of - // processors found in your - // computer at startup of the - // program: + typename DoFHandler::active_cell_iterator + active_cell_iterator; + + // ... and use it to split up the + // set of cells into a number of + // pieces of equal size. The + // number of blocks is set to the + // default number of threads to + // be used, which by default is + // set to the number of + // processors found in your + // computer at startup of the + // program: const unsigned int n_threads = multithread_info.n_default_threads; std::vector > - thread_ranges + thread_ranges = Threads::split_range (dof_handler.begin_active (), dof_handler.end (), n_threads); - // These ranges are then assigned - // to a number of threads which - // we create next. Each will - // assemble the local cell - // matrices on the assigned - // cells, and fill the matrix - // object with it. Since there is - // need for synchronization when - // filling the same matrix from - // different threads, we need a - // mutex here: + // These ranges are then assigned + // to a number of threads which + // we create next. Each will + // assemble the local cell + // matrices on the assigned + // cells, and fill the matrix + // object with it. Since there is + // need for synchronization when + // filling the same matrix from + // different threads, we need a + // mutex here: Threads::ThreadMutex mutex; Threads::ThreadGroup<> threads; for (unsigned int thread=0; thread boundary_value_map; VectorTools::interpolate_boundary_values (dof_handler, 0, @@ -1216,19 +1216,19 @@ namespace Step13 boundary_value_map); - // If this is done, wait for the - // matrix assembling threads, and - // condense the constraints in - // the matrix as well: + // If this is done, wait for the + // matrix assembling threads, and + // condense the constraints in + // the matrix as well: threads.join_all (); linear_system.hanging_node_constraints.condense (linear_system.matrix); - // Now that we have the linear - // system, we can also treat - // boundary values, which need to - // be eliminated from both the - // matrix and the right hand - // side: + // Now that we have the linear + // system, we can also treat + // boundary values, which need to + // be eliminated from both the + // matrix and the right hand + // side: MatrixTools::apply_boundary_values (boundary_value_map, linear_system.matrix, solution, @@ -1237,15 +1237,15 @@ namespace Step13 } - // The second of this pair of - // functions takes a range of cell - // iterators, and assembles the - // system matrix on this part of - // the domain. Since it's actions - // have all been explained in - // previous programs, we do not - // comment on it any more, except - // for one pointe below. + // The second of this pair of + // functions takes a range of cell + // iterators, and assembles the + // system matrix on this part of + // the domain. Since it's actions + // have all been explained in + // previous programs, we do not + // comment on it any more, except + // for one pointe below. template void Solver::assemble_matrix (LinearSystem &linear_system, @@ -1280,165 +1280,165 @@ namespace Step13 cell->get_dof_indices (local_dof_indices); - // In the step-9 program, we - // have shown that you have - // to use the mutex to lock - // the matrix when copying - // the elements from the - // local to the global - // matrix. This was necessary - // to avoid that two threads - // access it at the same - // time, eventually - // overwriting their - // respective - // work. Previously, we have - // used the acquire and - // release functions of - // the mutex to lock and - // unlock the mutex, - // respectively. While this - // is valid, there is one - // possible catch: if between - // the locking operation and - // the unlocking operation an - // exception is thrown, the - // mutex remains in the - // locked state, and in some - // cases this might lead to - // deadlocks. A similar - // situation arises, when one - // changes the code to have a - // return statement somewhere - // in the middle of the - // locked block, and forgets - // that before we call - // return, we also have - // to unlock the mutex. This - // all is not be a problem - // here, but we want to show - // the general technique to - // cope with these problems - // nevertheless: have an - // object that upon - // initialization (i.e. in - // its constructor) locks the - // mutex, and on running the - // destructor unlocks it - // again. This is called the - // scoped lock pattern - // (apparently invented by - // Doug Schmidt originally), - // and it works because - // destructors of local - // objects are also run when - // we exit the function - // either through a - // return statement, or - // when an exception is - // raised. Thus, it is - // guaranteed that the mutex - // will always be unlocked - // when we exit this part of - // the program, whether the - // operation completed - // successfully or not, - // whether the exit path was - // something we implemented - // willfully or whether the - // function was exited by an - // exception that we did not - // forsee. - // - // deal.II implements the - // scoped locking pattern in - // the - // ThreadMutex::ScopedLock - // class: it takes the mutex - // in the constructor and - // locks it; in its - // destructor, it unlocks it - // again. So here is how it - // is used: + // In the step-9 program, we + // have shown that you have + // to use the mutex to lock + // the matrix when copying + // the elements from the + // local to the global + // matrix. This was necessary + // to avoid that two threads + // access it at the same + // time, eventually + // overwriting their + // respective + // work. Previously, we have + // used the acquire and + // release functions of + // the mutex to lock and + // unlock the mutex, + // respectively. While this + // is valid, there is one + // possible catch: if between + // the locking operation and + // the unlocking operation an + // exception is thrown, the + // mutex remains in the + // locked state, and in some + // cases this might lead to + // deadlocks. A similar + // situation arises, when one + // changes the code to have a + // return statement somewhere + // in the middle of the + // locked block, and forgets + // that before we call + // return, we also have + // to unlock the mutex. This + // all is not be a problem + // here, but we want to show + // the general technique to + // cope with these problems + // nevertheless: have an + // object that upon + // initialization (i.e. in + // its constructor) locks the + // mutex, and on running the + // destructor unlocks it + // again. This is called the + // scoped lock pattern + // (apparently invented by + // Doug Schmidt originally), + // and it works because + // destructors of local + // objects are also run when + // we exit the function + // either through a + // return statement, or + // when an exception is + // raised. Thus, it is + // guaranteed that the mutex + // will always be unlocked + // when we exit this part of + // the program, whether the + // operation completed + // successfully or not, + // whether the exit path was + // something we implemented + // willfully or whether the + // function was exited by an + // exception that we did not + // forsee. + // + // deal.II implements the + // scoped locking pattern in + // the + // ThreadMutex::ScopedLock + // class: it takes the mutex + // in the constructor and + // locks it; in its + // destructor, it unlocks it + // again. So here is how it + // is used: Threads::ThreadMutex::ScopedLock lock (mutex); for (unsigned int i=0; ilock variable goes out - // of existence and its - // destructor the mutex is - // unlocked. + // Here, at the brace, the + // current scope ends, so the + // lock variable goes out + // of existence and its + // destructor the mutex is + // unlocked. }; } - // Now for the functions that - // implement actions in the linear - // system class. First, the - // constructor initializes all data - // elements to their correct sizes, - // and sets up a number of - // additional data structures, such - // as constraints due to hanging - // nodes. Since setting up the - // hanging nodes and finding out - // about the nonzero elements of - // the matrix is independent, we do - // that in parallel (if the library - // was configured to use - // concurrency, at least; - // otherwise, the actions are - // performed sequentially). Note - // that we start only one thread, - // and do the second action in the - // main thread. Since only one - // thread is generated, we don't - // use the Threads::ThreadGroup - // class here, but rather use the - // one created thread object - // directly to wait for this - // particular thread's exit. - // - // Note that taking up the address - // of the - // DoFTools::make_hanging_node_constraints - // function is a little tricky, - // since there are actually three - // of them, one for each supported - // space dimension. Taking - // addresses of overloaded - // functions is somewhat - // complicated in C++, since the - // address-of operator & in - // that case returns more like a - // set of values (the addresses of - // all functions with that name), - // and selecting the right one is - // then the next step. If the - // context dictates which one to - // take (for example by assigning - // to a function pointer of known - // type), then the compiler can do - // that by itself, but if this set - // of pointers shall be given as - // the argument to a function that - // takes a template, the compiler - // could choose all without having - // a preference for one. We - // therefore have to make it clear - // to the compiler which one we - // would like to have; for this, we - // could use a cast, but for more - // clarity, we assign it to a - // temporary mhnc_p (short for - // pointer to - // make_hanging_node_constraints) - // with the right type, and using - // this pointer instead. + // Now for the functions that + // implement actions in the linear + // system class. First, the + // constructor initializes all data + // elements to their correct sizes, + // and sets up a number of + // additional data structures, such + // as constraints due to hanging + // nodes. Since setting up the + // hanging nodes and finding out + // about the nonzero elements of + // the matrix is independent, we do + // that in parallel (if the library + // was configured to use + // concurrency, at least; + // otherwise, the actions are + // performed sequentially). Note + // that we start only one thread, + // and do the second action in the + // main thread. Since only one + // thread is generated, we don't + // use the Threads::ThreadGroup + // class here, but rather use the + // one created thread object + // directly to wait for this + // particular thread's exit. + // + // Note that taking up the address + // of the + // DoFTools::make_hanging_node_constraints + // function is a little tricky, + // since there are actually three + // of them, one for each supported + // space dimension. Taking + // addresses of overloaded + // functions is somewhat + // complicated in C++, since the + // address-of operator & in + // that case returns more like a + // set of values (the addresses of + // all functions with that name), + // and selecting the right one is + // then the next step. If the + // context dictates which one to + // take (for example by assigning + // to a function pointer of known + // type), then the compiler can do + // that by itself, but if this set + // of pointers shall be given as + // the argument to a function that + // takes a template, the compiler + // could choose all without having + // a preference for one. We + // therefore have to make it clear + // to the compiler which one we + // would like to have; for this, we + // could use a cast, but for more + // clarity, we assign it to a + // temporary mhnc_p (short for + // pointer to + // make_hanging_node_constraints) + // with the right type, and using + // this pointer instead. template Solver::LinearSystem:: LinearSystem (const DoFHandler &dof_handler) @@ -1446,32 +1446,32 @@ namespace Step13 hanging_node_constraints.clear (); void (*mhnc_p) (const DoFHandler &, - ConstraintMatrix &) + ConstraintMatrix &) = &DoFTools::make_hanging_node_constraints; Threads::Thread<> - mhnc_thread = Threads::new_thread (mhnc_p, - dof_handler, - hanging_node_constraints); + mhnc_thread = Threads::new_thread (mhnc_p, + dof_handler, + hanging_node_constraints); sparsity_pattern.reinit (dof_handler.n_dofs(), dof_handler.n_dofs(), dof_handler.max_couplings_between_dofs()); DoFTools::make_sparsity_pattern (dof_handler, sparsity_pattern); - // Wait until the - // hanging_node_constraints - // object is fully set up, then - // close it and use it to - // condense the sparsity pattern: + // Wait until the + // hanging_node_constraints + // object is fully set up, then + // close it and use it to + // condense the sparsity pattern: mhnc_thread.join (); hanging_node_constraints.close (); hanging_node_constraints.condense (sparsity_pattern); - // Finally, close the sparsity - // pattern, initialize the - // matrix, and set the right hand - // side vector to the right size. + // Finally, close the sparsity + // pattern, initialize the + // matrix, and set the right hand + // side vector to the right size. sparsity_pattern.compress(); matrix.reinit (sparsity_pattern); rhs.reinit (dof_handler.n_dofs()); @@ -1479,13 +1479,13 @@ namespace Step13 - // The second function of this - // class simply solves the linear - // system by a preconditioned - // conjugate gradient method. This - // has been extensively discussed - // before, so we don't dwell into - // it any more. + // The second function of this + // class simply solves the linear + // system by a preconditioned + // conjugate gradient method. This + // has been extensively discussed + // before, so we don't dwell into + // it any more. template void Solver::LinearSystem::solve (Vector &solution) const @@ -1504,59 +1504,59 @@ namespace Step13 - // @sect4{A primal solver} - - // In the previous section, a base - // class for Laplace solvers was - // implemented, that lacked the - // functionality to assemble the - // right hand side vector, however, - // for reasons that were explained - // there. Now we implement a - // corresponding class that can do - // this for the case that the right - // hand side of a problem is given - // as a function object. - // - // The actions of the class are - // rather what you have seen - // already in previous examples - // already, so a brief explanation - // should suffice: the constructor - // takes the same data as does that - // of the underlying class (to - // which it passes all information) - // except for one function object - // that denotes the right hand side - // of the problem. A pointer to - // this object is stored (again as - // a SmartPointer, in order to - // make sure that the function - // object is not deleted as long as - // it is still used by this class). - // - // The only functional part of this - // class is the assemble_rhs - // method that does what its name - // suggests. + // @sect4{A primal solver} + + // In the previous section, a base + // class for Laplace solvers was + // implemented, that lacked the + // functionality to assemble the + // right hand side vector, however, + // for reasons that were explained + // there. Now we implement a + // corresponding class that can do + // this for the case that the right + // hand side of a problem is given + // as a function object. + // + // The actions of the class are + // rather what you have seen + // already in previous examples + // already, so a brief explanation + // should suffice: the constructor + // takes the same data as does that + // of the underlying class (to + // which it passes all information) + // except for one function object + // that denotes the right hand side + // of the problem. A pointer to + // this object is stored (again as + // a SmartPointer, in order to + // make sure that the function + // object is not deleted as long as + // it is still used by this class). + // + // The only functional part of this + // class is the assemble_rhs + // method that does what its name + // suggests. template class PrimalSolver : public Solver { - public: - PrimalSolver (Triangulation &triangulation, - const FiniteElement &fe, - const Quadrature &quadrature, - const Function &rhs_function, - const Function &boundary_values); - protected: - const SmartPointer > rhs_function; - virtual void assemble_rhs (Vector &rhs) const; + public: + PrimalSolver (Triangulation &triangulation, + const FiniteElement &fe, + const Quadrature &quadrature, + const Function &rhs_function, + const Function &boundary_values); + protected: + const SmartPointer > rhs_function; + virtual void assemble_rhs (Vector &rhs) const; }; - // The constructor of this class - // basically does what it is - // announced to do above... + // The constructor of this class + // basically does what it is + // announced to do above... template PrimalSolver:: PrimalSolver (Triangulation &triangulation, @@ -1564,20 +1564,20 @@ namespace Step13 const Quadrature &quadrature, const Function &rhs_function, const Function &boundary_values) - : - Base (triangulation), - Solver (triangulation, fe, - quadrature, boundary_values), - rhs_function (&rhs_function) + : + Base (triangulation), + Solver (triangulation, fe, + quadrature, boundary_values), + rhs_function (&rhs_function) {} - // ... as does the assemble_rhs - // function. Since this is - // explained in several of the - // previous example programs, we - // leave it at that. + // ... as does the assemble_rhs + // function. Since this is + // explained in several of the + // previous example programs, we + // leave it at that. template void PrimalSolver:: @@ -1595,8 +1595,8 @@ namespace Step13 std::vector local_dof_indices (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = this->dof_handler.begin_active(), - endc = this->dof_handler.end(); + cell = this->dof_handler.begin_active(), + endc = this->dof_handler.end(); for (; cell!=endc; ++cell) { cell_rhs = 0; @@ -1617,47 +1617,47 @@ namespace Step13 } - // @sect4{Global refinement} - - // By now, all functions of the - // abstract base class except for - // the refine_grid function - // have been implemented. We will - // now have two classes that - // implement this function for the - // PrimalSolver class, one - // doing global refinement, one a - // form of local refinement. - // - // The first, doing global - // refinement, is rather simple: - // its main function just calls - // triangulation-@>refine_global - // (1);, which does all the work. - // - // Note that since the Base - // base class of the Solver - // class is virtual, we have to - // declare a constructor that - // initializes the immediate base - // class as well as the abstract - // virtual one. - // - // Apart from this technical - // complication, the class is - // probably simple enough to be - // left without further comments. + // @sect4{Global refinement} + + // By now, all functions of the + // abstract base class except for + // the refine_grid function + // have been implemented. We will + // now have two classes that + // implement this function for the + // PrimalSolver class, one + // doing global refinement, one a + // form of local refinement. + // + // The first, doing global + // refinement, is rather simple: + // its main function just calls + // triangulation-@>refine_global + // (1);, which does all the work. + // + // Note that since the Base + // base class of the Solver + // class is virtual, we have to + // declare a constructor that + // initializes the immediate base + // class as well as the abstract + // virtual one. + // + // Apart from this technical + // complication, the class is + // probably simple enough to be + // left without further comments. template class RefinementGlobal : public PrimalSolver { - public: - RefinementGlobal (Triangulation &coarse_grid, - const FiniteElement &fe, - const Quadrature &quadrature, - const Function &rhs_function, - const Function &boundary_values); - - virtual void refine_grid (); + public: + RefinementGlobal (Triangulation &coarse_grid, + const FiniteElement &fe, + const Quadrature &quadrature, + const Function &rhs_function, + const Function &boundary_values); + + virtual void refine_grid (); }; @@ -1669,10 +1669,10 @@ namespace Step13 const Quadrature &quadrature, const Function &rhs_function, const Function &boundary_values) - : - Base (coarse_grid), - PrimalSolver (coarse_grid, fe, quadrature, - rhs_function, boundary_values) + : + Base (coarse_grid), + PrimalSolver (coarse_grid, fe, quadrature, + rhs_function, boundary_values) {} @@ -1685,38 +1685,38 @@ namespace Step13 } - // @sect4{Local refinement by the Kelly error indicator} - - // The second class implementing - // refinement strategies uses the - // Kelly refinemet indicator used - // in various example programs - // before. Since this indicator is - // already implemented in a class - // of its own inside the deal.II - // library, there is not much t do - // here except cal the function - // computing the indicator, then - // using it to select a number of - // cells for refinement and - // coarsening, and refinement the - // mesh accordingly. - // - // Again, this should now be - // sufficiently standard to allow - // the omission of further - // comments. + // @sect4{Local refinement by the Kelly error indicator} + + // The second class implementing + // refinement strategies uses the + // Kelly refinemet indicator used + // in various example programs + // before. Since this indicator is + // already implemented in a class + // of its own inside the deal.II + // library, there is not much t do + // here except cal the function + // computing the indicator, then + // using it to select a number of + // cells for refinement and + // coarsening, and refinement the + // mesh accordingly. + // + // Again, this should now be + // sufficiently standard to allow + // the omission of further + // comments. template class RefinementKelly : public PrimalSolver { - public: - RefinementKelly (Triangulation &coarse_grid, - const FiniteElement &fe, - const Quadrature &quadrature, - const Function &rhs_function, - const Function &boundary_values); + public: + RefinementKelly (Triangulation &coarse_grid, + const FiniteElement &fe, + const Quadrature &quadrature, + const Function &rhs_function, + const Function &boundary_values); - virtual void refine_grid (); + virtual void refine_grid (); }; @@ -1728,10 +1728,10 @@ namespace Step13 const Quadrature &quadrature, const Function &rhs_function, const Function &boundary_values) - : - Base (coarse_grid), - PrimalSolver (coarse_grid, fe, quadrature, - rhs_function, boundary_values) + : + Base (coarse_grid), + PrimalSolver (coarse_grid, fe, quadrature, + rhs_function, boundary_values) {} @@ -1757,50 +1757,50 @@ namespace Step13 - // @sect3{Equation data} - - // As this is one more academic - // example, we'd like to compare - // exact and computed solution - // against each other. For this, we - // need to declare function classes - // representing the exact solution - // (for comparison and for the - // Dirichlet boundary values), as - // well as a class that denotes the - // right hand side of the equation - // (this is simply the Laplace - // operator applied to the exact - // solution we'd like to recover). - // - // For this example, let us choose as - // exact solution the function - // $u(x,y)=exp(x+sin(10y+5x^2))$. In more - // than two dimensions, simply repeat - // the sine-factor with y - // replaced by z and so on. Given - // this, the following two classes - // are probably straightforward from - // the previous examples. - // - // As in previous examples, the C++ - // language forces us to declare and - // define a constructor to the - // following classes even though they - // are empty. This is due to the fact - // that the base class has no default - // constructor (i.e. one without - // arguments), even though it has a - // constructor which has default - // values for all arguments. + // @sect3{Equation data} + + // As this is one more academic + // example, we'd like to compare + // exact and computed solution + // against each other. For this, we + // need to declare function classes + // representing the exact solution + // (for comparison and for the + // Dirichlet boundary values), as + // well as a class that denotes the + // right hand side of the equation + // (this is simply the Laplace + // operator applied to the exact + // solution we'd like to recover). + // + // For this example, let us choose as + // exact solution the function + // $u(x,y)=exp(x+sin(10y+5x^2))$. In more + // than two dimensions, simply repeat + // the sine-factor with y + // replaced by z and so on. Given + // this, the following two classes + // are probably straightforward from + // the previous examples. + // + // As in previous examples, the C++ + // language forces us to declare and + // define a constructor to the + // following classes even though they + // are empty. This is due to the fact + // that the base class has no default + // constructor (i.e. one without + // arguments), even though it has a + // constructor which has default + // values for all arguments. template class Solution : public Function { - public: - Solution () : Function () {} + public: + Solution () : Function () {} - virtual double value (const Point &p, - const unsigned int component) const; + virtual double value (const Point &p, + const unsigned int component) const; }; @@ -1821,11 +1821,11 @@ namespace Step13 template class RightHandSide : public Function { - public: - RightHandSide () : Function () {} + public: + RightHandSide () : Function () {} - virtual double value (const Point &p, - const unsigned int component) const; + virtual double value (const Point &p, + const unsigned int component) const; }; @@ -1856,68 +1856,68 @@ namespace Step13 - // @sect3{The driver routines} + // @sect3{The driver routines} - // What is now missing are only the - // functions that actually select the - // various options, and run the - // simulation on successively finer - // grids to monitor the progress as - // the mesh is refined. - // - // This we do in the following - // function: it takes a solver - // object, and a list of - // postprocessing (evaluation) - // objects, and runs them with - // intermittent mesh refinement: + // What is now missing are only the + // functions that actually select the + // various options, and run the + // simulation on successively finer + // grids to monitor the progress as + // the mesh is refined. + // + // This we do in the following + // function: it takes a solver + // object, and a list of + // postprocessing (evaluation) + // objects, and runs them with + // intermittent mesh refinement: template void run_simulation (LaplaceSolver::Base &solver, const std::list *> &postprocessor_list) { - // We will give an indicator of the - // step we are presently computing, - // in order to keep the user - // informed that something is still - // happening, and that the program - // is not in an endless loop. This - // is the head of this status line: + // We will give an indicator of the + // step we are presently computing, + // in order to keep the user + // informed that something is still + // happening, and that the program + // is not in an endless loop. This + // is the head of this status line: std::cout << "Refinement cycle: "; - // Then start a loop which only - // terminates once the number of - // degrees of freedom is larger - // than 20,000 (you may of course - // change this limit, if you need - // more -- or less -- accuracy from - // your program). + // Then start a loop which only + // terminates once the number of + // degrees of freedom is larger + // than 20,000 (you may of course + // change this limit, if you need + // more -- or less -- accuracy from + // your program). for (unsigned int step=0; true; ++step) { - // Then give the alive - // indication for this - // iteration. Note that the - // std::flush is needed to - // have the text actually - // appear on the screen, rather - // than only in some buffer - // that is only flushed the - // next time we issue an - // end-line. + // Then give the alive + // indication for this + // iteration. Note that the + // std::flush is needed to + // have the text actually + // appear on the screen, rather + // than only in some buffer + // that is only flushed the + // next time we issue an + // end-line. std::cout << step << " " << std::flush; - // Now solve the problem on the - // present grid, and run the - // evaluators on it. The long - // type name of iterators into - // the list is a little - // annoying, but could be - // shortened by a typedef, if - // so desired. + // Now solve the problem on the + // present grid, and run the + // evaluators on it. The long + // type name of iterators into + // the list is a little + // annoying, but could be + // shortened by a typedef, if + // so desired. solver.solve_problem (); for (typename std::list *>::const_iterator - i = postprocessor_list.begin(); + i = postprocessor_list.begin(); i != postprocessor_list.end(); ++i) { (*i)->set_refinement_cycle (step); @@ -1925,58 +1925,58 @@ namespace Step13 }; - // Now check whether more - // iterations are required, or - // whether the loop shall be - // ended: + // Now check whether more + // iterations are required, or + // whether the loop shall be + // ended: if (solver.n_dofs() < 20000) solver.refine_grid (); else break; }; - // Finally end the line in which we - // displayed status reports: + // Finally end the line in which we + // displayed status reports: std::cout << std::endl; } - // The final function is one which - // takes the name of a solver - // (presently "kelly" and "global" - // are allowed), creates a solver - // object out of it using a coarse - // grid (in this case the ubiquitous - // unit square) and a finite element - // object (here the likewise - // ubiquitous bilinear one), and uses - // that solver to ask for the - // solution of the problem on a - // sequence of successively refined - // grids. - // - // The function also sets up two of - // evaluation functions, one - // evaluating the solution at the - // point (0.5,0.5), the other writing - // out the solution to a file. + // The final function is one which + // takes the name of a solver + // (presently "kelly" and "global" + // are allowed), creates a solver + // object out of it using a coarse + // grid (in this case the ubiquitous + // unit square) and a finite element + // object (here the likewise + // ubiquitous bilinear one), and uses + // that solver to ask for the + // solution of the problem on a + // sequence of successively refined + // grids. + // + // The function also sets up two of + // evaluation functions, one + // evaluating the solution at the + // point (0.5,0.5), the other writing + // out the solution to a file. template void solve_problem (const std::string &solver_name) { - // First minor task: tell the user - // what is going to happen. Thus - // write a header line, and a line - // with all '-' characters of the - // same length as the first one - // right below. + // First minor task: tell the user + // what is going to happen. Thus + // write a header line, and a line + // with all '-' characters of the + // same length as the first one + // right below. const std::string header = "Running tests with \"" + solver_name + "\" refinement criterion:"; std::cout << header << std::endl << std::string (header.size(), '-') << std::endl; - // Then set up triangulation, - // finite element, etc. + // Then set up triangulation, + // finite element, etc. Triangulation triangulation; GridGenerator::hyper_cube (triangulation, -1, 1); triangulation.refine_global (2); @@ -1985,12 +1985,12 @@ namespace Step13 const RightHandSide rhs_function; const Solution boundary_values; - // Create a solver object of the - // kind indicated by the argument - // to this function. If the name is - // not recognized, throw an - // exception! - LaplaceSolver::Base * solver = 0; + // Create a solver object of the + // kind indicated by the argument + // to this function. If the name is + // not recognized, throw an + // exception! + LaplaceSolver::Base *solver = 0; if (solver_name == "global") solver = new LaplaceSolver::RefinementGlobal (triangulation, fe, quadrature, @@ -2004,58 +2004,58 @@ namespace Step13 else AssertThrow (false, ExcNotImplemented()); - // Next create a table object in - // which the values of the - // numerical solution at the point - // (0.5,0.5) will be stored, and - // create a respective evaluation - // object: + // Next create a table object in + // which the values of the + // numerical solution at the point + // (0.5,0.5) will be stored, and + // create a respective evaluation + // object: TableHandler results_table; Evaluation::PointValueEvaluation - postprocessor1 (Point(0.5,0.5), results_table); + postprocessor1 (Point(0.5,0.5), results_table); - // Also generate an evaluator which - // writes out the solution: + // Also generate an evaluator which + // writes out the solution: Evaluation::SolutionOutput - postprocessor2 (std::string("solution-")+solver_name, - DataOut::gnuplot); + postprocessor2 (std::string("solution-")+solver_name, + DataOut::gnuplot); - // Take these two evaluation - // objects and put them in a - // list... + // Take these two evaluation + // objects and put them in a + // list... std::list *> postprocessor_list; postprocessor_list.push_back (&postprocessor1); postprocessor_list.push_back (&postprocessor2); - // ... which we can then pass on to - // the function that actually runs - // the simulation on successively - // refined grids: + // ... which we can then pass on to + // the function that actually runs + // the simulation on successively + // refined grids: run_simulation (*solver, postprocessor_list); - // When this all is done, write out - // the results of the point - // evaluations, and finally delete - // the solver object: + // When this all is done, write out + // the results of the point + // evaluations, and finally delete + // the solver object: results_table.write_text (std::cout); delete solver; - // And one blank line after all - // results: + // And one blank line after all + // results: std::cout << std::endl; } } - // There is not much to say about the - // main function. It follows the same - // pattern as in all previous - // examples, with attempts to catch - // thrown exceptions, and displaying - // as much information as possible if - // we should get some. The rest is - // self-explanatory. +// There is not much to say about the +// main function. It follows the same +// pattern as in all previous +// examples, with attempts to catch +// thrown exceptions, and displaying +// as much information as possible if +// we should get some. The rest is +// self-explanatory. int main () { try diff --git a/deal.II/examples/step-14/step-14.cc b/deal.II/examples/step-14/step-14.cc index 4a6185ee26..c1d4613872 100644 --- a/deal.II/examples/step-14/step-14.cc +++ b/deal.II/examples/step-14/step-14.cc @@ -10,7 +10,7 @@ /* further information on this license. */ - // Start out with well known things... +// Start out with well known things... #include #include #include @@ -45,42 +45,42 @@ #include #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step14 { using namespace dealii; - // @sect3{Evaluating the solution} - - // As mentioned in the introduction, - // significant parts of the program - // have simply been taken over from - // the step-13 example program. We - // therefore only comment on those - // things that are new. - // - // First, the framework for - // evaluation of solutions is - // unchanged, i.e. the base class is - // the same, and the class to - // evaluate the solution at a grid - // point is unchanged: + // @sect3{Evaluating the solution} + + // As mentioned in the introduction, + // significant parts of the program + // have simply been taken over from + // the step-13 example program. We + // therefore only comment on those + // things that are new. + // + // First, the framework for + // evaluation of solutions is + // unchanged, i.e. the base class is + // the same, and the class to + // evaluate the solution at a grid + // point is unchanged: namespace Evaluation { - // @sect4{The EvaluationBase class} + // @sect4{The EvaluationBase class} template class EvaluationBase { - public: - virtual ~EvaluationBase (); + public: + virtual ~EvaluationBase (); - void set_refinement_cycle (const unsigned int refinement_cycle); + void set_refinement_cycle (const unsigned int refinement_cycle); - virtual void operator () (const DoFHandler &dof_handler, - const Vector &solution) const = 0; - protected: - unsigned int refinement_cycle; + virtual void operator () (const DoFHandler &dof_handler, + const Vector &solution) const = 0; + protected: + unsigned int refinement_cycle; }; @@ -98,30 +98,30 @@ namespace Step14 } - // @sect4{The PointValueEvaluation class} + // @sect4{The PointValueEvaluation class} template class PointValueEvaluation : public EvaluationBase { - public: - PointValueEvaluation (const Point &evaluation_point); + public: + PointValueEvaluation (const Point &evaluation_point); - virtual void operator () (const DoFHandler &dof_handler, - const Vector &solution) const; + virtual void operator () (const DoFHandler &dof_handler, + const Vector &solution) const; - DeclException1 (ExcEvaluationPointNotFound, - Point, - << "The evaluation point " << arg1 - << " was not found among the vertices of the present grid."); - private: - const Point evaluation_point; + DeclException1 (ExcEvaluationPointNotFound, + Point, + << "The evaluation point " << arg1 + << " was not found among the vertices of the present grid."); + private: + const Point evaluation_point; }; template PointValueEvaluation:: PointValueEvaluation (const Point &evaluation_point) - : - evaluation_point (evaluation_point) + : + evaluation_point (evaluation_point) {} @@ -135,8 +135,8 @@ namespace Step14 double point_value = 1e20; typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); bool evaluation_point_found = false; for (; (cell!=endc) && !evaluation_point_found; ++cell) for (unsigned int vertex=0; @@ -160,89 +160,89 @@ namespace Step14 } - // @sect4{The PointXDerivativeEvaluation class} - - // Besides the class implementing - // the evaluation of the solution - // at one point, we here provide - // one which evaluates the gradient - // at a grid point. Since in - // general the gradient of a finite - // element function is not - // continuous at a vertex, we have - // to be a little bit more careful - // here. What we do is to loop over - // all cells, even if we have found - // the point already on one cell, - // and use the mean value of the - // gradient at the vertex taken - // from all adjacent cells. - // - // Given the interface of the - // PointValueEvaluation class, - // the declaration of this class - // provides little surprise, and - // neither does the constructor: + // @sect4{The PointXDerivativeEvaluation class} + + // Besides the class implementing + // the evaluation of the solution + // at one point, we here provide + // one which evaluates the gradient + // at a grid point. Since in + // general the gradient of a finite + // element function is not + // continuous at a vertex, we have + // to be a little bit more careful + // here. What we do is to loop over + // all cells, even if we have found + // the point already on one cell, + // and use the mean value of the + // gradient at the vertex taken + // from all adjacent cells. + // + // Given the interface of the + // PointValueEvaluation class, + // the declaration of this class + // provides little surprise, and + // neither does the constructor: template class PointXDerivativeEvaluation : public EvaluationBase { - public: - PointXDerivativeEvaluation (const Point &evaluation_point); + public: + PointXDerivativeEvaluation (const Point &evaluation_point); - virtual void operator () (const DoFHandler &dof_handler, - const Vector &solution) const; + virtual void operator () (const DoFHandler &dof_handler, + const Vector &solution) const; - DeclException1 (ExcEvaluationPointNotFound, - Point, - << "The evaluation point " << arg1 - << " was not found among the vertices of the present grid."); - private: - const Point evaluation_point; + DeclException1 (ExcEvaluationPointNotFound, + Point, + << "The evaluation point " << arg1 + << " was not found among the vertices of the present grid."); + private: + const Point evaluation_point; }; template PointXDerivativeEvaluation:: PointXDerivativeEvaluation (const Point &evaluation_point) - : - evaluation_point (evaluation_point) + : + evaluation_point (evaluation_point) {} - // The more interesting things - // happen inside the function doing - // the actual evaluation: + // The more interesting things + // happen inside the function doing + // the actual evaluation: template void PointXDerivativeEvaluation:: operator () (const DoFHandler &dof_handler, const Vector &solution) const { - // This time initialize the - // return value with something - // useful, since we will have to - // add up a number of - // contributions and take the - // mean value afterwards... + // This time initialize the + // return value with something + // useful, since we will have to + // add up a number of + // contributions and take the + // mean value afterwards... double point_derivative = 0; - // ...then have some objects of - // which the meaning wil become - // clear below... + // ...then have some objects of + // which the meaning wil become + // clear below... QTrapez vertex_quadrature; FEValues fe_values (dof_handler.get_fe(), vertex_quadrature, update_gradients | update_quadrature_points); std::vector > - solution_gradients (vertex_quadrature.size()); + solution_gradients (vertex_quadrature.size()); - // ...and next loop over all cells - // and their vertices, and count - // how often the vertex has been - // found: + // ...and next loop over all cells + // and their vertices, and count + // how often the vertex has been + // found: typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); unsigned int evaluation_point_hits = 0; for (; cell!=endc; ++cell) for (unsigned int vertex=0; @@ -250,120 +250,120 @@ namespace Step14 ++vertex) if (cell->vertex(vertex) == evaluation_point) { - // Things are now no more - // as simple, since we - // can't get the gradient - // of the finite element - // field as before, where - // we simply had to pick - // one degree of freedom - // at a vertex. - // - // Rather, we have to - // evaluate the finite - // element field on this - // cell, and at a certain - // point. As you know, - // evaluating finite - // element fields at - // certain points is done - // through the - // FEValues class, so - // we use that. The - // question is: the - // FEValues object - // needs to be a given a - // quadrature formula and - // can then compute the - // values of finite - // element quantities at - // the quadrature - // points. Here, we don't - // want to do quadrature, - // we simply want to - // specify some points! - // - // Nevertheless, the same - // way is chosen: use a - // special quadrature - // rule with points at - // the vertices, since - // these are what we are - // interested in. The - // appropriate rule is - // the trapezoidal rule, - // so that is the reason - // why we used that one - // above. - // - // Thus: initialize the - // FEValues object on - // this cell, + // Things are now no more + // as simple, since we + // can't get the gradient + // of the finite element + // field as before, where + // we simply had to pick + // one degree of freedom + // at a vertex. + // + // Rather, we have to + // evaluate the finite + // element field on this + // cell, and at a certain + // point. As you know, + // evaluating finite + // element fields at + // certain points is done + // through the + // FEValues class, so + // we use that. The + // question is: the + // FEValues object + // needs to be a given a + // quadrature formula and + // can then compute the + // values of finite + // element quantities at + // the quadrature + // points. Here, we don't + // want to do quadrature, + // we simply want to + // specify some points! + // + // Nevertheless, the same + // way is chosen: use a + // special quadrature + // rule with points at + // the vertices, since + // these are what we are + // interested in. The + // appropriate rule is + // the trapezoidal rule, + // so that is the reason + // why we used that one + // above. + // + // Thus: initialize the + // FEValues object on + // this cell, fe_values.reinit (cell); - // and extract the - // gradients of the - // solution vector at the - // vertices: + // and extract the + // gradients of the + // solution vector at the + // vertices: fe_values.get_function_grads (solution, solution_gradients); - // Now we have the - // gradients at all - // vertices, so pick out - // that one which belongs - // to the evaluation - // point (note that the - // order of vertices is - // not necessarily the - // same as that of the - // quadrature points): + // Now we have the + // gradients at all + // vertices, so pick out + // that one which belongs + // to the evaluation + // point (note that the + // order of vertices is + // not necessarily the + // same as that of the + // quadrature points): unsigned int q_point = 0; for (; q_point 0, ExcEvaluationPointNotFound(evaluation_point)); - // We have simply summed up the - // contributions of all adjacent - // cells, so we still have to - // compute the mean value. Once - // this is done, report the status: + // We have simply summed up the + // contributions of all adjacent + // cells, so we still have to + // compute the mean value. Once + // this is done, report the status: point_derivative /= evaluation_point_hits; std::cout << " Point x-derivative=" << point_derivative << std::endl; @@ -371,49 +371,49 @@ namespace Step14 - // @sect4{The GridOutput class} - - // Since this program has a more - // difficult structure (it computed - // a dual solution in addition to a - // primal one), writing out the - // solution is no more done by an - // evaluation object since we want - // to write both solutions at once - // into one file, and that requires - // some more information than - // available to the evaluation - // classes. - // - // However, we also want to look at - // the grids generated. This again - // can be done with one such - // class. Its structure is analog - // to the SolutionOutput class - // of the previous example program, - // so we do not discuss it here in - // more detail. Furthermore, - // everything that is used here has - // already been used in previous - // example programs. + // @sect4{The GridOutput class} + + // Since this program has a more + // difficult structure (it computed + // a dual solution in addition to a + // primal one), writing out the + // solution is no more done by an + // evaluation object since we want + // to write both solutions at once + // into one file, and that requires + // some more information than + // available to the evaluation + // classes. + // + // However, we also want to look at + // the grids generated. This again + // can be done with one such + // class. Its structure is analog + // to the SolutionOutput class + // of the previous example program, + // so we do not discuss it here in + // more detail. Furthermore, + // everything that is used here has + // already been used in previous + // example programs. template class GridOutput : public EvaluationBase { - public: - GridOutput (const std::string &output_name_base); + public: + GridOutput (const std::string &output_name_base); - virtual void operator () (const DoFHandler &dof_handler, - const Vector &solution) const; - private: - const std::string output_name_base; + virtual void operator () (const DoFHandler &dof_handler, + const Vector &solution) const; + private: + const std::string output_name_base; }; template GridOutput:: GridOutput (const std::string &output_name_base) - : - output_name_base (output_name_base) + : + output_name_base (output_name_base) {} @@ -434,68 +434,68 @@ namespace Step14 } - // @sect3{The Laplace solver classes} + // @sect3{The Laplace solver classes} - // Next are the actual solver - // classes. Again, we discuss only - // the differences to the previous - // program. + // Next are the actual solver + // classes. Again, we discuss only + // the differences to the previous + // program. namespace LaplaceSolver { - // Before everything else, - // forward-declare one class that - // we will have later, since we - // will want to make it a friend of - // some of the classes that follow, - // which requires the class to be - // known: + // Before everything else, + // forward-declare one class that + // we will have later, since we + // will want to make it a friend of + // some of the classes that follow, + // which requires the class to be + // known: template class WeightedResidual; - // @sect4{The Laplace solver base class} - - // This class is almost unchanged, - // with the exception that it - // declares two more functions: - // output_solution will be used - // to generate output files from - // the actual solutions computed by - // derived classes, and the - // set_refinement_cycle - // function by which the testing - // framework sets the number of the - // refinement cycle to a local - // variable in this class; this - // number is later used to generate - // filenames for the solution - // output. + // @sect4{The Laplace solver base class} + + // This class is almost unchanged, + // with the exception that it + // declares two more functions: + // output_solution will be used + // to generate output files from + // the actual solutions computed by + // derived classes, and the + // set_refinement_cycle + // function by which the testing + // framework sets the number of the + // refinement cycle to a local + // variable in this class; this + // number is later used to generate + // filenames for the solution + // output. template class Base { - public: - Base (Triangulation &coarse_grid); - virtual ~Base (); + public: + Base (Triangulation &coarse_grid); + virtual ~Base (); - virtual void solve_problem () = 0; - virtual void postprocess (const Evaluation::EvaluationBase &postprocessor) const = 0; - virtual void refine_grid () = 0; - virtual unsigned int n_dofs () const = 0; + virtual void solve_problem () = 0; + virtual void postprocess (const Evaluation::EvaluationBase &postprocessor) const = 0; + virtual void refine_grid () = 0; + virtual unsigned int n_dofs () const = 0; - virtual void set_refinement_cycle (const unsigned int cycle); + virtual void set_refinement_cycle (const unsigned int cycle); - virtual void output_solution () const = 0; + virtual void output_solution () const = 0; - protected: - const SmartPointer > triangulation; + protected: + const SmartPointer > triangulation; - unsigned int refinement_cycle; + unsigned int refinement_cycle; }; template Base::Base (Triangulation &coarse_grid) - : - triangulation (&coarse_grid) + : + triangulation (&coarse_grid) {} @@ -513,66 +513,66 @@ namespace Step14 } - // @sect4{The Laplace Solver class} + // @sect4{The Laplace Solver class} - // Likewise, the Solver class - // is entirely unchanged and will - // thus not be discussed. + // Likewise, the Solver class + // is entirely unchanged and will + // thus not be discussed. template class Solver : public virtual Base { - public: - Solver (Triangulation &triangulation, - const FiniteElement &fe, - const Quadrature &quadrature, - const Quadrature &face_quadrature, - const Function &boundary_values); - virtual - ~Solver (); - - virtual - void - solve_problem (); - - virtual - void - postprocess (const Evaluation::EvaluationBase &postprocessor) const; - - virtual - unsigned int - n_dofs () const; - - protected: - const SmartPointer > fe; - const SmartPointer > quadrature; - const SmartPointer > face_quadrature; - DoFHandler dof_handler; - Vector solution; - const SmartPointer > boundary_values; - - virtual void assemble_rhs (Vector &rhs) const = 0; - - private: - struct LinearSystem - { - LinearSystem (const DoFHandler &dof_handler); + public: + Solver (Triangulation &triangulation, + const FiniteElement &fe, + const Quadrature &quadrature, + const Quadrature &face_quadrature, + const Function &boundary_values); + virtual + ~Solver (); + + virtual + void + solve_problem (); + + virtual + void + postprocess (const Evaluation::EvaluationBase &postprocessor) const; + + virtual + unsigned int + n_dofs () const; + + protected: + const SmartPointer > fe; + const SmartPointer > quadrature; + const SmartPointer > face_quadrature; + DoFHandler dof_handler; + Vector solution; + const SmartPointer > boundary_values; + + virtual void assemble_rhs (Vector &rhs) const = 0; + + private: + struct LinearSystem + { + LinearSystem (const DoFHandler &dof_handler); - void solve (Vector &solution) const; + void solve (Vector &solution) const; - ConstraintMatrix hanging_node_constraints; - SparsityPattern sparsity_pattern; - SparseMatrix matrix; - Vector rhs; - }; + ConstraintMatrix hanging_node_constraints; + SparsityPattern sparsity_pattern; + SparseMatrix matrix; + Vector rhs; + }; - void - assemble_linear_system (LinearSystem &linear_system); + void + assemble_linear_system (LinearSystem &linear_system); - void - assemble_matrix (LinearSystem &linear_system, - const typename DoFHandler::active_cell_iterator &begin_cell, - const typename DoFHandler::active_cell_iterator &end_cell, - Threads::ThreadMutex &mutex) const; + void + assemble_matrix (LinearSystem &linear_system, + const typename DoFHandler::active_cell_iterator &begin_cell, + const typename DoFHandler::active_cell_iterator &end_cell, + Threads::ThreadMutex &mutex) const; }; @@ -583,13 +583,13 @@ namespace Step14 const Quadrature &quadrature, const Quadrature &face_quadrature, const Function &boundary_values) - : - Base (triangulation), - fe (&fe), - quadrature (&quadrature), - face_quadrature (&face_quadrature), - dof_handler (triangulation), - boundary_values (&boundary_values) + : + Base (triangulation), + fe (&fe), + quadrature (&quadrature), + face_quadrature (&face_quadrature), + dof_handler (triangulation), + boundary_values (&boundary_values) {} @@ -635,12 +635,12 @@ namespace Step14 Solver::assemble_linear_system (LinearSystem &linear_system) { typedef - typename DoFHandler::active_cell_iterator - active_cell_iterator; + typename DoFHandler::active_cell_iterator + active_cell_iterator; const unsigned int n_threads = multithread_info.n_default_threads; std::vector > - thread_ranges + thread_ranges = Threads::split_range (dof_handler.begin_active (), dof_handler.end (), n_threads); @@ -724,13 +724,13 @@ namespace Step14 hanging_node_constraints.clear (); void (*mhnc_p) (const DoFHandler &, - ConstraintMatrix &) + ConstraintMatrix &) = &DoFTools::make_hanging_node_constraints; Threads::Thread<> - mhnc_thread = Threads::new_thread (mhnc_p, - dof_handler, - hanging_node_constraints); + mhnc_thread = Threads::new_thread (mhnc_p, + dof_handler, + hanging_node_constraints); sparsity_pattern.reinit (dof_handler.n_dofs(), dof_handler.n_dofs(), @@ -766,103 +766,103 @@ namespace Step14 - // @sect4{The PrimalSolver class} - - // The PrimalSolver class is - // also mostly unchanged except for - // overloading the functions - // solve_problem, n_dofs, - // and postprocess of the base - // class, and implementing the - // output_solution - // function. These overloaded - // functions do nothing particular - // besides calling the functions of - // the base class -- that seems - // superfluous, but works around a - // bug in a popular compiler which - // requires us to write such - // functions for the following - // scenario: Besides the - // PrimalSolver class, we will - // have a DualSolver, both - // derived from Solver. We will - // then have a final classes which - // derived from these two, which - // will then have two instances of - // the Solver class as its base - // classes. If we want, for - // example, the number of degrees - // of freedom of the primal solver, - // we would have to indicate this - // like so: - // PrimalSolver::n_dofs(). - // However, the compiler does not - // accept this since the n_dofs - // function is actually from a base - // class of the PrimalSolver - // class, so we have to inject the - // name from the base to the - // derived class using these - // additional functions. - // - // Regarding the implementation of - // the output_solution - // function, we keep the - // GlobalRefinement and - // RefinementKelly classes in - // this program, and they can then - // rely on the default - // implementation of this function - // which simply outputs the primal - // solution. The class implementing - // dual weighted error estimators - // will overload this function - // itself, to also output the dual - // solution. - // - // Except for this, the class is - // unchanged with respect to the - // previous example. + // @sect4{The PrimalSolver class} + + // The PrimalSolver class is + // also mostly unchanged except for + // overloading the functions + // solve_problem, n_dofs, + // and postprocess of the base + // class, and implementing the + // output_solution + // function. These overloaded + // functions do nothing particular + // besides calling the functions of + // the base class -- that seems + // superfluous, but works around a + // bug in a popular compiler which + // requires us to write such + // functions for the following + // scenario: Besides the + // PrimalSolver class, we will + // have a DualSolver, both + // derived from Solver. We will + // then have a final classes which + // derived from these two, which + // will then have two instances of + // the Solver class as its base + // classes. If we want, for + // example, the number of degrees + // of freedom of the primal solver, + // we would have to indicate this + // like so: + // PrimalSolver::n_dofs(). + // However, the compiler does not + // accept this since the n_dofs + // function is actually from a base + // class of the PrimalSolver + // class, so we have to inject the + // name from the base to the + // derived class using these + // additional functions. + // + // Regarding the implementation of + // the output_solution + // function, we keep the + // GlobalRefinement and + // RefinementKelly classes in + // this program, and they can then + // rely on the default + // implementation of this function + // which simply outputs the primal + // solution. The class implementing + // dual weighted error estimators + // will overload this function + // itself, to also output the dual + // solution. + // + // Except for this, the class is + // unchanged with respect to the + // previous example. template class PrimalSolver : public Solver { - public: - PrimalSolver (Triangulation &triangulation, - const FiniteElement &fe, - const Quadrature &quadrature, - const Quadrature &face_quadrature, - const Function &rhs_function, - const Function &boundary_values); - - virtual - void solve_problem (); - - virtual - unsigned int n_dofs () const; - - virtual - void postprocess (const Evaluation::EvaluationBase &postprocessor) const; - - virtual - void output_solution () const; - - protected: - const SmartPointer > rhs_function; - virtual void assemble_rhs (Vector &rhs) const; - - // Now, in order to work around - // some problems in one of the - // compilers this library can - // be compiled with, we will - // have to declare a - // class that is actually - // derived from the present - // one, as a friend (strange as - // that seems). The full - // rationale will be explained - // below. - friend class WeightedResidual; + public: + PrimalSolver (Triangulation &triangulation, + const FiniteElement &fe, + const Quadrature &quadrature, + const Quadrature &face_quadrature, + const Function &rhs_function, + const Function &boundary_values); + + virtual + void solve_problem (); + + virtual + unsigned int n_dofs () const; + + virtual + void postprocess (const Evaluation::EvaluationBase &postprocessor) const; + + virtual + void output_solution () const; + + protected: + const SmartPointer > rhs_function; + virtual void assemble_rhs (Vector &rhs) const; + + // Now, in order to work around + // some problems in one of the + // compilers this library can + // be compiled with, we will + // have to declare a + // class that is actually + // derived from the present + // one, as a friend (strange as + // that seems). The full + // rationale will be explained + // below. + friend class WeightedResidual; }; @@ -874,12 +874,12 @@ namespace Step14 const Quadrature &face_quadrature, const Function &rhs_function, const Function &boundary_values) - : - Base (triangulation), - Solver (triangulation, fe, - quadrature, face_quadrature, - boundary_values), - rhs_function (&rhs_function) + : + Base (triangulation), + Solver (triangulation, fe, + quadrature, face_quadrature, + boundary_values), + rhs_function (&rhs_function) {} @@ -947,8 +947,8 @@ namespace Step14 std::vector local_dof_indices (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = this->dof_handler.begin_active(), - endc = this->dof_handler.end(); + cell = this->dof_handler.begin_active(), + endc = this->dof_handler.end(); for (; cell!=endc; ++cell) { cell_rhs = 0; @@ -971,24 +971,24 @@ namespace Step14 } - // @sect4{The RefinementGlobal and RefinementKelly classes} + // @sect4{The RefinementGlobal and RefinementKelly classes} - // For the following two classes, - // the same applies as for most of - // the above: the class is taken - // from the previous example as-is: + // For the following two classes, + // the same applies as for most of + // the above: the class is taken + // from the previous example as-is: template class RefinementGlobal : public PrimalSolver { - public: - RefinementGlobal (Triangulation &coarse_grid, - const FiniteElement &fe, - const Quadrature &quadrature, - const Quadrature &face_quadrature, - const Function &rhs_function, - const Function &boundary_values); - - virtual void refine_grid (); + public: + RefinementGlobal (Triangulation &coarse_grid, + const FiniteElement &fe, + const Quadrature &quadrature, + const Quadrature &face_quadrature, + const Function &rhs_function, + const Function &boundary_values); + + virtual void refine_grid (); }; @@ -1001,11 +1001,11 @@ namespace Step14 const Quadrature &face_quadrature, const Function &rhs_function, const Function &boundary_values) - : - Base (coarse_grid), - PrimalSolver (coarse_grid, fe, quadrature, - face_quadrature, rhs_function, - boundary_values) + : + Base (coarse_grid), + PrimalSolver (coarse_grid, fe, quadrature, + face_quadrature, rhs_function, + boundary_values) {} @@ -1022,15 +1022,15 @@ namespace Step14 template class RefinementKelly : public PrimalSolver { - public: - RefinementKelly (Triangulation &coarse_grid, - const FiniteElement &fe, - const Quadrature &quadrature, - const Quadrature &face_quadrature, - const Function &rhs_function, - const Function &boundary_values); - - virtual void refine_grid (); + public: + RefinementKelly (Triangulation &coarse_grid, + const FiniteElement &fe, + const Quadrature &quadrature, + const Quadrature &face_quadrature, + const Function &rhs_function, + const Function &boundary_values); + + virtual void refine_grid (); }; @@ -1043,11 +1043,11 @@ namespace Step14 const Quadrature &face_quadrature, const Function &rhs_function, const Function &boundary_values) - : - Base (coarse_grid), - PrimalSolver (coarse_grid, fe, quadrature, - face_quadrature, - rhs_function, boundary_values) + : + Base (coarse_grid), + PrimalSolver (coarse_grid, fe, quadrature, + face_quadrature, + rhs_function, boundary_values) {} @@ -1070,46 +1070,46 @@ namespace Step14 - // @sect4{The RefinementWeightedKelly class} - - // This class is a variant of the - // previous one, in that it allows - // to weight the refinement - // indicators we get from the - // library's Kelly indicator by - // some function. We include this - // class since the goal of this - // example program is to - // demonstrate automatic refinement - // criteria even for complex output - // quantities such as point values - // or stresses. If we did not solve - // a dual problem and compute the - // weights thereof, we would - // probably be tempted to give a - // hand-crafted weighting to the - // indicators to account for the - // fact that we are going to - // evaluate these quantities. This - // class accepts such a weighting - // function as argument to its - // constructor: + // @sect4{The RefinementWeightedKelly class} + + // This class is a variant of the + // previous one, in that it allows + // to weight the refinement + // indicators we get from the + // library's Kelly indicator by + // some function. We include this + // class since the goal of this + // example program is to + // demonstrate automatic refinement + // criteria even for complex output + // quantities such as point values + // or stresses. If we did not solve + // a dual problem and compute the + // weights thereof, we would + // probably be tempted to give a + // hand-crafted weighting to the + // indicators to account for the + // fact that we are going to + // evaluate these quantities. This + // class accepts such a weighting + // function as argument to its + // constructor: template class RefinementWeightedKelly : public PrimalSolver { - public: - RefinementWeightedKelly (Triangulation &coarse_grid, - const FiniteElement &fe, - const Quadrature &quadrature, - const Quadrature &face_quadrature, - const Function &rhs_function, - const Function &boundary_values, - const Function &weighting_function); - - virtual void refine_grid (); - - private: - const SmartPointer > weighting_function; + public: + RefinementWeightedKelly (Triangulation &coarse_grid, + const FiniteElement &fe, + const Quadrature &quadrature, + const Quadrature &face_quadrature, + const Function &rhs_function, + const Function &boundary_values, + const Function &weighting_function); + + virtual void refine_grid (); + + private: + const SmartPointer > weighting_function; }; @@ -1123,30 +1123,30 @@ namespace Step14 const Function &rhs_function, const Function &boundary_values, const Function &weighting_function) - : - Base (coarse_grid), - PrimalSolver (coarse_grid, fe, quadrature, - face_quadrature, - rhs_function, boundary_values), - weighting_function (&weighting_function) + : + Base (coarse_grid), + PrimalSolver (coarse_grid, fe, quadrature, + face_quadrature, + rhs_function, boundary_values), + weighting_function (&weighting_function) {} - // Now, here comes the main - // function, including the - // weighting: + // Now, here comes the main + // function, including the + // weighting: template void RefinementWeightedKelly::refine_grid () { - // First compute some residual - // based error indicators for all - // cells by a method already - // implemented in the - // library. What exactly is - // computed can be read in the - // documentation of that class. + // First compute some residual + // based error indicators for all + // cells by a method already + // implemented in the + // library. What exactly is + // computed can be read in the + // documentation of that class. Vector estimated_error (this->triangulation->n_active_cells()); KellyErrorEstimator::estimate (this->dof_handler, *this->face_quadrature, @@ -1154,16 +1154,16 @@ namespace Step14 this->solution, estimated_error); - // Now we are going to weight - // these indicators by the value - // of the function given to the - // constructor: + // Now we are going to weight + // these indicators by the value + // of the function given to the + // constructor: typename DoFHandler::active_cell_iterator - cell = this->dof_handler.begin_active(), - endc = this->dof_handler.end(); + cell = this->dof_handler.begin_active(), + endc = this->dof_handler.end(); for (unsigned int cell_index=0; cell!=endc; ++cell, ++cell_index) estimated_error(cell_index) - *= weighting_function->value (cell->center()); + *= weighting_function->value (cell->center()); GridRefinement::refine_and_coarsen_fixed_number (*this->triangulation, estimated_error, @@ -1174,205 +1174,205 @@ namespace Step14 } - // @sect3{Equation data} - // - // In this example program, we work - // with the same data sets as in the - // previous one, but as it may so - // happen that someone wants to run - // the program with different - // boundary values and right hand side - // functions, or on a different grid, - // we show a simple technique to do - // exactly that. For more clarity, we - // furthermore pack everything that - // has to do with equation data into - // a namespace of its own. - // - // The underlying assumption is that - // this is a research program, and - // that there we often have a number - // of test cases that consist of a - // domain, a right hand side, - // boundary values, possibly a - // specified coefficient, and a - // number of other parameters. They - // often vary all at the same time - // when shifting from one example to - // another. To make handling such - // sets of problem description - // parameters simple is the goal of - // the following. - // - // Basically, the idea is this: let - // us have a structure for each set - // of data, in which we pack - // everything that describes a test - // case: here, these are two - // subclasses, one called - // BoundaryValues for the - // boundary values of the exact - // solution, and one called - // RightHandSide, and then a way - // to generate the coarse grid. Since - // the solution of the previous - // example program looked like curved - // ridges, we use this name here for - // the enclosing class. Note that the - // names of the two inner classes - // have to be the same for all - // enclosing test case classes, and - // also that we have attached the - // dimension template argument to the - // enclosing class rather than to the - // inner ones, to make further - // processing simpler. (From a - // language viewpoint, a namespace - // would be better to encapsulate - // these inner classes, rather than a - // structure. However, namespaces - // cannot be given as template - // arguments, so we use a structure - // to allow a second object to select - // from within its given - // argument. The enclosing structure, - // of course, has no member variables - // apart from the classes it - // declares, and a static function to - // generate the coarse mesh; it will - // in general never be instantiated.) - // - // The idea is then the following - // (this is the right time to also - // take a brief look at the code - // below): we can generate objects - // for boundary values and - // right hand side by simply giving - // the name of the outer class as a - // template argument to a class which - // we call here Data::SetUp, and - // it then creates objects for the - // inner classes. In this case, to - // get all that characterizes the - // curved ridge solution, we would - // simply generate an instance of - // Data::SetUp@, - // and everything we need to know - // about the solution would be static - // member variables and functions of - // that object. - // - // This approach might seem like - // overkill in this case, but will - // become very handy once a certain - // set up is not only characterized - // by Dirichlet boundary values and a - // right hand side function, but in - // addition by material properties, - // Neumann values, different boundary - // descriptors, etc. In that case, - // the SetUp class might consist - // of a dozen or more objects, and - // each descriptor class (like the - // CurvedRidges class below) - // would have to provide them. Then, - // you will be happy to be able to - // change from one set of data to - // another by only changing the - // template argument to the SetUp - // class at one place, rather than at - // many. - // - // With this framework for different - // test cases, we are almost - // finished, but one thing remains: - // by now we can select statically, - // by changing one template argument, - // which data set to choose. In order - // to be able to do that dynamically, - // i.e. at run time, we need a base - // class. This we provide in the - // obvious way, see below, with - // virtual abstract functions. It - // forces us to introduce a second - // template parameter dim which - // we need for the base class (which - // could be avoided using some - // template magic, but we omit that), - // but that's all. - // - // Adding new testcases is now - // simple, you don't have to touch - // the framework classes, only a - // structure like the - // CurvedRidges one is needed. + // @sect3{Equation data} + // + // In this example program, we work + // with the same data sets as in the + // previous one, but as it may so + // happen that someone wants to run + // the program with different + // boundary values and right hand side + // functions, or on a different grid, + // we show a simple technique to do + // exactly that. For more clarity, we + // furthermore pack everything that + // has to do with equation data into + // a namespace of its own. + // + // The underlying assumption is that + // this is a research program, and + // that there we often have a number + // of test cases that consist of a + // domain, a right hand side, + // boundary values, possibly a + // specified coefficient, and a + // number of other parameters. They + // often vary all at the same time + // when shifting from one example to + // another. To make handling such + // sets of problem description + // parameters simple is the goal of + // the following. + // + // Basically, the idea is this: let + // us have a structure for each set + // of data, in which we pack + // everything that describes a test + // case: here, these are two + // subclasses, one called + // BoundaryValues for the + // boundary values of the exact + // solution, and one called + // RightHandSide, and then a way + // to generate the coarse grid. Since + // the solution of the previous + // example program looked like curved + // ridges, we use this name here for + // the enclosing class. Note that the + // names of the two inner classes + // have to be the same for all + // enclosing test case classes, and + // also that we have attached the + // dimension template argument to the + // enclosing class rather than to the + // inner ones, to make further + // processing simpler. (From a + // language viewpoint, a namespace + // would be better to encapsulate + // these inner classes, rather than a + // structure. However, namespaces + // cannot be given as template + // arguments, so we use a structure + // to allow a second object to select + // from within its given + // argument. The enclosing structure, + // of course, has no member variables + // apart from the classes it + // declares, and a static function to + // generate the coarse mesh; it will + // in general never be instantiated.) + // + // The idea is then the following + // (this is the right time to also + // take a brief look at the code + // below): we can generate objects + // for boundary values and + // right hand side by simply giving + // the name of the outer class as a + // template argument to a class which + // we call here Data::SetUp, and + // it then creates objects for the + // inner classes. In this case, to + // get all that characterizes the + // curved ridge solution, we would + // simply generate an instance of + // Data::SetUp@, + // and everything we need to know + // about the solution would be static + // member variables and functions of + // that object. + // + // This approach might seem like + // overkill in this case, but will + // become very handy once a certain + // set up is not only characterized + // by Dirichlet boundary values and a + // right hand side function, but in + // addition by material properties, + // Neumann values, different boundary + // descriptors, etc. In that case, + // the SetUp class might consist + // of a dozen or more objects, and + // each descriptor class (like the + // CurvedRidges class below) + // would have to provide them. Then, + // you will be happy to be able to + // change from one set of data to + // another by only changing the + // template argument to the SetUp + // class at one place, rather than at + // many. + // + // With this framework for different + // test cases, we are almost + // finished, but one thing remains: + // by now we can select statically, + // by changing one template argument, + // which data set to choose. In order + // to be able to do that dynamically, + // i.e. at run time, we need a base + // class. This we provide in the + // obvious way, see below, with + // virtual abstract functions. It + // forces us to introduce a second + // template parameter dim which + // we need for the base class (which + // could be avoided using some + // template magic, but we omit that), + // but that's all. + // + // Adding new testcases is now + // simple, you don't have to touch + // the framework classes, only a + // structure like the + // CurvedRidges one is needed. namespace Data { - // @sect4{The SetUpBase and SetUp classes} + // @sect4{The SetUpBase and SetUp classes} - // Based on the above description, - // the SetUpBase class then - // looks as follows. To allow using - // the SmartPointer class with - // this class, we derived from the - // Subscriptor class. + // Based on the above description, + // the SetUpBase class then + // looks as follows. To allow using + // the SmartPointer class with + // this class, we derived from the + // Subscriptor class. template struct SetUpBase : public Subscriptor { - virtual - const Function & get_boundary_values () const = 0; + virtual + const Function &get_boundary_values () const = 0; - virtual - const Function & get_right_hand_side () const = 0; + virtual + const Function &get_right_hand_side () const = 0; - virtual - void create_coarse_grid (Triangulation &coarse_grid) const = 0; + virtual + void create_coarse_grid (Triangulation &coarse_grid) const = 0; }; - // And now for the derived class - // that takes the template argument - // as explained above. For some - // reason, C++ requires us to - // define a constructor (which - // maybe empty), as otherwise a - // warning is generated that some - // data is not initialized. - // - // Here we pack the data elements - // into private variables, and - // allow access to them through the - // methods of the base class. + // And now for the derived class + // that takes the template argument + // as explained above. For some + // reason, C++ requires us to + // define a constructor (which + // maybe empty), as otherwise a + // warning is generated that some + // data is not initialized. + // + // Here we pack the data elements + // into private variables, and + // allow access to them through the + // methods of the base class. template struct SetUp : public SetUpBase { - SetUp () {} + SetUp () {} - virtual - const Function & get_boundary_values () const; + virtual + const Function &get_boundary_values () const; - virtual - const Function & get_right_hand_side () const; + virtual + const Function &get_right_hand_side () const; - virtual - void create_coarse_grid (Triangulation &coarse_grid) const; + virtual + void create_coarse_grid (Triangulation &coarse_grid) const; - private: - static const typename Traits::BoundaryValues boundary_values; - static const typename Traits::RightHandSide right_hand_side; + private: + static const typename Traits::BoundaryValues boundary_values; + static const typename Traits::RightHandSide right_hand_side; }; - // We have to provide definitions - // for the static member variables - // of the above class: + // We have to provide definitions + // for the static member variables + // of the above class: template const typename Traits::BoundaryValues SetUp::boundary_values; template const typename Traits::RightHandSide SetUp::right_hand_side; - // And definitions of the member - // functions: + // And definitions of the member + // functions: template const Function & SetUp::get_boundary_values () const @@ -1398,39 +1398,39 @@ namespace Step14 } - // @sect4{The CurvedRidges class} + // @sect4{The CurvedRidges class} - // The class that is used to - // describe the boundary values and - // right hand side of the curved - // ridge problem already used in - // the step-13 example program is - // then like so: + // The class that is used to + // describe the boundary values and + // right hand side of the curved + // ridge problem already used in + // the step-13 example program is + // then like so: template struct CurvedRidges { - class BoundaryValues : public Function - { - public: - BoundaryValues () : Function () {} + class BoundaryValues : public Function + { + public: + BoundaryValues () : Function () {} - virtual double value (const Point &p, - const unsigned int component) const; - }; + virtual double value (const Point &p, + const unsigned int component) const; + }; - class RightHandSide : public Function - { - public: - RightHandSide () : Function () {} + class RightHandSide : public Function + { + public: + RightHandSide () : Function () {} - virtual double value (const Point &p, - const unsigned int component) const; - }; + virtual double value (const Point &p, + const unsigned int component) const; + }; - static - void - create_coarse_grid (Triangulation &coarse_grid); + static + void + create_coarse_grid (Triangulation &coarse_grid); }; @@ -1485,117 +1485,117 @@ namespace Step14 } - // @sect4{The Exercise_2_3 class} - - // This example program was written - // while giving practical courses - // for a lecture on adaptive finite - // element methods and duality - // based error estimates. For these - // courses, we had one exercise, - // which required to solve the - // Laplace equation with constant - // right hand side on a square - // domain with a square hole in the - // center, and zero boundary - // values. Since the implementation - // of the properties of this - // problem is so particularly - // simple here, lets do it. As the - // number of the exercise was 2.3, - // we take the liberty to retain - // this name for the class as well. + // @sect4{The Exercise_2_3 class} + + // This example program was written + // while giving practical courses + // for a lecture on adaptive finite + // element methods and duality + // based error estimates. For these + // courses, we had one exercise, + // which required to solve the + // Laplace equation with constant + // right hand side on a square + // domain with a square hole in the + // center, and zero boundary + // values. Since the implementation + // of the properties of this + // problem is so particularly + // simple here, lets do it. As the + // number of the exercise was 2.3, + // we take the liberty to retain + // this name for the class as well. template struct Exercise_2_3 { - // We need a class to denote - // the boundary values of the - // problem. In this case, this - // is simple: it's the zero - // function, so don't even - // declare a class, just a - // typedef: - typedef ZeroFunction BoundaryValues; - - // Second, a class that denotes - // the right hand side. Since - // they are constant, just - // subclass the corresponding - // class of the library and be - // done: - class RightHandSide : public ConstantFunction - { - public: - RightHandSide () : ConstantFunction (1.) {} - }; - - // Finally a function to - // generate the coarse - // grid. This is somewhat more - // complicated here, see - // immediately below. - static - void - create_coarse_grid (Triangulation &coarse_grid); + // We need a class to denote + // the boundary values of the + // problem. In this case, this + // is simple: it's the zero + // function, so don't even + // declare a class, just a + // typedef: + typedef ZeroFunction BoundaryValues; + + // Second, a class that denotes + // the right hand side. Since + // they are constant, just + // subclass the corresponding + // class of the library and be + // done: + class RightHandSide : public ConstantFunction + { + public: + RightHandSide () : ConstantFunction (1.) {} + }; + + // Finally a function to + // generate the coarse + // grid. This is somewhat more + // complicated here, see + // immediately below. + static + void + create_coarse_grid (Triangulation &coarse_grid); }; - // As stated above, the grid for - // this example is the square - // [-1,1]^2 with the square - // [-1/2,1/2]^2 as hole in it. We - // create the coarse grid as 4 - // times 4 cells with the middle - // four ones missing. - // - // Of course, the example has an - // extension to 3d, but since this - // function cannot be written in a - // dimension independent way we - // choose not to implement this - // here, but rather only specialize - // the template for dim=2. If you - // compile the program for 3d, - // you'll get a message from the - // linker that this function is not - // implemented for 3d, and needs to - // be provided. - // - // For the creation of this - // geometry, the library has no - // predefined method. In this case, - // the geometry is still simple - // enough to do the creation by - // hand, rather than using a mesh - // generator. + // As stated above, the grid for + // this example is the square + // [-1,1]^2 with the square + // [-1/2,1/2]^2 as hole in it. We + // create the coarse grid as 4 + // times 4 cells with the middle + // four ones missing. + // + // Of course, the example has an + // extension to 3d, but since this + // function cannot be written in a + // dimension independent way we + // choose not to implement this + // here, but rather only specialize + // the template for dim=2. If you + // compile the program for 3d, + // you'll get a message from the + // linker that this function is not + // implemented for 3d, and needs to + // be provided. + // + // For the creation of this + // geometry, the library has no + // predefined method. In this case, + // the geometry is still simple + // enough to do the creation by + // hand, rather than using a mesh + // generator. template <> void Exercise_2_3<2>:: create_coarse_grid (Triangulation<2> &coarse_grid) { - // First define the space - // dimension, to allow those - // parts of the function that are - // actually dimension independent - // to use this variable. That - // makes it simpler if you later - // takes this as a starting point - // to implement the 3d version. + // First define the space + // dimension, to allow those + // parts of the function that are + // actually dimension independent + // to use this variable. That + // makes it simpler if you later + // takes this as a starting point + // to implement the 3d version. const unsigned int dim = 2; - // Then have a list of - // vertices. Here, they are 24 (5 - // times 5, with the middle one - // omitted). It is probably best - // to draw a sketch here. Note - // that we leave the number of - // vertices open at first, but - // then let the compiler compute - // this number afterwards. This - // reduces the possibility of - // having the dimension to large - // and leaving the last ones - // uninitialized. + // Then have a list of + // vertices. Here, they are 24 (5 + // times 5, with the middle one + // omitted). It is probably best + // to draw a sketch here. Note + // that we leave the number of + // vertices open at first, but + // then let the compiler compute + // this number afterwards. This + // reduces the possibility of + // having the dimension to large + // and leaving the last ones + // uninitialized. static const Point<2> vertices_1[] = { Point<2> (-1., -1.), Point<2> (-1./2, -1.), @@ -1624,47 +1624,49 @@ namespace Step14 Point<2> (-1./2, 1.), Point<2> (0., 1.), Point<2> (+1./2, 1.), - Point<2> (+1, 1.) }; + Point<2> (+1, 1.) + }; const unsigned int - n_vertices = sizeof(vertices_1) / sizeof(vertices_1[0]); + n_vertices = sizeof(vertices_1) / sizeof(vertices_1[0]); - // From this static list of - // vertices, we generate an STL - // vector of the vertices, as - // this is the data type the - // library wants to see. + // From this static list of + // vertices, we generate an STL + // vector of the vertices, as + // this is the data type the + // library wants to see. const std::vector > vertices (&vertices_1[0], &vertices_1[n_vertices]); - // Next, we have to define the - // cells and the vertices they - // contain. Here, we have 8 - // vertices, but leave the number - // open and let it be computed - // afterwards: + // Next, we have to define the + // cells and the vertices they + // contain. Here, we have 8 + // vertices, but leave the number + // open and let it be computed + // afterwards: static const int cell_vertices[][GeometryInfo::vertices_per_cell] - = {{0, 1, 5, 6}, - {1, 2, 6, 7}, - {2, 3, 7, 8}, - {3, 4, 8, 9}, - {5, 6, 10, 11}, - {8, 9, 12, 13}, - {10, 11, 14, 15}, - {12, 13, 17, 18}, - {14, 15, 19, 20}, - {15, 16, 20, 21}, - {16, 17, 21, 22}, - {17, 18, 22, 23}}; + = {{0, 1, 5, 6}, + {1, 2, 6, 7}, + {2, 3, 7, 8}, + {3, 4, 8, 9}, + {5, 6, 10, 11}, + {8, 9, 12, 13}, + {10, 11, 14, 15}, + {12, 13, 17, 18}, + {14, 15, 19, 20}, + {15, 16, 20, 21}, + {16, 17, 21, 22}, + {17, 18, 22, 23} + }; const unsigned int - n_cells = sizeof(cell_vertices) / sizeof(cell_vertices[0]); - - // Again, we generate a C++ - // vector type from this, but - // this time by looping over the - // cells (yes, this is - // boring). Additionally, we set - // the material indicator to zero - // for all the cells: + n_cells = sizeof(cell_vertices) / sizeof(cell_vertices[0]); + + // Again, we generate a C++ + // vector type from this, but + // this time by looping over the + // cells (yes, this is + // boring). Additionally, we set + // the material indicator to zero + // for all the cells: std::vector > cells (n_cells, CellData()); for (unsigned int i=0; iCurvedRidges class) directly - // as classes derived from - // Data::SetUpBase. Indeed, we - // could have done very well so. The - // only reason is that then we would - // have to have member variables for - // the solution and right hand side - // classes in the CurvedRidges - // class, as well as member functions - // overloading the abstract functions - // of the base class giving access to - // these member variables. The - // SetUp class has the sole - // reason to relieve us from the need - // to reiterate these member - // variables and functions that would - // be necessary in all such - // classes. In some way, the template - // mechanism here only provides a way - // to have default implementations - // for a number of functions that - // depend on external quantities and - // can thus not be provided using - // normal virtual functions, at least - // not without the help of templates. - // - // However, there might be good - // reasons to actually implement - // classes derived from - // Data::SetUpBase, for example - // if the solution or right hand side - // classes require constructors that - // take arguments, which the - // Data::SetUpBase class cannot - // provide. In that case, subclassing - // is a worthwhile strategy. Other - // possibilities for special cases - // are to derive from - // Data::SetUp@ where - // SomeSetUp denotes a class, or - // even to explicitly specialize - // Data::SetUp@. The - // latter allows to transparently use - // the way the SetUp class is - // used for other set-ups, but with - // special actions taken for special - // arguments. - // - // A final observation favoring the - // approach taken here is the - // following: we have found numerous - // times that when starting a - // project, the number of parameters - // (usually boundary values, right - // hand side, coarse grid, just as - // here) was small, and the number of - // test cases was small as well. One - // then starts out by handcoding them - // into a number of switch - // statements. Over time, projects - // grow, and so does the number of - // test cases. The number of - // switch statements grows with - // that, and their length as well, - // and one starts to find ways to - // consider impossible examples where - // domains, boundary values, and - // right hand sides do not fit - // together any more, and starts - // losing the overview over the - // whole structure. Encapsulating - // everything belonging to a certain - // test case into a structure of its - // own has proven worthwhile for - // this, as it keeps everything that - // belongs to one test case in one - // place. Furthermore, it allows to - // put these things all in one or - // more files that are only devoted - // to test cases and their data, - // without having to bring their - // actual implementation into contact - // with the rest of the program. - - - // @sect3{Dual functionals} - - // As with the other components of - // the program, we put everything we - // need to describe dual functionals - // into a namespace of its own, and - // define an abstract base class that - // provides the interface the class - // solving the dual problem needs for - // its work. - // - // We will then implement two such - // classes, for the evaluation of a - // point value and of the derivative - // of the solution at that point. For - // these functionals we already have - // the corresponding evaluation - // objects, so they are comlementary. + // @sect4{Discussion} + // + // As you have now read through this + // framework, you may be wondering + // why we have not chosen to + // implement the classes implementing + // a certain setup (like the + // CurvedRidges class) directly + // as classes derived from + // Data::SetUpBase. Indeed, we + // could have done very well so. The + // only reason is that then we would + // have to have member variables for + // the solution and right hand side + // classes in the CurvedRidges + // class, as well as member functions + // overloading the abstract functions + // of the base class giving access to + // these member variables. The + // SetUp class has the sole + // reason to relieve us from the need + // to reiterate these member + // variables and functions that would + // be necessary in all such + // classes. In some way, the template + // mechanism here only provides a way + // to have default implementations + // for a number of functions that + // depend on external quantities and + // can thus not be provided using + // normal virtual functions, at least + // not without the help of templates. + // + // However, there might be good + // reasons to actually implement + // classes derived from + // Data::SetUpBase, for example + // if the solution or right hand side + // classes require constructors that + // take arguments, which the + // Data::SetUpBase class cannot + // provide. In that case, subclassing + // is a worthwhile strategy. Other + // possibilities for special cases + // are to derive from + // Data::SetUp@ where + // SomeSetUp denotes a class, or + // even to explicitly specialize + // Data::SetUp@. The + // latter allows to transparently use + // the way the SetUp class is + // used for other set-ups, but with + // special actions taken for special + // arguments. + // + // A final observation favoring the + // approach taken here is the + // following: we have found numerous + // times that when starting a + // project, the number of parameters + // (usually boundary values, right + // hand side, coarse grid, just as + // here) was small, and the number of + // test cases was small as well. One + // then starts out by handcoding them + // into a number of switch + // statements. Over time, projects + // grow, and so does the number of + // test cases. The number of + // switch statements grows with + // that, and their length as well, + // and one starts to find ways to + // consider impossible examples where + // domains, boundary values, and + // right hand sides do not fit + // together any more, and starts + // losing the overview over the + // whole structure. Encapsulating + // everything belonging to a certain + // test case into a structure of its + // own has proven worthwhile for + // this, as it keeps everything that + // belongs to one test case in one + // place. Furthermore, it allows to + // put these things all in one or + // more files that are only devoted + // to test cases and their data, + // without having to bring their + // actual implementation into contact + // with the rest of the program. + + + // @sect3{Dual functionals} + + // As with the other components of + // the program, we put everything we + // need to describe dual functionals + // into a namespace of its own, and + // define an abstract base class that + // provides the interface the class + // solving the dual problem needs for + // its work. + // + // We will then implement two such + // classes, for the evaluation of a + // point value and of the derivative + // of the solution at that point. For + // these functionals we already have + // the corresponding evaluation + // objects, so they are comlementary. namespace DualFunctional { - // @sect4{The DualFunctionalBase class} - - // First start with the base class - // for dual functionals. Since for - // linear problems the - // characteristics of the dual - // problem play a role only in the - // right hand side, we only need to - // provide for a function that - // assembles the right hand side - // for a given discretization: + // @sect4{The DualFunctionalBase class} + + // First start with the base class + // for dual functionals. Since for + // linear problems the + // characteristics of the dual + // problem play a role only in the + // right hand side, we only need to + // provide for a function that + // assembles the right hand side + // for a given discretization: template class DualFunctionalBase : public Subscriptor { - public: - virtual - void - assemble_rhs (const DoFHandler &dof_handler, - Vector &rhs) const = 0; + public: + virtual + void + assemble_rhs (const DoFHandler &dof_handler, + Vector &rhs) const = 0; }; - // @sect4{The PointValueEvaluation class} + // @sect4{The PointValueEvaluation class} - // As a first application, we - // consider the functional - // corresponding to the evaluation - // of the solution's value at a - // given point which again we - // assume to be a vertex. Apart - // from the constructor that takes - // and stores the evaluation point, - // this class consists only of the - // function that implements - // assembling the right hand side. + // As a first application, we + // consider the functional + // corresponding to the evaluation + // of the solution's value at a + // given point which again we + // assume to be a vertex. Apart + // from the constructor that takes + // and stores the evaluation point, + // this class consists only of the + // function that implements + // assembling the right hand side. template class PointValueEvaluation : public DualFunctionalBase { - public: - PointValueEvaluation (const Point &evaluation_point); + public: + PointValueEvaluation (const Point &evaluation_point); - virtual - void - assemble_rhs (const DoFHandler &dof_handler, - Vector &rhs) const; + virtual + void + assemble_rhs (const DoFHandler &dof_handler, + Vector &rhs) const; - DeclException1 (ExcEvaluationPointNotFound, - Point, - << "The evaluation point " << arg1 - << " was not found among the vertices of the present grid."); + DeclException1 (ExcEvaluationPointNotFound, + Point, + << "The evaluation point " << arg1 + << " was not found among the vertices of the present grid."); - protected: - const Point evaluation_point; + protected: + const Point evaluation_point; }; template PointValueEvaluation:: PointValueEvaluation (const Point &evaluation_point) - : - evaluation_point (evaluation_point) + : + evaluation_point (evaluation_point) {} - // As for doing the main purpose of - // the class, assembling the right - // hand side, let us first consider - // what is necessary: The right - // hand side of the dual problem is - // a vector of values J(phi_i), - // where J is the error functional, - // and phi_i is the i-th shape - // function. Here, J is the - // evaluation at the point x0, - // i.e. J(phi_i)=phi_i(x0). - // - // Now, we have assumed that the - // evaluation point is a - // vertex. Thus, for the usual - // finite elements we might be - // using in this program, we can - // take for granted that at such a - // point exactly one shape function - // is nonzero, and in particular - // has the value one. Thus, we set - // the right hand side vector to - // all-zeros, then seek for the - // shape function associated with - // that point and set the - // corresponding value of the right - // hand side vector to one: + // As for doing the main purpose of + // the class, assembling the right + // hand side, let us first consider + // what is necessary: The right + // hand side of the dual problem is + // a vector of values J(phi_i), + // where J is the error functional, + // and phi_i is the i-th shape + // function. Here, J is the + // evaluation at the point x0, + // i.e. J(phi_i)=phi_i(x0). + // + // Now, we have assumed that the + // evaluation point is a + // vertex. Thus, for the usual + // finite elements we might be + // using in this program, we can + // take for granted that at such a + // point exactly one shape function + // is nonzero, and in particular + // has the value one. Thus, we set + // the right hand side vector to + // all-zeros, then seek for the + // shape function associated with + // that point and set the + // corresponding value of the right + // hand side vector to one: template void PointValueEvaluation:: assemble_rhs (const DoFHandler &dof_handler, Vector &rhs) const { - // So, first set everything to - // zeros... + // So, first set everything to + // zeros... rhs.reinit (dof_handler.n_dofs()); - // ...then loop over cells and - // find the evaluation point - // among the vertices (or very - // close to a vertex, which may - // happen due to floating point - // round-off): + // ...then loop over cells and + // find the evaluation point + // among the vertices (or very + // close to a vertex, which may + // happen due to floating point + // round-off): typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) for (unsigned int vertex=0; vertex::vertices_per_cell; @@ -1926,111 +1928,111 @@ namespace Step14 if (cell->vertex(vertex).distance(evaluation_point) < cell->diameter()*1e-8) { - // Ok, found, so set - // corresponding entry, - // and leave function - // since we are finished: + // Ok, found, so set + // corresponding entry, + // and leave function + // since we are finished: rhs(cell->vertex_dof_index(vertex,0)) = 1; return; } - // Finally, a sanity check: if we - // somehow got here, then we must - // have missed the evaluation - // point, so raise an exception - // unconditionally: + // Finally, a sanity check: if we + // somehow got here, then we must + // have missed the evaluation + // point, so raise an exception + // unconditionally: AssertThrow (false, ExcEvaluationPointNotFound(evaluation_point)); } - // @sect4{The PointXDerivativeEvaluation class} + // @sect4{The PointXDerivativeEvaluation class} - // As second application, we again - // consider the evaluation of the - // x-derivative of the solution at - // one point. Again, the - // declaration of the class, and - // the implementation of its - // constructor is not too - // interesting: + // As second application, we again + // consider the evaluation of the + // x-derivative of the solution at + // one point. Again, the + // declaration of the class, and + // the implementation of its + // constructor is not too + // interesting: template class PointXDerivativeEvaluation : public DualFunctionalBase { - public: - PointXDerivativeEvaluation (const Point &evaluation_point); + public: + PointXDerivativeEvaluation (const Point &evaluation_point); - virtual - void - assemble_rhs (const DoFHandler &dof_handler, - Vector &rhs) const; + virtual + void + assemble_rhs (const DoFHandler &dof_handler, + Vector &rhs) const; - DeclException1 (ExcEvaluationPointNotFound, - Point, - << "The evaluation point " << arg1 - << " was not found among the vertices of the present grid."); + DeclException1 (ExcEvaluationPointNotFound, + Point, + << "The evaluation point " << arg1 + << " was not found among the vertices of the present grid."); - protected: - const Point evaluation_point; + protected: + const Point evaluation_point; }; template PointXDerivativeEvaluation:: PointXDerivativeEvaluation (const Point &evaluation_point) - : - evaluation_point (evaluation_point) + : + evaluation_point (evaluation_point) {} - // What is interesting is the - // implementation of this - // functional: here, - // J(phi_i)=d/dx phi_i(x0). - // - // We could, as in the - // implementation of the respective - // evaluation object take the - // average of the gradients of each - // shape function phi_i at this - // evaluation point. However, we - // take a slightly different - // approach: we simply take the - // average over all cells that - // surround this point. The - // question which cells - // surrounds the evaluation - // point is made dependent on the - // mesh width by including those - // cells for which the distance of - // the cell's midpoint to the - // evaluation point is less than - // the cell's diameter. - // - // Taking the average of the - // gradient over the area/volume of - // these cells leads to a dual - // solution which is very close to - // the one which would result from - // the point evaluation of the - // gradient. It is simple to - // justify theoretically that this - // does not change the method - // significantly. + // What is interesting is the + // implementation of this + // functional: here, + // J(phi_i)=d/dx phi_i(x0). + // + // We could, as in the + // implementation of the respective + // evaluation object take the + // average of the gradients of each + // shape function phi_i at this + // evaluation point. However, we + // take a slightly different + // approach: we simply take the + // average over all cells that + // surround this point. The + // question which cells + // surrounds the evaluation + // point is made dependent on the + // mesh width by including those + // cells for which the distance of + // the cell's midpoint to the + // evaluation point is less than + // the cell's diameter. + // + // Taking the average of the + // gradient over the area/volume of + // these cells leads to a dual + // solution which is very close to + // the one which would result from + // the point evaluation of the + // gradient. It is simple to + // justify theoretically that this + // does not change the method + // significantly. template void PointXDerivativeEvaluation:: assemble_rhs (const DoFHandler &dof_handler, Vector &rhs) const { - // Again, first set all entries - // to zero: + // Again, first set all entries + // to zero: rhs.reinit (dof_handler.n_dofs()); - // Initialize a FEValues - // object with a quadrature - // formula, have abbreviations - // for the number of quadrature - // points and shape functions... + // Initialize a FEValues + // object with a quadrature + // formula, have abbreviations + // for the number of quadrature + // points and shape functions... QGauss quadrature(4); FEValues fe_values (dof_handler.get_fe(), quadrature, update_gradients | @@ -2039,44 +2041,44 @@ namespace Step14 const unsigned int n_q_points = fe_values.n_quadrature_points; const unsigned int dofs_per_cell = dof_handler.get_fe().dofs_per_cell; - // ...and have two objects that - // are used to store the global - // indices of the degrees of - // freedom on a cell, and the - // values of the gradients of the - // shape functions at the - // quadrature points: + // ...and have two objects that + // are used to store the global + // indices of the degrees of + // freedom on a cell, and the + // values of the gradients of the + // shape functions at the + // quadrature points: Vector cell_rhs (dofs_per_cell); std::vector local_dof_indices (dofs_per_cell); - // Finally have a variable in - // which we will sum up the - // area/volume of the cells over - // which we integrate, by - // integrating the unit functions - // on these cells: + // Finally have a variable in + // which we will sum up the + // area/volume of the cells over + // which we integrate, by + // integrating the unit functions + // on these cells: double total_volume = 0; - // Then start the loop over all - // cells, and select those cells - // which are close enough to the - // evaluation point: + // Then start the loop over all + // cells, and select those cells + // which are close enough to the + // evaluation point: typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) if (cell->center().distance(evaluation_point) <= cell->diameter()) { - // If we have found such a - // cell, then initialize - // the FEValues object - // and integrate the - // x-component of the - // gradient of each shape - // function, as well as the - // unit function for the - // total area/volume. + // If we have found such a + // cell, then initialize + // the FEValues object + // and integrate the + // x-component of the + // gradient of each shape + // function, as well as the + // unit function for the + // total area/volume. fe_values.reinit (cell); cell_rhs = 0; @@ -2088,34 +2090,34 @@ namespace Step14 total_volume += fe_values.JxW (q); } - // If we have the local - // contributions, - // distribute them to the - // global vector: + // If we have the local + // contributions, + // distribute them to the + // global vector: cell->get_dof_indices (local_dof_indices); for (unsigned int i=0; i 0, ExcEvaluationPointNotFound(evaluation_point)); - // Finally, we have by now only - // integrated the gradients of - // the shape functions, not - // taking their mean value. We - // fix this by dividing by the - // measure of the volume over - // which we have integrated: + // Finally, we have by now only + // integrated the gradients of + // the shape functions, not + // taking their mean value. We + // fix this by dividing by the + // measure of the volume over + // which we have integrated: rhs.scale (1./total_volume); } @@ -2123,79 +2125,79 @@ namespace Step14 } - // @sect3{Extending the LaplaceSolver namespace} + // @sect3{Extending the LaplaceSolver namespace} namespace LaplaceSolver { - // @sect4{The DualSolver class} - - // In the same way as the - // PrimalSolver class above, we - // now implement a - // DualSolver. It has all the - // same features, the only - // difference is that it does not - // take a function object denoting - // a right hand side object, but - // now takes a - // DualFunctionalBase object - // that will assemble the right - // hand side vector of the dual - // problem. The rest of the class - // is rather trivial. - // - // Since both primal and dual - // solver will use the same - // triangulation, but different - // discretizations, it now becomes - // clear why we have made the - // Base class a virtual one: - // since the final class will be - // derived from both - // PrimalSolver as well as - // DualSolver, it would have - // two Base instances, would we - // not have marked the inheritance - // as virtual. Since in many - // applications the base class - // would store much more - // information than just the - // triangulation which needs to be - // shared between primal and dual - // solvers, we do not usually want - // to use two such base classes. + // @sect4{The DualSolver class} + + // In the same way as the + // PrimalSolver class above, we + // now implement a + // DualSolver. It has all the + // same features, the only + // difference is that it does not + // take a function object denoting + // a right hand side object, but + // now takes a + // DualFunctionalBase object + // that will assemble the right + // hand side vector of the dual + // problem. The rest of the class + // is rather trivial. + // + // Since both primal and dual + // solver will use the same + // triangulation, but different + // discretizations, it now becomes + // clear why we have made the + // Base class a virtual one: + // since the final class will be + // derived from both + // PrimalSolver as well as + // DualSolver, it would have + // two Base instances, would we + // not have marked the inheritance + // as virtual. Since in many + // applications the base class + // would store much more + // information than just the + // triangulation which needs to be + // shared between primal and dual + // solvers, we do not usually want + // to use two such base classes. template class DualSolver : public Solver { - public: - DualSolver (Triangulation &triangulation, - const FiniteElement &fe, - const Quadrature &quadrature, - const Quadrature &face_quadrature, - const DualFunctional::DualFunctionalBase &dual_functional); + public: + DualSolver (Triangulation &triangulation, + const FiniteElement &fe, + const Quadrature &quadrature, + const Quadrature &face_quadrature, + const DualFunctional::DualFunctionalBase &dual_functional); - virtual - void - solve_problem (); + virtual + void + solve_problem (); - virtual - unsigned int - n_dofs () const; + virtual + unsigned int + n_dofs () const; - virtual - void - postprocess (const Evaluation::EvaluationBase &postprocessor) const; + virtual + void + postprocess (const Evaluation::EvaluationBase &postprocessor) const; - protected: - const SmartPointer > dual_functional; - virtual void assemble_rhs (Vector &rhs) const; + protected: + const SmartPointer > dual_functional; + virtual void assemble_rhs (Vector &rhs) const; - static const ZeroFunction boundary_values; + static const ZeroFunction boundary_values; - // Same as above -- make a - // derived class a friend of - // this one: - friend class WeightedResidual; + // Same as above -- make a + // derived class a friend of + // this one: + friend class WeightedResidual; }; template @@ -2208,12 +2210,12 @@ namespace Step14 const Quadrature &quadrature, const Quadrature &face_quadrature, const DualFunctional::DualFunctionalBase &dual_functional) - : - Base (triangulation), - Solver (triangulation, fe, - quadrature, face_quadrature, - boundary_values), - dual_functional (&dual_functional) + : + Base (triangulation), + Solver (triangulation, fe, + quadrature, face_quadrature, + boundary_values), + dual_functional (&dual_functional) {} @@ -2253,307 +2255,307 @@ namespace Step14 } - // @sect4{The WeightedResidual class} - - // Here finally comes the main - // class of this program, the one - // that implements the dual - // weighted residual error - // estimator. It joins the primal - // and dual solver classes to use - // them for the computation of - // primal and dual solutions, and - // implements the error - // representation formula for use - // as error estimate and mesh - // refinement. - // - // The first few of the functions - // of this class are mostly - // overriders of the respective - // functions of the base class: + // @sect4{The WeightedResidual class} + + // Here finally comes the main + // class of this program, the one + // that implements the dual + // weighted residual error + // estimator. It joins the primal + // and dual solver classes to use + // them for the computation of + // primal and dual solutions, and + // implements the error + // representation formula for use + // as error estimate and mesh + // refinement. + // + // The first few of the functions + // of this class are mostly + // overriders of the respective + // functions of the base class: template class WeightedResidual : public PrimalSolver, - public DualSolver + public DualSolver { - public: - WeightedResidual (Triangulation &coarse_grid, - const FiniteElement &primal_fe, - const FiniteElement &dual_fe, - const Quadrature &quadrature, - const Quadrature &face_quadrature, - const Function &rhs_function, - const Function &boundary_values, - const DualFunctional::DualFunctionalBase &dual_functional); - - virtual - void - solve_problem (); - - virtual - void - postprocess (const Evaluation::EvaluationBase &postprocessor) const; - - virtual - unsigned int - n_dofs () const; - - virtual void refine_grid (); - - virtual - void - output_solution () const; - - private: - // In the private section, we - // have two functions that are - // used to call the - // solve_problem functions - // of the primal and dual base - // classes. These two functions - // will be called in parallel - // by the solve_problem - // function of this class. - void solve_primal_problem (); - void solve_dual_problem (); - // Then declare abbreviations - // for active cell iterators, - // to avoid that we have to - // write this lengthy name - // over and over again: - - typedef - typename DoFHandler::active_cell_iterator - active_cell_iterator; - - // Next, declare a data type - // that we will us to store the - // contribution of faces to the - // error estimator. The idea is - // that we can compute the face - // terms from each of the two - // cells to this face, as they - // are the same when viewed - // from both sides. What we - // will do is to compute them - // only once, based on some - // rules explained below which - // of the two adjacent cells - // will be in charge to do - // so. We then store the - // contribution of each face in - // a map mapping faces to their - // values, and only collect the - // contributions for each cell - // by looping over the cells a - // second time and grabbing the - // values from the map. - // - // The data type of this map is - // declared here: - typedef - typename std::map::face_iterator,double> - FaceIntegrals; - - // In the computation of the - // error estimates on cells and - // faces, we need a number of - // helper objects, such as - // FEValues and - // FEFaceValues functions, - // but also temporary objects - // storing the values and - // gradients of primal and dual - // solutions, for - // example. These fields are - // needed in the three - // functions that do the - // integration on cells, and - // regular and irregular faces, - // respectively. - // - // There are three reasonable - // ways to provide these - // fields: first, as local - // variables in the function - // that needs them; second, as - // member variables of this - // class; third, as arguments - // passed to that function. - // - // These three alternatives all - // have drawbacks: the third - // that their number is not - // neglectable and would make - // calling these functions a - // lengthy enterprise. The - // second has the drawback that - // it disallows - // parallelization, since the - // threads that will compute - // the error estimate have to - // have their own copies of - // these variables each, so - // member variables of the - // enclosing class will not - // work. The first approach, - // although straightforward, - // has a subtle but important - // drawback: we will call these - // functions over and over - // again, many thousands of times - // maybe; it has now turned out - // that allocating vectors and - // other objects that need - // memory from the heap is an - // expensive business in terms - // of run-time, since memory - // allocation is expensive when - // several threads are - // involved. In our experience, - // more than 20 per cent of the - // total run time of error - // estimation functions are due - // to memory allocation, if - // done on a per-call level. It - // is thus significantly better - // to allocate the memory only - // once, and recycle the - // objects as often as - // possible. - // - // What to do? Our answer is to - // use a variant of the third - // strategy, namely generating - // these variables once in the - // main function of each - // thread, and passing them - // down to the functions that - // do the actual work. To avoid - // that we have to give these - // functions a dozen or so - // arguments, we pack all these - // variables into two - // structures, one which is - // used for the computations on - // cells, the other doing them - // on the faces. Instead of - // many individual objects, we - // will then only pass one such - // object to these functions, - // making their calling - // sequence simpler. - struct CellData - { - FEValues fe_values; - const SmartPointer > right_hand_side; - - std::vector cell_residual; - std::vector rhs_values; - std::vector dual_weights; - std::vector cell_laplacians; - CellData (const FiniteElement &fe, - const Quadrature &quadrature, - const Function &right_hand_side); - }; + public: + WeightedResidual (Triangulation &coarse_grid, + const FiniteElement &primal_fe, + const FiniteElement &dual_fe, + const Quadrature &quadrature, + const Quadrature &face_quadrature, + const Function &rhs_function, + const Function &boundary_values, + const DualFunctional::DualFunctionalBase &dual_functional); + + virtual + void + solve_problem (); + + virtual + void + postprocess (const Evaluation::EvaluationBase &postprocessor) const; + + virtual + unsigned int + n_dofs () const; + + virtual void refine_grid (); + + virtual + void + output_solution () const; + + private: + // In the private section, we + // have two functions that are + // used to call the + // solve_problem functions + // of the primal and dual base + // classes. These two functions + // will be called in parallel + // by the solve_problem + // function of this class. + void solve_primal_problem (); + void solve_dual_problem (); + // Then declare abbreviations + // for active cell iterators, + // to avoid that we have to + // write this lengthy name + // over and over again: - struct FaceData - { - FEFaceValues fe_face_values_cell; - FEFaceValues fe_face_values_neighbor; - FESubfaceValues fe_subface_values_cell; - - std::vector jump_residual; - std::vector dual_weights; - typename std::vector > cell_grads; - typename std::vector > neighbor_grads; - FaceData (const FiniteElement &fe, - const Quadrature &face_quadrature); - }; - - - - // Regarding the evaluation of - // the error estimator, we have - // two driver functions that do - // this: the first is called to - // generate the cell-wise - // estimates, and splits up the - // task in a number of threads - // each of which work on a - // subset of the cells. The - // first function will run the - // second for each of these - // threads: - void estimate_error (Vector &error_indicators) const; - - void estimate_some (const Vector &primal_solution, - const Vector &dual_weights, - const unsigned int n_threads, - const unsigned int this_thread, - Vector &error_indicators, - FaceIntegrals &face_integrals) const; - - // Then we have functions that - // do the actual integration of - // the error representation - // formula. They will treat the - // terms on the cell interiors, - // on those faces that have no - // hanging nodes, and on those - // faces with hanging nodes, - // respectively: - void - integrate_over_cell (const active_cell_iterator &cell, - const unsigned int cell_index, - const Vector &primal_solution, - const Vector &dual_weights, - CellData &cell_data, - Vector &error_indicators) const; - - void - integrate_over_regular_face (const active_cell_iterator &cell, + typedef + typename DoFHandler::active_cell_iterator + active_cell_iterator; + + // Next, declare a data type + // that we will us to store the + // contribution of faces to the + // error estimator. The idea is + // that we can compute the face + // terms from each of the two + // cells to this face, as they + // are the same when viewed + // from both sides. What we + // will do is to compute them + // only once, based on some + // rules explained below which + // of the two adjacent cells + // will be in charge to do + // so. We then store the + // contribution of each face in + // a map mapping faces to their + // values, and only collect the + // contributions for each cell + // by looping over the cells a + // second time and grabbing the + // values from the map. + // + // The data type of this map is + // declared here: + typedef + typename std::map::face_iterator,double> + FaceIntegrals; + + // In the computation of the + // error estimates on cells and + // faces, we need a number of + // helper objects, such as + // FEValues and + // FEFaceValues functions, + // but also temporary objects + // storing the values and + // gradients of primal and dual + // solutions, for + // example. These fields are + // needed in the three + // functions that do the + // integration on cells, and + // regular and irregular faces, + // respectively. + // + // There are three reasonable + // ways to provide these + // fields: first, as local + // variables in the function + // that needs them; second, as + // member variables of this + // class; third, as arguments + // passed to that function. + // + // These three alternatives all + // have drawbacks: the third + // that their number is not + // neglectable and would make + // calling these functions a + // lengthy enterprise. The + // second has the drawback that + // it disallows + // parallelization, since the + // threads that will compute + // the error estimate have to + // have their own copies of + // these variables each, so + // member variables of the + // enclosing class will not + // work. The first approach, + // although straightforward, + // has a subtle but important + // drawback: we will call these + // functions over and over + // again, many thousands of times + // maybe; it has now turned out + // that allocating vectors and + // other objects that need + // memory from the heap is an + // expensive business in terms + // of run-time, since memory + // allocation is expensive when + // several threads are + // involved. In our experience, + // more than 20 per cent of the + // total run time of error + // estimation functions are due + // to memory allocation, if + // done on a per-call level. It + // is thus significantly better + // to allocate the memory only + // once, and recycle the + // objects as often as + // possible. + // + // What to do? Our answer is to + // use a variant of the third + // strategy, namely generating + // these variables once in the + // main function of each + // thread, and passing them + // down to the functions that + // do the actual work. To avoid + // that we have to give these + // functions a dozen or so + // arguments, we pack all these + // variables into two + // structures, one which is + // used for the computations on + // cells, the other doing them + // on the faces. Instead of + // many individual objects, we + // will then only pass one such + // object to these functions, + // making their calling + // sequence simpler. + struct CellData + { + FEValues fe_values; + const SmartPointer > right_hand_side; + + std::vector cell_residual; + std::vector rhs_values; + std::vector dual_weights; + std::vector cell_laplacians; + CellData (const FiniteElement &fe, + const Quadrature &quadrature, + const Function &right_hand_side); + }; + + struct FaceData + { + FEFaceValues fe_face_values_cell; + FEFaceValues fe_face_values_neighbor; + FESubfaceValues fe_subface_values_cell; + + std::vector jump_residual; + std::vector dual_weights; + typename std::vector > cell_grads; + typename std::vector > neighbor_grads; + FaceData (const FiniteElement &fe, + const Quadrature &face_quadrature); + }; + + + + // Regarding the evaluation of + // the error estimator, we have + // two driver functions that do + // this: the first is called to + // generate the cell-wise + // estimates, and splits up the + // task in a number of threads + // each of which work on a + // subset of the cells. The + // first function will run the + // second for each of these + // threads: + void estimate_error (Vector &error_indicators) const; + + void estimate_some (const Vector &primal_solution, + const Vector &dual_weights, + const unsigned int n_threads, + const unsigned int this_thread, + Vector &error_indicators, + FaceIntegrals &face_integrals) const; + + // Then we have functions that + // do the actual integration of + // the error representation + // formula. They will treat the + // terms on the cell interiors, + // on those faces that have no + // hanging nodes, and on those + // faces with hanging nodes, + // respectively: + void + integrate_over_cell (const active_cell_iterator &cell, + const unsigned int cell_index, + const Vector &primal_solution, + const Vector &dual_weights, + CellData &cell_data, + Vector &error_indicators) const; + + void + integrate_over_regular_face (const active_cell_iterator &cell, + const unsigned int face_no, + const Vector &primal_solution, + const Vector &dual_weights, + FaceData &face_data, + FaceIntegrals &face_integrals) const; + void + integrate_over_irregular_face (const active_cell_iterator &cell, const unsigned int face_no, const Vector &primal_solution, const Vector &dual_weights, FaceData &face_data, FaceIntegrals &face_integrals) const; - void - integrate_over_irregular_face (const active_cell_iterator &cell, - const unsigned int face_no, - const Vector &primal_solution, - const Vector &dual_weights, - FaceData &face_data, - FaceIntegrals &face_integrals) const; }; - // In the implementation of this - // class, we first have the - // constructors of the CellData - // and FaceData member classes, - // and the WeightedResidual - // constructor. They only - // initialize fields to their - // correct lengths, so we do not - // have to discuss them to length. + // In the implementation of this + // class, we first have the + // constructors of the CellData + // and FaceData member classes, + // and the WeightedResidual + // constructor. They only + // initialize fields to their + // correct lengths, so we do not + // have to discuss them to length. template WeightedResidual::CellData:: CellData (const FiniteElement &fe, const Quadrature &quadrature, const Function &right_hand_side) - : - fe_values (fe, quadrature, - update_values | - update_hessians | - update_quadrature_points | - update_JxW_values), - right_hand_side (&right_hand_side), - cell_residual (quadrature.size()), - rhs_values (quadrature.size()), - dual_weights (quadrature.size()), - cell_laplacians (quadrature.size()) + : + fe_values (fe, quadrature, + update_values | + update_hessians | + update_quadrature_points | + update_JxW_values), + right_hand_side (&right_hand_side), + cell_residual (quadrature.size()), + rhs_values (quadrature.size()), + dual_weights (quadrature.size()), + cell_laplacians (quadrature.size()) {} @@ -2562,19 +2564,19 @@ namespace Step14 WeightedResidual::FaceData:: FaceData (const FiniteElement &fe, const Quadrature &face_quadrature) - : - fe_face_values_cell (fe, face_quadrature, - update_values | - update_gradients | - update_JxW_values | - update_normal_vectors), - fe_face_values_neighbor (fe, face_quadrature, - update_values | - update_gradients | - update_JxW_values | - update_normal_vectors), - fe_subface_values_cell (fe, face_quadrature, - update_gradients) + : + fe_face_values_cell (fe, face_quadrature, + update_values | + update_gradients | + update_JxW_values | + update_normal_vectors), + fe_face_values_neighbor (fe, face_quadrature, + update_values | + update_gradients | + update_JxW_values | + update_normal_vectors), + fe_subface_values_cell (fe, face_quadrature, + update_gradients) { const unsigned int n_face_q_points = face_quadrature.size(); @@ -2598,26 +2600,26 @@ namespace Step14 const Function &rhs_function, const Function &bv, const DualFunctional::DualFunctionalBase &dual_functional) - : - Base (coarse_grid), - PrimalSolver (coarse_grid, primal_fe, - quadrature, face_quadrature, - rhs_function, bv), - DualSolver (coarse_grid, dual_fe, - quadrature, face_quadrature, - dual_functional) + : + Base (coarse_grid), + PrimalSolver (coarse_grid, primal_fe, + quadrature, face_quadrature, + rhs_function, bv), + DualSolver (coarse_grid, dual_fe, + quadrature, face_quadrature, + dual_functional) {} - // The next five functions are - // boring, as they simply relay - // their work to the base - // classes. The first calls the - // primal and dual solvers in - // parallel, while postprocessing - // the solution and retrieving the - // number of degrees of freedom is - // done by the primal class. + // The next five functions are + // boring, as they simply relay + // their work to the base + // classes. The first calls the + // primal and dual solvers in + // parallel, while postprocessing + // the solution and retrieving the + // number of degrees of freedom is + // done by the primal class. template void WeightedResidual::solve_problem () @@ -2664,44 +2666,44 @@ namespace Step14 - // Now, it is becoming more - // interesting: the refine_grid - // function asks the error - // estimator to compute the - // cell-wise error indicators, then - // uses their absolute values for - // mesh refinement. + // Now, it is becoming more + // interesting: the refine_grid + // function asks the error + // estimator to compute the + // cell-wise error indicators, then + // uses their absolute values for + // mesh refinement. template void WeightedResidual::refine_grid () { - // First call the function that - // computes the cell-wise and - // global error: + // First call the function that + // computes the cell-wise and + // global error: Vector error_indicators (this->triangulation->n_active_cells()); estimate_error (error_indicators); - // Then note that marking cells - // for refinement or coarsening - // only works if all indicators - // are positive, to allow their - // comparison. Thus, drop the - // signs on all these indicators: + // Then note that marking cells + // for refinement or coarsening + // only works if all indicators + // are positive, to allow their + // comparison. Thus, drop the + // signs on all these indicators: for (Vector::iterator i=error_indicators.begin(); i != error_indicators.end(); ++i) *i = std::fabs (*i); - // Finally, we can select between - // different strategies for - // refinement. The default here - // is to refine those cells with - // the largest error indicators - // that make up for a total of 80 - // per cent of the error, while - // we coarsen those with the - // smallest indicators that make - // up for the bottom 2 per cent - // of the error. + // Finally, we can select between + // different strategies for + // refinement. The default here + // is to refine those cells with + // the largest error indicators + // that make up for a total of 80 + // per cent of the error, while + // we coarsen those with the + // smallest indicators that make + // up for the bottom 2 per cent + // of the error. GridRefinement::refine_and_coarsen_fixed_fraction (*this->triangulation, error_indicators, 0.8, 0.02); @@ -2709,90 +2711,90 @@ namespace Step14 } - // Since we want to output both the - // primal and the dual solution, we - // overload the output_solution - // function. The only interesting - // feature of this function is that - // the primal and dual solutions - // are defined on different finite - // element spaces, which is not the - // format the DataOut class - // expects. Thus, we have to - // transfer them to a common finite - // element space. Since we want the - // solutions only to see them - // qualitatively, we contend - // ourselves with interpolating the - // dual solution to the (smaller) - // primal space. For the - // interpolation, there is a - // library function, that takes a - // ConstraintMatrix object - // including the hanging node - // constraints. The rest is - // standard. - // - // There is, however, one - // work-around worth mentioning: in - // this function, as in a couple of - // following ones, we have to - // access the DoFHandler - // objects and solutions of both - // the primal as well as of the - // dual solver. Since these are - // members of the Solver base - // class which exists twice in the - // class hierarchy leading to the - // present class (once as base - // class of the PrimalSolver - // class, once as base class of the - // DualSolver class), we have - // to disambiguate accesses to them - // by telling the compiler a member - // of which of these two instances - // we want to access. The way to do - // this would be identify the - // member by pointing a path - // through the class hierarchy - // which disambiguates the base - // class, for example writing - // PrimalSolver::dof_handler to - // denote the member variable - // dof_handler from the - // Solver base class of the - // PrimalSolver - // class. Unfortunately, this - // confuses gcc's version 2.96 (a - // version that was intended as a - // development snapshot, but - // delivered as system compiler by - // Red Hat in their 7.x releases) - // so much that it bails out and - // refuses to compile the code. - // - // Thus, we have to work around - // this problem. We do this by - // introducing references to the - // PrimalSolver and - // DualSolver components of the - // WeightedResidual object at - // the beginning of the - // function. Since each of these - // has an unambiguous base class - // Solver, we can access the - // member variables we want through - // these references. However, we - // are now accessing protected - // member variables of these - // classes through a pointer other - // than the this pointer (in - // fact, this is of course the - // this pointer, but not - // explicitly). This finally is the - // reason why we had to declare the - // present class a friend of the - // classes we so access. + // Since we want to output both the + // primal and the dual solution, we + // overload the output_solution + // function. The only interesting + // feature of this function is that + // the primal and dual solutions + // are defined on different finite + // element spaces, which is not the + // format the DataOut class + // expects. Thus, we have to + // transfer them to a common finite + // element space. Since we want the + // solutions only to see them + // qualitatively, we contend + // ourselves with interpolating the + // dual solution to the (smaller) + // primal space. For the + // interpolation, there is a + // library function, that takes a + // ConstraintMatrix object + // including the hanging node + // constraints. The rest is + // standard. + // + // There is, however, one + // work-around worth mentioning: in + // this function, as in a couple of + // following ones, we have to + // access the DoFHandler + // objects and solutions of both + // the primal as well as of the + // dual solver. Since these are + // members of the Solver base + // class which exists twice in the + // class hierarchy leading to the + // present class (once as base + // class of the PrimalSolver + // class, once as base class of the + // DualSolver class), we have + // to disambiguate accesses to them + // by telling the compiler a member + // of which of these two instances + // we want to access. The way to do + // this would be identify the + // member by pointing a path + // through the class hierarchy + // which disambiguates the base + // class, for example writing + // PrimalSolver::dof_handler to + // denote the member variable + // dof_handler from the + // Solver base class of the + // PrimalSolver + // class. Unfortunately, this + // confuses gcc's version 2.96 (a + // version that was intended as a + // development snapshot, but + // delivered as system compiler by + // Red Hat in their 7.x releases) + // so much that it bails out and + // refuses to compile the code. + // + // Thus, we have to work around + // this problem. We do this by + // introducing references to the + // PrimalSolver and + // DualSolver components of the + // WeightedResidual object at + // the beginning of the + // function. Since each of these + // has an unambiguous base class + // Solver, we can access the + // member variables we want through + // these references. However, we + // are now accessing protected + // member variables of these + // classes through a pointer other + // than the this pointer (in + // fact, this is of course the + // this pointer, but not + // explicitly). This finally is the + // reason why we had to declare the + // present class a friend of the + // classes we so access. template void WeightedResidual::output_solution () const @@ -2814,11 +2816,11 @@ namespace Step14 DataOut data_out; data_out.attach_dof_handler (primal_solver.dof_handler); - // Add the data vectors for which - // we want output. Add them both, - // the DataOut functions can - // handle as many data vectors as - // you wish to write to output: + // Add the data vectors for which + // we want output. Add them both, + // the DataOut functions can + // handle as many data vectors as + // you wish to write to output: data_out.add_data_vector (primal_solver.solution, "primal_solution"); data_out.add_data_vector (dual_solution, @@ -2837,17 +2839,17 @@ namespace Step14 } - // @sect3{Estimating errors} + // @sect3{Estimating errors} - // @sect4{Error estimation driver functions} - // - // As for the actual computation of - // error estimates, let's start - // with the function that drives - // all this, i.e. calls those - // functions that actually do the - // work, and finally collects the - // results. + // @sect4{Error estimation driver functions} + // + // As for the actual computation of + // error estimates, let's start + // with the function that drives + // all this, i.e. calls those + // functions that actually do the + // work, and finally collects the + // results. template void @@ -2857,39 +2859,39 @@ namespace Step14 const PrimalSolver &primal_solver = *this; const DualSolver &dual_solver = *this; - // The first task in computing - // the error is to set up vectors - // that denote the primal - // solution, and the weights - // (z-z_h)=(z-I_hz), both in the - // finite element space for which - // we have computed the dual - // solution. For this, we have to - // interpolate the primal - // solution to the dual finite - // element space, and to subtract - // the interpolation of the - // computed dual solution to the - // primal finite element - // space. Fortunately, the - // library provides functions for - // the interpolation into larger - // or smaller finite element - // spaces, so this is mostly - // obvious. - // - // First, let's do that for the - // primal solution: it is - // cell-wise interpolated into - // the finite element space in - // which we have solved the dual - // problem: But, again as in the - // WeightedResidual::output_solution - // function we first need to - // create a ConstraintMatrix - // including the hanging node - // constraints, but this time of - // the dual finite element space. + // The first task in computing + // the error is to set up vectors + // that denote the primal + // solution, and the weights + // (z-z_h)=(z-I_hz), both in the + // finite element space for which + // we have computed the dual + // solution. For this, we have to + // interpolate the primal + // solution to the dual finite + // element space, and to subtract + // the interpolation of the + // computed dual solution to the + // primal finite element + // space. Fortunately, the + // library provides functions for + // the interpolation into larger + // or smaller finite element + // spaces, so this is mostly + // obvious. + // + // First, let's do that for the + // primal solution: it is + // cell-wise interpolated into + // the finite element space in + // which we have solved the dual + // problem: But, again as in the + // WeightedResidual::output_solution + // function we first need to + // create a ConstraintMatrix + // including the hanging node + // constraints, but this time of + // the dual finite element space. ConstraintMatrix dual_hanging_node_constraints; DoFTools::make_hanging_node_constraints (dual_solver.dof_handler, dual_hanging_node_constraints); @@ -2901,17 +2903,17 @@ namespace Step14 dual_hanging_node_constraints, primal_solution); - // Then for computing the - // interpolation of the - // numerically approximated dual - // solution z into the finite - // element space of the primal - // solution and subtracting it - // from z: use the - // interpolate_difference - // function, that gives (z-I_hz) - // in the element space of the - // dual solution. + // Then for computing the + // interpolation of the + // numerically approximated dual + // solution z into the finite + // element space of the primal + // solution and subtracting it + // from z: use the + // interpolate_difference + // function, that gives (z-I_hz) + // in the element space of the + // dual solution. ConstraintMatrix primal_hanging_node_constraints; DoFTools::make_hanging_node_constraints (primal_solver.dof_handler, primal_hanging_node_constraints); @@ -2924,52 +2926,52 @@ namespace Step14 primal_hanging_node_constraints, dual_weights); - // Note that this could probably - // have been more efficient since - // those constraints have been - // used previously when - // assembling matrix and right - // hand side for the primal - // problem and writing out the - // dual solution. We leave the - // optimization of the program in - // this respect as an exercise. - - // Having computed the dual - // weights we now proceed with - // computing the cell and face - // residuals of the primal - // solution. First we set up a - // map between face iterators and - // their jump term contributions - // of faces to the error - // estimator. The reason is that - // we compute the jump terms only - // once, from one side of the - // face, and want to collect them - // only afterwards when looping - // over all cells a second time. - // - // We initialize this map already - // with a value of -1e20 for all - // faces, since this value will - // strike in the results if - // something should go wrong and - // we fail to compute the value - // for a face for some - // reason. Secondly, we - // initialize the map once before - // we branch to different threads - // since this way the map's - // structure is no more modified - // by the individual threads, - // only existing entries are set - // to new values. This relieves - // us from the necessity to - // synchronise the threads - // through a mutex each time they - // write to (and modify the - // structure of) this map. + // Note that this could probably + // have been more efficient since + // those constraints have been + // used previously when + // assembling matrix and right + // hand side for the primal + // problem and writing out the + // dual solution. We leave the + // optimization of the program in + // this respect as an exercise. + + // Having computed the dual + // weights we now proceed with + // computing the cell and face + // residuals of the primal + // solution. First we set up a + // map between face iterators and + // their jump term contributions + // of faces to the error + // estimator. The reason is that + // we compute the jump terms only + // once, from one side of the + // face, and want to collect them + // only afterwards when looping + // over all cells a second time. + // + // We initialize this map already + // with a value of -1e20 for all + // faces, since this value will + // strike in the results if + // something should go wrong and + // we fail to compute the value + // for a face for some + // reason. Secondly, we + // initialize the map once before + // we branch to different threads + // since this way the map's + // structure is no more modified + // by the individual threads, + // only existing entries are set + // to new values. This relieves + // us from the necessity to + // synchronise the threads + // through a mutex each time they + // write to (and modify the + // structure of) this map. FaceIntegrals face_integrals; for (active_cell_iterator cell=dual_solver.dof_handler.begin_active(); cell!=dual_solver.dof_handler.end(); @@ -2979,19 +2981,19 @@ namespace Step14 ++face_no) face_integrals[cell->face(face_no)] = -1e20; - // Then set up a vector with - // error indicators. Reserve one - // slot for each cell and set it - // to zero. + // Then set up a vector with + // error indicators. Reserve one + // slot for each cell and set it + // to zero. error_indicators.reinit (dual_solver.dof_handler .get_tria().n_active_cells()); - // Now start a number of threads - // which compute the error - // formula on parts of all the - // cells, and once they are all - // started wait until they have - // all finished: + // Now start a number of threads + // which compute the error + // formula on parts of all the + // cells, and once they are all + // started wait until they have + // all finished: const unsigned int n_threads = multithread_info.n_default_threads; Threads::ThreadGroup<> threads; for (unsigned int i=0; iface(face_no)]; + -= 0.5*face_integrals[cell->face(face_no)]; } std::cout << " Estimated error=" << std::accumulate (error_indicators.begin(), @@ -3038,15 +3040,15 @@ namespace Step14 } - // @sect4{Estimating on a subset of cells} + // @sect4{Estimating on a subset of cells} - // Next we have the function that - // is called to estimate the error - // on a subset of cells. The - // function may be called multiply - // if the library was configured to - // use multi-threading. Here it - // goes: + // Next we have the function that + // is called to estimate the error + // on a subset of cells. The + // function may be called multiply + // if the library was configured to + // use multi-threading. Here it + // goes: template void WeightedResidual:: @@ -3060,181 +3062,181 @@ namespace Step14 const PrimalSolver &primal_solver = *this; const DualSolver &dual_solver = *this; - // At the beginning, we - // initialize two variables for - // each thread which may be - // running this function. The - // reason for these functions was - // discussed above, when the - // respective classes were - // discussed, so we here only - // point out that since they are - // local to the function that is - // spawned when running more than - // one thread, the data of these - // objects exists actually once - // per thread, so we don't have - // to take care about - // synchronising access to them. + // At the beginning, we + // initialize two variables for + // each thread which may be + // running this function. The + // reason for these functions was + // discussed above, when the + // respective classes were + // discussed, so we here only + // point out that since they are + // local to the function that is + // spawned when running more than + // one thread, the data of these + // objects exists actually once + // per thread, so we don't have + // to take care about + // synchronising access to them. CellData cell_data (*dual_solver.fe, *dual_solver.quadrature, *primal_solver.rhs_function); FaceData face_data (*dual_solver.fe, *dual_solver.face_quadrature); - // Then calculate the start cell - // for this thread. We let the - // different threads run on - // interleaved cells, i.e. for - // example if we have 4 threads, - // then the first thread treates - // cells 0, 4, 8, etc, while the - // second threads works on cells 1, - // 5, 9, and so on. The reason is - // that it takes vastly more time - // to work on cells with hanging - // nodes than on regular cells, but - // such cells are not evenly - // distributed across the range of - // cell iterators, so in order to - // have the different threads do - // approximately the same amount of - // work, we have to let them work - // interleaved to the effect of a - // pseudorandom distribution of the - // `hard' cells to the different - // threads. + // Then calculate the start cell + // for this thread. We let the + // different threads run on + // interleaved cells, i.e. for + // example if we have 4 threads, + // then the first thread treates + // cells 0, 4, 8, etc, while the + // second threads works on cells 1, + // 5, 9, and so on. The reason is + // that it takes vastly more time + // to work on cells with hanging + // nodes than on regular cells, but + // such cells are not evenly + // distributed across the range of + // cell iterators, so in order to + // have the different threads do + // approximately the same amount of + // work, we have to let them work + // interleaved to the effect of a + // pseudorandom distribution of the + // `hard' cells to the different + // threads. active_cell_iterator cell=dual_solver.dof_handler.begin_active(); for (unsigned int t=0; (terror_indicators - // variable: + // First task on each cell is + // to compute the cell + // residual contributions of + // this cell, and put them + // into the + // error_indicators + // variable: integrate_over_cell (cell, cell_index, primal_solution, dual_weights, cell_data, error_indicators); - // After computing the cell - // terms, turn to the face - // terms. For this, loop over - // all faces of the present - // cell, and see whether - // something needs to be - // computed on it: + // After computing the cell + // terms, turn to the face + // terms. For this, loop over + // all faces of the present + // cell, and see whether + // something needs to be + // computed on it: for (unsigned int face_no=0; face_no::faces_per_cell; ++face_no) { - // First, if this face is - // part of the boundary, - // then there is nothing - // to do. However, to - // make things easier - // when summing up the - // contributions of the - // faces of cells, we - // enter this face into - // the list of faces with - // a zero contribution to - // the error. + // First, if this face is + // part of the boundary, + // then there is nothing + // to do. However, to + // make things easier + // when summing up the + // contributions of the + // faces of cells, we + // enter this face into + // the list of faces with + // a zero contribution to + // the error. if (cell->face(face_no)->at_boundary()) { face_integrals[cell->face(face_no)] = 0; continue; } - // Next, note that since - // we want to compute the - // jump terms on each - // face only once - // although we access it - // twice (if it is not at - // the boundary), we have - // to define some rules - // who is responsible for - // computing on a face: - // - // First, if the - // neighboring cell is on - // the same level as this - // one, i.e. neither - // further refined not - // coarser, then the one - // with the lower index - // within this level does - // the work. In other - // words: if the other - // one has a lower index, - // then skip work on this - // face: + // Next, note that since + // we want to compute the + // jump terms on each + // face only once + // although we access it + // twice (if it is not at + // the boundary), we have + // to define some rules + // who is responsible for + // computing on a face: + // + // First, if the + // neighboring cell is on + // the same level as this + // one, i.e. neither + // further refined not + // coarser, then the one + // with the lower index + // within this level does + // the work. In other + // words: if the other + // one has a lower index, + // then skip work on this + // face: if ((cell->neighbor(face_no)->has_children() == false) && (cell->neighbor(face_no)->level() == cell->level()) && (cell->neighbor(face_no)->index() < cell->index())) continue; - // Likewise, we always - // work from the coarser - // cell if this and its - // neighbor differ in - // refinement. Thus, if - // the neighboring cell - // is less refined than - // the present one, then - // do nothing since we - // integrate over the - // subfaces when we visit - // the coarse cell. + // Likewise, we always + // work from the coarser + // cell if this and its + // neighbor differ in + // refinement. Thus, if + // the neighboring cell + // is less refined than + // the present one, then + // do nothing since we + // integrate over the + // subfaces when we visit + // the coarse cell. if (cell->at_boundary(face_no) == false) if (cell->neighbor(face_no)->level() < cell->level()) continue; - // Now we know that we - // are in charge here, so - // actually compute the - // face jump terms. If - // the face is a regular - // one, i.e. the other - // side's cell is neither - // coarser not finer than - // this cell, then call - // one function, and if - // the cell on the other - // side is further - // refined, then use - // another function. Note - // that the case that the - // cell on the other side - // is coarser cannot - // happen since we have - // decided above that we - // handle this case when - // we pass over that - // other cell. + // Now we know that we + // are in charge here, so + // actually compute the + // face jump terms. If + // the face is a regular + // one, i.e. the other + // side's cell is neither + // coarser not finer than + // this cell, then call + // one function, and if + // the cell on the other + // side is further + // refined, then use + // another function. Note + // that the case that the + // cell on the other side + // is coarser cannot + // happen since we have + // decided above that we + // handle this case when + // we pass over that + // other cell. if (cell->face(face_no)->has_children() == false) integrate_over_regular_face (cell, face_no, primal_solution, @@ -3249,16 +3251,16 @@ namespace Step14 face_integrals); } - // After computing the cell - // contributions and looping - // over the faces, go to the - // next cell for this - // thread. Note again that - // the cells for each of the - // threads are interleaved. - // If we are at the end of - // our workload, jump out - // of the loop. + // After computing the cell + // contributions and looping + // over the faces, go to the + // next cell for this + // thread. Note again that + // the cells for each of the + // threads are interleaved. + // If we are at the end of + // our workload, jump out + // of the loop. for (unsigned int t=0; ((t void WeightedResidual:: integrate_over_cell (const active_cell_iterator &cell, @@ -3284,29 +3286,29 @@ namespace Step14 CellData &cell_data, Vector &error_indicators) const { - // The tasks to be done are what - // appears natural from looking - // at the error estimation - // formula: first get the - // right hand side and - // Laplacian of the numerical - // solution at the quadrature - // points for the cell residual, + // The tasks to be done are what + // appears natural from looking + // at the error estimation + // formula: first get the + // right hand side and + // Laplacian of the numerical + // solution at the quadrature + // points for the cell residual, cell_data.fe_values.reinit (cell); cell_data.right_hand_side - ->value_list (cell_data.fe_values.get_quadrature_points(), - cell_data.rhs_values); + ->value_list (cell_data.fe_values.get_quadrature_points(), + cell_data.rhs_values); cell_data.fe_values.get_function_laplacians (primal_solution, cell_data.cell_laplacians); - // ...then get the dual weights... + // ...then get the dual weights... cell_data.fe_values.get_function_values (dual_weights, cell_data.dual_weights); - // ...and finally build the sum - // over all quadrature points and - // store it with the present - // cell: + // ...and finally build the sum + // over all quadrature points and + // store it with the present + // cell: double sum = 0; for (unsigned int p=0; p void WeightedResidual:: integrate_over_regular_face (const active_cell_iterator &cell, @@ -3339,128 +3341,128 @@ namespace Step14 FaceIntegrals &face_integrals) const { const unsigned int - n_q_points = face_data.fe_face_values_cell.n_quadrature_points; - - // The first step is to get the - // values of the gradients at the - // quadrature points of the - // finite element field on the - // present cell. For this, - // initialize the - // FEFaceValues object - // corresponding to this side of - // the face, and extract the - // gradients using that - // object. + n_q_points = face_data.fe_face_values_cell.n_quadrature_points; + + // The first step is to get the + // values of the gradients at the + // quadrature points of the + // finite element field on the + // present cell. For this, + // initialize the + // FEFaceValues object + // corresponding to this side of + // the face, and extract the + // gradients using that + // object. face_data.fe_face_values_cell.reinit (cell, face_no); face_data.fe_face_values_cell.get_function_grads (primal_solution, face_data.cell_grads); - // The second step is then to - // extract the gradients of the - // finite element solution at the - // quadrature points on the other - // side of the face, i.e. from - // the neighboring cell. - // - // For this, do a sanity check - // before: make sure that the - // neigbor actually exists (yes, - // we should not have come here - // if the neighbor did not exist, - // but in complicated software - // there are bugs, so better - // check this), and if this is - // not the case throw an error. + // The second step is then to + // extract the gradients of the + // finite element solution at the + // quadrature points on the other + // side of the face, i.e. from + // the neighboring cell. + // + // For this, do a sanity check + // before: make sure that the + // neigbor actually exists (yes, + // we should not have come here + // if the neighbor did not exist, + // but in complicated software + // there are bugs, so better + // check this), and if this is + // not the case throw an error. Assert (cell->neighbor(face_no).state() == IteratorState::valid, ExcInternalError()); - // If we have that, then we need - // to find out with which face of - // the neighboring cell we have - // to work, i.e. the - // home-manythe neighbor the - // present cell is of the cell - // behind the present face. For - // this, there is a function, and - // we put the result into a - // variable with the name - // neighbor_neighbor: + // If we have that, then we need + // to find out with which face of + // the neighboring cell we have + // to work, i.e. the + // home-manythe neighbor the + // present cell is of the cell + // behind the present face. For + // this, there is a function, and + // we put the result into a + // variable with the name + // neighbor_neighbor: const unsigned int - neighbor_neighbor = cell->neighbor_of_neighbor (face_no); - // Then define an abbreviation - // for the neigbor cell, - // initialize the - // FEFaceValues object on - // that cell, and extract the - // gradients on that cell: + neighbor_neighbor = cell->neighbor_of_neighbor (face_no); + // Then define an abbreviation + // for the neigbor cell, + // initialize the + // FEFaceValues object on + // that cell, and extract the + // gradients on that cell: const active_cell_iterator neighbor = cell->neighbor(face_no); face_data.fe_face_values_neighbor.reinit (neighbor, neighbor_neighbor); face_data.fe_face_values_neighbor.get_function_grads (primal_solution, face_data.neighbor_grads); - // Now that we have the gradients - // on this and the neighboring - // cell, compute the jump - // residual by multiplying the - // jump in the gradient with the - // normal vector: + // Now that we have the gradients + // on this and the neighboring + // cell, compute the jump + // residual by multiplying the + // jump in the gradient with the + // normal vector: for (unsigned int p=0; pface(face_no)) != face_integrals.end(), ExcInternalError()); Assert (face_integrals[cell->face(face_no)] == -1e20, ExcInternalError()); - // ...then store computed value - // at assigned location. Note - // that the stored value does not - // contain the factor 1/2 that - // appears in the error - // representation. The reason is - // that the term actually does - // not have this factor if we - // loop over all faces in the - // triangulation, but only - // appears if we write it as a - // sum over all cells and all - // faces of each cell; we thus - // visit the same face twice. We - // take account of this by using - // this factor -1/2 later, when we - // sum up the contributions for - // each cell individually. + // ...then store computed value + // at assigned location. Note + // that the stored value does not + // contain the factor 1/2 that + // appears in the error + // representation. The reason is + // that the term actually does + // not have this factor if we + // loop over all faces in the + // triangulation, but only + // appears if we write it as a + // sum over all cells and all + // faces of each cell; we thus + // visit the same face twice. We + // take account of this by using + // this factor -1/2 later, when we + // sum up the contributions for + // each cell individually. face_integrals[cell->face(face_no)] = face_integral; } - // @sect4{Computing edge term error contributions -- 2} + // @sect4{Computing edge term error contributions -- 2} - // We are still missing the case of - // faces with hanging nodes. This - // is what is covered in this - // function: + // We are still missing the case of + // faces with hanging nodes. This + // is what is covered in this + // function: template void WeightedResidual:: integrate_over_irregular_face (const active_cell_iterator &cell, @@ -3470,107 +3472,107 @@ namespace Step14 FaceData &face_data, FaceIntegrals &face_integrals) const { - // First again two abbreviations, - // and some consistency checks - // whether the function is called - // only on faces for which it is - // supposed to be called: + // First again two abbreviations, + // and some consistency checks + // whether the function is called + // only on faces for which it is + // supposed to be called: const unsigned int - n_q_points = face_data.fe_face_values_cell.n_quadrature_points; + n_q_points = face_data.fe_face_values_cell.n_quadrature_points; const typename DoFHandler::face_iterator - face = cell->face(face_no); + face = cell->face(face_no); const typename DoFHandler::cell_iterator - neighbor = cell->neighbor(face_no); + neighbor = cell->neighbor(face_no); Assert (neighbor.state() == IteratorState::valid, ExcInternalError()); Assert (neighbor->has_children(), ExcInternalError()); - // Then find out which neighbor - // the present cell is of the - // adjacent cell. Note that we - // will operator on the children - // of this adjacent cell, but - // that their orientation is the - // same as that of their mother, - // i.e. the neigbor direction is - // the same. + // Then find out which neighbor + // the present cell is of the + // adjacent cell. Note that we + // will operator on the children + // of this adjacent cell, but + // that their orientation is the + // same as that of their mother, + // i.e. the neigbor direction is + // the same. const unsigned int - neighbor_neighbor = cell->neighbor_of_neighbor (face_no); + neighbor_neighbor = cell->neighbor_of_neighbor (face_no); - // Then simply do everything we - // did in the previous function - // for one face for all the - // sub-faces now: + // Then simply do everything we + // did in the previous function + // for one face for all the + // sub-faces now: for (unsigned int subface_no=0; subface_non_children(); ++subface_no) { - // Start with some checks - // again: get an iterator - // pointing to the cell - // behind the present subface - // and check whether its face - // is a subface of the one we - // are considering. If that - // were not the case, then - // there would be either a - // bug in the - // neighbor_neighbor - // function called above, or - // -- worse -- some function - // in the library did not - // keep to some underlying - // assumptions about cells, - // their children, and their - // faces. In any case, even - // though this assertion - // should not be triggered, - // it does not harm to be - // cautious, and in optimized - // mode computations the - // assertion will be removed - // anyway. + // Start with some checks + // again: get an iterator + // pointing to the cell + // behind the present subface + // and check whether its face + // is a subface of the one we + // are considering. If that + // were not the case, then + // there would be either a + // bug in the + // neighbor_neighbor + // function called above, or + // -- worse -- some function + // in the library did not + // keep to some underlying + // assumptions about cells, + // their children, and their + // faces. In any case, even + // though this assertion + // should not be triggered, + // it does not harm to be + // cautious, and in optimized + // mode computations the + // assertion will be removed + // anyway. const active_cell_iterator neighbor_child = cell->neighbor_child_on_subface (face_no, subface_no); Assert (neighbor_child->face(neighbor_neighbor) == cell->face(face_no)->child(subface_no), ExcInternalError()); - // Now start the work by - // again getting the gradient - // of the solution first at - // this side of the - // interface, + // Now start the work by + // again getting the gradient + // of the solution first at + // this side of the + // interface, face_data.fe_subface_values_cell.reinit (cell, face_no, subface_no); face_data.fe_subface_values_cell.get_function_grads (primal_solution, face_data.cell_grads); - // then at the other side, + // then at the other side, face_data.fe_face_values_neighbor.reinit (neighbor_child, neighbor_neighbor); face_data.fe_face_values_neighbor.get_function_grads (primal_solution, face_data.neighbor_grads); - // and finally building the - // jump residuals. Since we - // take the normal vector - // from the other cell this - // time, revert the sign of - // the first term compared to - // the other function: + // and finally building the + // jump residuals. Since we + // take the normal vector + // from the other cell this + // time, revert the sign of + // the first term compared to + // the other function: for (unsigned int p=0; pn_children(); ++subface_no) @@ -3603,291 +3605,292 @@ namespace Step14 sum += face_integrals[face->child(subface_no)]; } - // Finally store the value with - // the parent face. + // Finally store the value with + // the parent face. face_integrals[face] = sum; } } - // @sect3{A simulation framework} - - // In the previous example program, - // we have had two functions that - // were used to drive the process of - // solving on subsequently finer - // grids. We extend this here to - // allow for a number of parameters - // to be passed to these functions, - // and put all of that into framework - // class. - // - // You will have noted that this - // program is built up of a number of - // small parts (evaluation functions, - // solver classes implementing - // various refinement methods, - // different dual functionals, - // different problem and data - // descriptions), which makes the - // program relatively simple to - // extend, but also allows to solve a - // large number of different problems - // by replacing one part by - // another. We reflect this - // flexibility by declaring a - // structure in the following - // framework class that holds a - // number of parameters that may be - // set to test various combinations - // of the parts of this program, and - // which can be used to test it at - // various problems and - // discretizations in a simple way. + // @sect3{A simulation framework} + + // In the previous example program, + // we have had two functions that + // were used to drive the process of + // solving on subsequently finer + // grids. We extend this here to + // allow for a number of parameters + // to be passed to these functions, + // and put all of that into framework + // class. + // + // You will have noted that this + // program is built up of a number of + // small parts (evaluation functions, + // solver classes implementing + // various refinement methods, + // different dual functionals, + // different problem and data + // descriptions), which makes the + // program relatively simple to + // extend, but also allows to solve a + // large number of different problems + // by replacing one part by + // another. We reflect this + // flexibility by declaring a + // structure in the following + // framework class that holds a + // number of parameters that may be + // set to test various combinations + // of the parts of this program, and + // which can be used to test it at + // various problems and + // discretizations in a simple way. template struct Framework { - public: - // First, we declare two - // abbreviations for simple use - // of the respective data types: - typedef Evaluation::EvaluationBase Evaluator; - typedef std::list EvaluatorList; - - - // Then we have the structure - // which declares all the - // parameters that may be set. In - // the default constructor of the - // structure, these values are - // all set to default values, for - // simple use. - struct ProblemDescription + public: + // First, we declare two + // abbreviations for simple use + // of the respective data types: + typedef Evaluation::EvaluationBase Evaluator; + typedef std::list EvaluatorList; + + + // Then we have the structure + // which declares all the + // parameters that may be set. In + // the default constructor of the + // structure, these values are + // all set to default values, for + // simple use. + struct ProblemDescription + { + // First allow for the + // degrees of the piecewise + // polynomials by which the + // primal and dual problems + // will be discretized. They + // default to (bi-, + // tri-)linear ansatz + // functions for the primal, + // and (bi-, tri-)quadratic + // ones for the dual + // problem. If a refinement + // criterion is chosen that + // does not need the solution + // of a dual problem, the + // value of the dual finite + // element degree is of + // course ignored. + unsigned int primal_fe_degree; + unsigned int dual_fe_degree; + + // Then have an object that + // describes the problem + // type, i.e. right hand + // side, domain, boundary + // values, etc. The pointer + // needed here defaults to + // the Null pointer, i.e. you + // will have to set it in + // actual instances of this + // object to make it useful. + SmartPointer > data; + + // Since we allow to use + // different refinement + // criteria (global + // refinement, refinement by + // the Kelly error indicator, + // possibly with a weight, + // and using the dual + // estimator), define a + // number of enumeration + // values, and subsequently a + // variable of that type. It + // will default to + // dual_weighted_error_estimator. + enum RefinementCriterion { - // First allow for the - // degrees of the piecewise - // polynomials by which the - // primal and dual problems - // will be discretized. They - // default to (bi-, - // tri-)linear ansatz - // functions for the primal, - // and (bi-, tri-)quadratic - // ones for the dual - // problem. If a refinement - // criterion is chosen that - // does not need the solution - // of a dual problem, the - // value of the dual finite - // element degree is of - // course ignored. - unsigned int primal_fe_degree; - unsigned int dual_fe_degree; - - // Then have an object that - // describes the problem - // type, i.e. right hand - // side, domain, boundary - // values, etc. The pointer - // needed here defaults to - // the Null pointer, i.e. you - // will have to set it in - // actual instances of this - // object to make it useful. - SmartPointer > data; - - // Since we allow to use - // different refinement - // criteria (global - // refinement, refinement by - // the Kelly error indicator, - // possibly with a weight, - // and using the dual - // estimator), define a - // number of enumeration - // values, and subsequently a - // variable of that type. It - // will default to - // dual_weighted_error_estimator. - enum RefinementCriterion { - dual_weighted_error_estimator, - global_refinement, - kelly_indicator, - weighted_kelly_indicator - }; - - RefinementCriterion refinement_criterion; - - // Next, an object that - // describes the dual - // functional. It is only - // needed if the dual - // weighted residual - // refinement is chosen, and - // also defaults to a Null - // pointer. - SmartPointer > dual_functional; - - // Then a list of evaluation - // objects. Its default value - // is empty, i.e. no - // evaluation objects. - EvaluatorList evaluator_list; - - // Next to last, a function - // that is used as a weight - // to the - // RefinementWeightedKelly - // class. The default value - // of this pointer is zero, - // but you have to set it to - // some other value if you - // want to use the - // weighted_kelly_indicator - // refinement criterion. - SmartPointer > kelly_weight; - - // Finally, we have a - // variable that denotes the - // maximum number of degrees - // of freedom we allow for - // the (primal) - // discretization. If it is - // exceeded, we stop the - // process of solving and - // intermittend mesh - // refinement. Its default - // value is 20,000. - unsigned int max_degrees_of_freedom; - - // Finally the default - // constructor of this class: - ProblemDescription (); + dual_weighted_error_estimator, + global_refinement, + kelly_indicator, + weighted_kelly_indicator }; - // The driver framework class - // only has one method which - // calls solver and mesh - // refinement intermittently, and - // does some other small tasks in - // between. Since it does not - // need data besides the - // parameters given to it, we - // make it static: - static void run (const ProblemDescription &descriptor); + RefinementCriterion refinement_criterion; + + // Next, an object that + // describes the dual + // functional. It is only + // needed if the dual + // weighted residual + // refinement is chosen, and + // also defaults to a Null + // pointer. + SmartPointer > dual_functional; + + // Then a list of evaluation + // objects. Its default value + // is empty, i.e. no + // evaluation objects. + EvaluatorList evaluator_list; + + // Next to last, a function + // that is used as a weight + // to the + // RefinementWeightedKelly + // class. The default value + // of this pointer is zero, + // but you have to set it to + // some other value if you + // want to use the + // weighted_kelly_indicator + // refinement criterion. + SmartPointer > kelly_weight; + + // Finally, we have a + // variable that denotes the + // maximum number of degrees + // of freedom we allow for + // the (primal) + // discretization. If it is + // exceeded, we stop the + // process of solving and + // intermittend mesh + // refinement. Its default + // value is 20,000. + unsigned int max_degrees_of_freedom; + + // Finally the default + // constructor of this class: + ProblemDescription (); + }; + + // The driver framework class + // only has one method which + // calls solver and mesh + // refinement intermittently, and + // does some other small tasks in + // between. Since it does not + // need data besides the + // parameters given to it, we + // make it static: + static void run (const ProblemDescription &descriptor); }; - // As for the implementation, first - // the constructor of the parameter - // object, setting all values to - // their defaults: + // As for the implementation, first + // the constructor of the parameter + // object, setting all values to + // their defaults: template Framework::ProblemDescription::ProblemDescription () - : - primal_fe_degree (1), - dual_fe_degree (2), - refinement_criterion (dual_weighted_error_estimator), - max_degrees_of_freedom (20000) + : + primal_fe_degree (1), + dual_fe_degree (2), + refinement_criterion (dual_weighted_error_estimator), + max_degrees_of_freedom (20000) {} - // Then the function which drives the - // whole process: + // Then the function which drives the + // whole process: template void Framework::run (const ProblemDescription &descriptor) { - // First create a triangulation - // from the given data object, + // First create a triangulation + // from the given data object, Triangulation - triangulation (Triangulation::smoothing_on_refinement); + triangulation (Triangulation::smoothing_on_refinement); descriptor.data->create_coarse_grid (triangulation); - // then a set of finite elements - // and appropriate quadrature - // formula: + // then a set of finite elements + // and appropriate quadrature + // formula: const FE_Q primal_fe(descriptor.primal_fe_degree); const FE_Q dual_fe(descriptor.dual_fe_degree); const QGauss quadrature(descriptor.dual_fe_degree+1); const QGauss face_quadrature(descriptor.dual_fe_degree+1); - // Next, select one of the classes - // implementing different - // refinement criteria. - LaplaceSolver::Base * solver = 0; + // Next, select one of the classes + // implementing different + // refinement criteria. + LaplaceSolver::Base *solver = 0; switch (descriptor.refinement_criterion) { - case ProblemDescription::dual_weighted_error_estimator: - { - solver - = new LaplaceSolver::WeightedResidual (triangulation, - primal_fe, - dual_fe, - quadrature, - face_quadrature, - descriptor.data->get_right_hand_side(), - descriptor.data->get_boundary_values(), - *descriptor.dual_functional); - break; - } + case ProblemDescription::dual_weighted_error_estimator: + { + solver + = new LaplaceSolver::WeightedResidual (triangulation, + primal_fe, + dual_fe, + quadrature, + face_quadrature, + descriptor.data->get_right_hand_side(), + descriptor.data->get_boundary_values(), + *descriptor.dual_functional); + break; + } - case ProblemDescription::global_refinement: - { - solver - = new LaplaceSolver::RefinementGlobal (triangulation, - primal_fe, - quadrature, - face_quadrature, - descriptor.data->get_right_hand_side(), - descriptor.data->get_boundary_values()); - break; - } + case ProblemDescription::global_refinement: + { + solver + = new LaplaceSolver::RefinementGlobal (triangulation, + primal_fe, + quadrature, + face_quadrature, + descriptor.data->get_right_hand_side(), + descriptor.data->get_boundary_values()); + break; + } - case ProblemDescription::kelly_indicator: - { - solver - = new LaplaceSolver::RefinementKelly (triangulation, - primal_fe, - quadrature, - face_quadrature, - descriptor.data->get_right_hand_side(), - descriptor.data->get_boundary_values()); - break; - } + case ProblemDescription::kelly_indicator: + { + solver + = new LaplaceSolver::RefinementKelly (triangulation, + primal_fe, + quadrature, + face_quadrature, + descriptor.data->get_right_hand_side(), + descriptor.data->get_boundary_values()); + break; + } - case ProblemDescription::weighted_kelly_indicator: - { - solver - = new LaplaceSolver::RefinementWeightedKelly (triangulation, - primal_fe, - quadrature, - face_quadrature, - descriptor.data->get_right_hand_side(), - descriptor.data->get_boundary_values(), - *descriptor.kelly_weight); - break; - } + case ProblemDescription::weighted_kelly_indicator: + { + solver + = new LaplaceSolver::RefinementWeightedKelly (triangulation, + primal_fe, + quadrature, + face_quadrature, + descriptor.data->get_right_hand_side(), + descriptor.data->get_boundary_values(), + *descriptor.kelly_weight); + break; + } - default: - AssertThrow (false, ExcInternalError()); + default: + AssertThrow (false, ExcInternalError()); } - // Now that all objects are in - // place, run the main loop. The - // stopping criterion is - // implemented at the bottom of the - // loop. - // - // In the loop, first set the new - // cycle number, then solve the - // problem, output its solution(s), - // apply the evaluation objects to - // it, then decide whether we want - // to refine the mesh further and - // solve again on this mesh, or - // jump out of the loop. + // Now that all objects are in + // place, run the main loop. The + // stopping criterion is + // implemented at the bottom of the + // loop. + // + // In the loop, first set the new + // cycle number, then solve the + // problem, output its solution(s), + // apply the evaluation objects to + // it, then decide whether we want + // to refine the mesh further and + // solve again on this mesh, or + // jump out of the loop. for (unsigned int step=0; true; ++step) { std::cout << "Refinement cycle: " << step @@ -3901,7 +3904,7 @@ namespace Step14 << solver->n_dofs() << std::endl; for (typename EvaluatorList::const_iterator - e = descriptor.evaluator_list.begin(); + e = descriptor.evaluator_list.begin(); e != descriptor.evaluator_list.end(); ++e) { (*e)->set_refinement_cycle (step); @@ -3915,9 +3918,9 @@ namespace Step14 break; } - // After the loop has run, clean up - // the screen, and delete objects - // no more needed: + // After the loop has run, clean up + // the screen, and delete objects + // no more needed: std::cout << std::endl; delete solver; solver = 0; @@ -3927,17 +3930,17 @@ namespace Step14 - // @sect3{The main function} +// @sect3{The main function} - // Here finally comes the main - // function. It drives the whole - // process by specifying a set of - // parameters to be used for the - // simulation (polynomial degrees, - // evaluation and dual functionals, - // etc), and passes them packed into - // a structure to the frame work - // class above. +// Here finally comes the main +// function. It drives the whole +// process by specifying a set of +// parameters to be used for the +// simulation (polynomial degrees, +// evaluation and dual functionals, +// etc), and passes them packed into +// a structure to the frame work +// class above. int main () { try @@ -3946,107 +3949,107 @@ int main () using namespace Step14; deallog.depth_console (0); - // Describe the problem we want - // to solve here by passing a - // descriptor object to the - // function doing the rest of - // the work: + // Describe the problem we want + // to solve here by passing a + // descriptor object to the + // function doing the rest of + // the work: const unsigned int dim = 2; Framework::ProblemDescription descriptor; - // First set the refinement - // criterion we wish to use: + // First set the refinement + // criterion we wish to use: descriptor.refinement_criterion = Framework::ProblemDescription::dual_weighted_error_estimator; - // Here, we could as well have - // used global_refinement - // or - // weighted_kelly_indicator. Note - // that the information given - // about dual finite elements, - // dual functional, etc is only - // important for the given - // choice of refinement - // criterion, and is ignored - // otherwise. - - // Then set the polynomial - // degrees of primal and dual - // problem. We choose here - // bi-linear and bi-quadratic - // ones: + // Here, we could as well have + // used global_refinement + // or + // weighted_kelly_indicator. Note + // that the information given + // about dual finite elements, + // dual functional, etc is only + // important for the given + // choice of refinement + // criterion, and is ignored + // otherwise. + + // Then set the polynomial + // degrees of primal and dual + // problem. We choose here + // bi-linear and bi-quadratic + // ones: descriptor.primal_fe_degree = 1; descriptor.dual_fe_degree = 2; - // Then set the description of - // the test case, i.e. domain, - // boundary values, and right - // hand side. These are - // prepackaged in classes. We - // take here the description of - // Exercise_2_3, but you - // can also use - // CurvedRidges@: + // Then set the description of + // the test case, i.e. domain, + // boundary values, and right + // hand side. These are + // prepackaged in classes. We + // take here the description of + // Exercise_2_3, but you + // can also use + // CurvedRidges@: descriptor.data = new Data::SetUp,dim> (); - // Next set first a dual - // functional, then a list of - // evaluation objects. We - // choose as default the - // evaluation of the - // value at an - // evaluation point, - // represented by the classes - // PointValueEvaluation - // in the namespaces of - // evaluation and dual - // functional classes. You can - // also set the - // PointXDerivativeEvaluation - // classes for the x-derivative - // instead of the value - // at the evaluation point. - // - // Note that dual functional - // and evaluation objects - // should match. However, you - // can give as many evaluation - // functionals as you want, so - // you can have both point - // value and derivative - // evaluated after each step. - // One such additional - // evaluation is to output the - // grid in each step. + // Next set first a dual + // functional, then a list of + // evaluation objects. We + // choose as default the + // evaluation of the + // value at an + // evaluation point, + // represented by the classes + // PointValueEvaluation + // in the namespaces of + // evaluation and dual + // functional classes. You can + // also set the + // PointXDerivativeEvaluation + // classes for the x-derivative + // instead of the value + // at the evaluation point. + // + // Note that dual functional + // and evaluation objects + // should match. However, you + // can give as many evaluation + // functionals as you want, so + // you can have both point + // value and derivative + // evaluated after each step. + // One such additional + // evaluation is to output the + // grid in each step. const Point evaluation_point (0.75, 0.75); descriptor.dual_functional = new DualFunctional::PointValueEvaluation (evaluation_point); Evaluation::PointValueEvaluation - postprocessor1 (evaluation_point); + postprocessor1 (evaluation_point); Evaluation::GridOutput - postprocessor2 ("grid"); + postprocessor2 ("grid"); descriptor.evaluator_list.push_back (&postprocessor1); descriptor.evaluator_list.push_back (&postprocessor2); - // Set the maximal number of - // degrees of freedom after - // which we want the program to - // stop refining the mesh - // further: + // Set the maximal number of + // degrees of freedom after + // which we want the program to + // stop refining the mesh + // further: descriptor.max_degrees_of_freedom = 20000; - // Finally pass the descriptor - // object to a function that - // runs the entire solution - // with it: + // Finally pass the descriptor + // object to a function that + // runs the entire solution + // with it: Framework::run (descriptor); } - // Catch exceptions to give - // information about things that - // failed: + // Catch exceptions to give + // information about things that + // failed: catch (std::exception &exc) { std::cerr << std::endl << std::endl diff --git a/deal.II/examples/step-15/step-15.cc b/deal.II/examples/step-15/step-15.cc index d149599e2b..655090b9ad 100644 --- a/deal.II/examples/step-15/step-15.cc +++ b/deal.II/examples/step-15/step-15.cc @@ -10,12 +10,12 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // The first few files have already - // been covered in previous examples - // and will thus not be further - // commented on. +// The first few files have already +// been covered in previous examples +// and will thus not be further +// commented on. #include #include #include @@ -52,140 +52,140 @@ #include #include - // We will use adaptive mesh refinement - // between Newton interations. To do so, we - // need to be able to work with a solution on - // the new mesh, although it was computed on - // the old one. The SolutionTransfer class - // transfers the solution from the old to the - // new mesh: +// We will use adaptive mesh refinement +// between Newton interations. To do so, we +// need to be able to work with a solution on +// the new mesh, although it was computed on +// the old one. The SolutionTransfer class +// transfers the solution from the old to the +// new mesh: #include - // We then open a namepsace for this program - // and import everything from the dealii - // namespace into it, as in previous - // programs: +// We then open a namepsace for this program +// and import everything from the dealii +// namespace into it, as in previous +// programs: namespace Step15 { using namespace dealii; - // @sect3{The MinimalSurfaceProblem class template} - - // The class template is basically the same - // as in step-6. Four additions are made: - // - There are two solution vectors, one for - // the Newton update $\delta u^n$, and one - // for the current iterate $u^n$. - // - The setup_system function - // takes an argument that denotes whether - // this is the first time it is called or - // not. The difference is that the first - // time around we need to distributed - // degrees of freedom and set the - // solution vector for $u^n$ to the - // correct size. The following times, the - // function is called after we have - // already done these steps as part of - // refining the mesh in - // refine_mesh. - // - We then also need new functions: - // set_boundary_values() - // takes care of setting the boundary - // values on the solution vector - // correctly, as discussed at the end of - // the - // introduction. compute_residual() - // is a function that computes the norm - // of the nonlinear (discrete) - // residual. We use this function to - // monitor convergence of the Newton - // iteration. The function takes a step - // length $\alpha^n$ as argument to - // compute the residual of $u^n + - // \alpha^n \; \delta u^n$. This is - // something one typically needs for step - // length control, although we will not - // use this feature here. Finally, - // determine_step_length() - // computes the step length $\alpha^n$ in - // each Newton iteration. As discussed in - // the introduction, we here use a fixed - // step length and leave implementing a - // better strategy as an exercise. + // @sect3{The MinimalSurfaceProblem class template} + + // The class template is basically the same + // as in step-6. Four additions are made: + // - There are two solution vectors, one for + // the Newton update $\delta u^n$, and one + // for the current iterate $u^n$. + // - The setup_system function + // takes an argument that denotes whether + // this is the first time it is called or + // not. The difference is that the first + // time around we need to distributed + // degrees of freedom and set the + // solution vector for $u^n$ to the + // correct size. The following times, the + // function is called after we have + // already done these steps as part of + // refining the mesh in + // refine_mesh. + // - We then also need new functions: + // set_boundary_values() + // takes care of setting the boundary + // values on the solution vector + // correctly, as discussed at the end of + // the + // introduction. compute_residual() + // is a function that computes the norm + // of the nonlinear (discrete) + // residual. We use this function to + // monitor convergence of the Newton + // iteration. The function takes a step + // length $\alpha^n$ as argument to + // compute the residual of $u^n + + // \alpha^n \; \delta u^n$. This is + // something one typically needs for step + // length control, although we will not + // use this feature here. Finally, + // determine_step_length() + // computes the step length $\alpha^n$ in + // each Newton iteration. As discussed in + // the introduction, we here use a fixed + // step length and leave implementing a + // better strategy as an exercise. template class MinimalSurfaceProblem { - public: - MinimalSurfaceProblem (); - ~MinimalSurfaceProblem (); + public: + MinimalSurfaceProblem (); + ~MinimalSurfaceProblem (); - void run (); + void run (); - private: - void setup_system (const bool initial_step); - void assemble_system (); - void solve (); - void refine_mesh (); - void set_boundary_values (); - double compute_residual (const double alpha) const; - double determine_step_length () const; + private: + void setup_system (const bool initial_step); + void assemble_system (); + void solve (); + void refine_mesh (); + void set_boundary_values (); + double compute_residual (const double alpha) const; + double determine_step_length () const; - Triangulation triangulation; + Triangulation triangulation; - DoFHandler dof_handler; - FE_Q fe; + DoFHandler dof_handler; + FE_Q fe; - ConstraintMatrix hanging_node_constraints; + ConstraintMatrix hanging_node_constraints; - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; - Vector present_solution; - Vector newton_update; - Vector system_rhs; + Vector present_solution; + Vector newton_update; + Vector system_rhs; }; - // @sect3{Boundary condition} + // @sect3{Boundary condition} - // The boundary condition is - // implemented just like in step-4. - // It is chosen as $g(x,y)=\sin(2 - // \pi (x+y))$: + // The boundary condition is + // implemented just like in step-4. + // It is chosen as $g(x,y)=\sin(2 + // \pi (x+y))$: template class BoundaryValues : public Function { - public: - BoundaryValues () : Function() {} + public: + BoundaryValues () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; template double BoundaryValues::value (const Point &p, - const unsigned int /*component*/) const + const unsigned int /*component*/) const { return std::sin(2 * numbers::PI * (p[0]+p[1])); } - // @sect3{The MinimalSurfaceProblem class implementation} + // @sect3{The MinimalSurfaceProblem class implementation} - // @sect4{MinimalSurfaceProblem::MinimalSurfaceProblem} + // @sect4{MinimalSurfaceProblem::MinimalSurfaceProblem} - // The constructor and destructor - // of the class are the same as in - // the first few tutorials. + // The constructor and destructor + // of the class are the same as in + // the first few tutorials. template MinimalSurfaceProblem::MinimalSurfaceProblem () - : - dof_handler (triangulation), - fe (2) + : + dof_handler (triangulation), + fe (2) {} @@ -196,44 +196,44 @@ namespace Step15 dof_handler.clear (); } - // @sect4{MinimalSurfaceProblem::setup_system} - - // As always in the setup-system function, - // we setup the variables of the finite - // element method. There are same - // differences to step-6, because there we - // start solving the PDE from scratch in - // every refinement cycle whereas here we - // need to take the solution from the - // previous mesh onto the current - // mesh. Consequently, we can't just reset - // solution vectors. The argument passed to - // this function thus indicates whether we - // can distributed degrees of freedom (plus - // compute constraints) and set the - // solution vector to zero or whether this - // has happened elsewhere already - // (specifically, in - // refine_mesh()). + // @sect4{MinimalSurfaceProblem::setup_system} + + // As always in the setup-system function, + // we setup the variables of the finite + // element method. There are same + // differences to step-6, because there we + // start solving the PDE from scratch in + // every refinement cycle whereas here we + // need to take the solution from the + // previous mesh onto the current + // mesh. Consequently, we can't just reset + // solution vectors. The argument passed to + // this function thus indicates whether we + // can distributed degrees of freedom (plus + // compute constraints) and set the + // solution vector to zero or whether this + // has happened elsewhere already + // (specifically, in + // refine_mesh()). template void MinimalSurfaceProblem::setup_system (const bool initial_step) { if (initial_step) { - dof_handler.distribute_dofs (fe); - present_solution.reinit (dof_handler.n_dofs()); + dof_handler.distribute_dofs (fe); + present_solution.reinit (dof_handler.n_dofs()); - hanging_node_constraints.clear (); - DoFTools::make_hanging_node_constraints (dof_handler, - hanging_node_constraints); - hanging_node_constraints.close (); + hanging_node_constraints.clear (); + DoFTools::make_hanging_node_constraints (dof_handler, + hanging_node_constraints); + hanging_node_constraints.close (); } - // The remaining parts of the - // function are the same as in - // step-6. + // The remaining parts of the + // function are the same as in + // step-6. newton_update.reinit (dof_handler.n_dofs()); system_rhs.reinit (dof_handler.n_dofs()); @@ -247,27 +247,27 @@ namespace Step15 system_matrix.reinit (sparsity_pattern); } - // @sect4{MinimalSurfaceProblem::assemble_system} - - // This function does the same as in the - // previous tutorials except that now, of - // course, the matrix and right hand side - // functions depend on the previous - // iteration's solution. As discussed in - // the introduction, we need to use zero - // boundary values for the Newton updates; - // we compute them at the end of this - // function. - // - // The top of the function contains the - // usual boilerplate code, setting up the - // objects that allow us to evaluate shape - // functions at quadrature points and - // temporary storage locations for the - // local matrices and vectors, as well as - // for the gradients of the previous - // solution at the quadrature points. We - // then start the loop over all cells: + // @sect4{MinimalSurfaceProblem::assemble_system} + + // This function does the same as in the + // previous tutorials except that now, of + // course, the matrix and right hand side + // functions depend on the previous + // iteration's solution. As discussed in + // the introduction, we need to use zero + // boundary values for the Newton updates; + // we compute them at the end of this + // function. + // + // The top of the function contains the + // usual boilerplate code, setting up the + // objects that allow us to evaluate shape + // functions at quadrature points and + // temporary storage locations for the + // local matrices and vectors, as well as + // for the gradients of the previous + // solution at the quadrature points. We + // then start the loop over all cells: template void MinimalSurfaceProblem::assemble_system () { @@ -277,9 +277,9 @@ namespace Step15 system_rhs = 0; FEValues fe_values (fe, quadrature_formula, - update_gradients | - update_quadrature_points | - update_JxW_values); + update_gradients | + update_quadrature_points | + update_JxW_values); const unsigned int dofs_per_cell = fe.dofs_per_cell; const unsigned int n_q_points = quadrature_formula.size(); @@ -292,132 +292,132 @@ namespace Step15 std::vector local_dof_indices (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { - cell_matrix = 0; - cell_rhs = 0; - - fe_values.reinit (cell); - - // For the assembly of the linear - // system, we have to obtain the - // values of the previous solution's - // gradients at the quadrature - // points. There is a standard way of - // doing this: the - // FEValues::get_function function - // takes a vector that represents a - // finite element field defined on a - // DoFHandler, and evaluates the - // gradients of this field at the - // quadrature points of the cell with - // which the FEValues object has last - // been reinitialized. The values of - // the gradients at all quadrature - // points are then written into the - // second argument: - fe_values.get_function_gradients(present_solution, - old_solution_gradients); - - // With this, we can then do the - // integration loop over all - // quadrature points and shape - // functions. Having just computed - // the gradients of the old solution - // in the quadrature points, we are - // able to compute the coefficients - // $a_{n}$ in these points. The - // assembly of the system itself then - // looks similar to what we always do - // with the exception of the - // nonlinear terms, as does copying - // the results from the local objects - // into the global ones: - for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) - { - const double coeff - = 1.0 / std::sqrt(1 + - old_solution_gradients[q_point] * - old_solution_gradients[q_point]); - - for (unsigned int i=0; iget_dof_indices (local_dof_indices); - for (unsigned int i=0; iget_dof_indices (local_dof_indices); + for (unsigned int i=0; i boundary_values; VectorTools::interpolate_boundary_values (dof_handler, - 0, - ZeroFunction(), - boundary_values); + 0, + ZeroFunction(), + boundary_values); MatrixTools::apply_boundary_values (boundary_values, - system_matrix, - newton_update, - system_rhs); + system_matrix, + newton_update, + system_rhs); } - // @sect4{MinimalSurfaceProblem::solve} + // @sect4{MinimalSurfaceProblem::solve} - // The solve function is the same as - // always. At the end of the solution - // process we update the current solution - // by setting $u^{n+1}=u^n+\alpha^n\;\delta - // u^n$. + // The solve function is the same as + // always. At the end of the solution + // process we update the current solution + // by setting $u^{n+1}=u^n+\alpha^n\;\delta + // u^n$. template void MinimalSurfaceProblem::solve () { SolverControl solver_control (system_rhs.size(), - system_rhs.l2_norm()*1e-6); + system_rhs.l2_norm()*1e-6); SolverCG<> solver (solver_control); PreconditionSSOR<> preconditioner; preconditioner.initialize(system_matrix, 1.2); solver.solve (system_matrix, newton_update, system_rhs, - preconditioner); + preconditioner); hanging_node_constraints.distribute (newton_update); @@ -426,72 +426,72 @@ namespace Step15 } - // @sect4{MinimalSurfaceProblem::refine_mesh} + // @sect4{MinimalSurfaceProblem::refine_mesh} - // The first part of this function is the - // same as in step-6... However, after - // refining the mesh we have to transfer - // the old solution to the new one which we - // do with the help of the SolutionTransfer - // class. The process is slightly - // convoluted, so let us describe it in - // detail: + // The first part of this function is the + // same as in step-6... However, after + // refining the mesh we have to transfer + // the old solution to the new one which we + // do with the help of the SolutionTransfer + // class. The process is slightly + // convoluted, so let us describe it in + // detail: template void MinimalSurfaceProblem::refine_mesh () { Vector estimated_error_per_cell (triangulation.n_active_cells()); KellyErrorEstimator::estimate (dof_handler, - QGauss(3), - typename FunctionMap::type(), - present_solution, - estimated_error_per_cell); + QGauss(3), + typename FunctionMap::type(), + present_solution, + estimated_error_per_cell); GridRefinement::refine_and_coarsen_fixed_number (triangulation, - estimated_error_per_cell, - 0.3, 0.03); - - // Then we need an additional step: if, - // for example, you flag a cell that is - // once more refined than its neighbor, - // and that neighbor is not flagged for - // refinement, we would end up with a - // jump of two refinement levels across a - // cell interface. To avoid these - // situations, the library will silently - // also have to refine the neighbor cell - // once. It does so by calling the - // Triangulation::prepare_coarsening_and_refinement - // function before actually doing the - // refinement and coarsening. This - // function flags a set of additional - // cells for refinement or coarsening, to - // enforce rules like the - // one-hanging-node rule. The cells that - // are flagged for refinement and - // coarsening after calling this function - // are exactly the ones that will - // actually be refined or - // coarsened. Usually, you don't have to - // do this by hand - // (Triangulation::execute_coarsening_and_refinement - // does this for you). However, we need - // to initialize the SolutionTransfer - // class and it needs to know the final - // set of cells that will be coarsened or - // refined in order to store the data - // from the old mesh and transfer to the - // new one. Thus, we call the function by - // hand: + estimated_error_per_cell, + 0.3, 0.03); + + // Then we need an additional step: if, + // for example, you flag a cell that is + // once more refined than its neighbor, + // and that neighbor is not flagged for + // refinement, we would end up with a + // jump of two refinement levels across a + // cell interface. To avoid these + // situations, the library will silently + // also have to refine the neighbor cell + // once. It does so by calling the + // Triangulation::prepare_coarsening_and_refinement + // function before actually doing the + // refinement and coarsening. This + // function flags a set of additional + // cells for refinement or coarsening, to + // enforce rules like the + // one-hanging-node rule. The cells that + // are flagged for refinement and + // coarsening after calling this function + // are exactly the ones that will + // actually be refined or + // coarsened. Usually, you don't have to + // do this by hand + // (Triangulation::execute_coarsening_and_refinement + // does this for you). However, we need + // to initialize the SolutionTransfer + // class and it needs to know the final + // set of cells that will be coarsened or + // refined in order to store the data + // from the old mesh and transfer to the + // new one. Thus, we call the function by + // hand: triangulation.prepare_coarsening_and_refinement (); - // With this out of the way, we - // initialize a SolutionTransfer object - // with the present DoFHandler and attach - // the solution vector to it, followed by - // doing the actual refinement and - // distribution of degrees of freedom on - // the new mesh + // With this out of the way, we + // initialize a SolutionTransfer object + // with the present DoFHandler and attach + // the solution vector to it, followed by + // doing the actual refinement and + // distribution of degrees of freedom on + // the new mesh SolutionTransfer solution_transfer(dof_handler); solution_transfer.prepare_for_coarsening_and_refinement(present_solution); @@ -499,120 +499,120 @@ namespace Step15 dof_handler.distribute_dofs(fe); - // Finally, we retrieve the old solution - // interpolated to the new mesh. Since - // the SolutionTransfer function does not - // actually store the values of the old - // solution, but rather indices, we need - // to preserve the old solution vector - // until we have gotten the new - // interpolated values. Thus, we have the - // new values written into a temporary - // vector, and only afterwards write them - // into the solution vector object. Once - // we have this solution we have to make - // sure that the $u^n$ we now have - // actually has the correct boundary - // values. As explained at the end of the - // introduction, this is not - // automatically the case even if the - // solution before refinement had the - // correct boundary values, and so we - // have to explicitly make sure that it - // now has: + // Finally, we retrieve the old solution + // interpolated to the new mesh. Since + // the SolutionTransfer function does not + // actually store the values of the old + // solution, but rather indices, we need + // to preserve the old solution vector + // until we have gotten the new + // interpolated values. Thus, we have the + // new values written into a temporary + // vector, and only afterwards write them + // into the solution vector object. Once + // we have this solution we have to make + // sure that the $u^n$ we now have + // actually has the correct boundary + // values. As explained at the end of the + // introduction, this is not + // automatically the case even if the + // solution before refinement had the + // correct boundary values, and so we + // have to explicitly make sure that it + // now has: Vector tmp(dof_handler.n_dofs()); solution_transfer.interpolate(present_solution, tmp); present_solution = tmp; set_boundary_values (); - // On the new mesh, there are different - // hanging nodes, which we have to - // compute again. To ensure there are no - // hanging nodes of the old mesh in the - // object, it's first cleared. To be on - // the safe side, we then also make sure - // that the current solution's vector - // entries satisfy the hanging node - // constraints: + // On the new mesh, there are different + // hanging nodes, which we have to + // compute again. To ensure there are no + // hanging nodes of the old mesh in the + // object, it's first cleared. To be on + // the safe side, we then also make sure + // that the current solution's vector + // entries satisfy the hanging node + // constraints: hanging_node_constraints.clear(); DoFTools::make_hanging_node_constraints(dof_handler, - hanging_node_constraints); + hanging_node_constraints); hanging_node_constraints.close(); hanging_node_constraints.distribute (present_solution); - // We end the function by updating all - // the remaining data structures, - // indicating to - // setup_dofs() that this is - // not the first go-around and that it - // needs to preserve the content of the - // solution vector: + // We end the function by updating all + // the remaining data structures, + // indicating to + // setup_dofs() that this is + // not the first go-around and that it + // needs to preserve the content of the + // solution vector: setup_system (false); } - // @sect4{MinimalSurfaceProblem::set_boundary_values} + // @sect4{MinimalSurfaceProblem::set_boundary_values} - // The next function ensures that the - // solution vector's entries respect the - // boundary values for our problem. Having - // refined the mesh (or just started - // computations), there might be new nodal - // points on the boundary. These have - // values that are simply interpolated from - // the previous mesh (or are just zero), - // instead of the correct boundary - // values. This is fixed up by setting all - // boundary nodes explicit to the right - // value: + // The next function ensures that the + // solution vector's entries respect the + // boundary values for our problem. Having + // refined the mesh (or just started + // computations), there might be new nodal + // points on the boundary. These have + // values that are simply interpolated from + // the previous mesh (or are just zero), + // instead of the correct boundary + // values. This is fixed up by setting all + // boundary nodes explicit to the right + // value: template void MinimalSurfaceProblem::set_boundary_values () { std::map boundary_values; VectorTools::interpolate_boundary_values (dof_handler, - 0, - BoundaryValues(), - boundary_values); + 0, + BoundaryValues(), + boundary_values); for (std::map::const_iterator - p = boundary_values.begin(); - p != boundary_values.end(); ++p) + p = boundary_values.begin(); + p != boundary_values.end(); ++p) present_solution(p->first) = p->second; } - // @sect4{MinimalSurfaceProblem::compute_residual} - - // In order to monitor convergence, we need - // a way to compute the norm of the - // (discrete) residual, i.e., the norm of - // the vector - // $\left$ with - // $F(u)=-\nabla \cdot \left( - // \frac{1}{\sqrt{1+|\nabla u|^{2}}}\nabla - // u \right)$ as discussed in the - // introduction. It turns out that - // (although we don't use this feature in - // the current version of the program) one - // needs to compute the residual - // $\left$ - // when determining optimal step lengths, - // and so this is what we implement here: - // the function takes the step length - // $\alpha^n$ as an argument. The original - // functionality is of course obtained by - // passing a zero as argument. - // - // In the function below, we first set up a - // vector for the residual, and then a - // vector for the evaluation point - // $u^n+\alpha^n\;\delta u^n$. This is - // followed by the same boilerplate code we - // use for all integration operations: + // @sect4{MinimalSurfaceProblem::compute_residual} + + // In order to monitor convergence, we need + // a way to compute the norm of the + // (discrete) residual, i.e., the norm of + // the vector + // $\left$ with + // $F(u)=-\nabla \cdot \left( + // \frac{1}{\sqrt{1+|\nabla u|^{2}}}\nabla + // u \right)$ as discussed in the + // introduction. It turns out that + // (although we don't use this feature in + // the current version of the program) one + // needs to compute the residual + // $\left$ + // when determining optimal step lengths, + // and so this is what we implement here: + // the function takes the step length + // $\alpha^n$ as an argument. The original + // functionality is of course obtained by + // passing a zero as argument. + // + // In the function below, we first set up a + // vector for the residual, and then a + // vector for the evaluation point + // $u^n+\alpha^n\;\delta u^n$. This is + // followed by the same boilerplate code we + // use for all integration operations: template double MinimalSurfaceProblem::compute_residual (const double alpha) const { @@ -624,9 +624,9 @@ namespace Step15 const QGauss quadrature_formula(3); FEValues fe_values (fe, quadrature_formula, - update_gradients | - update_quadrature_points | - update_JxW_values); + update_gradients | + update_quadrature_points | + update_JxW_values); const unsigned int dofs_per_cell = fe.dofs_per_cell; const unsigned int n_q_points = quadrature_formula.size(); @@ -637,110 +637,110 @@ namespace Step15 std::vector local_dof_indices (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { - cell_rhs = 0; - fe_values.reinit (cell); - - // The actual computation is much as - // in - // assemble_system(). We - // first evaluate the gradients of - // $u^n+\alpha^n\,\delta u^n$ at the - // quadrature points, then compute - // the coefficient $a_n$, and then - // plug it all into the formula for - // the residual: - fe_values.get_function_gradients (evaluation_point, - gradients); - - - for (unsigned int q_point=0; q_pointget_dof_indices (local_dof_indices); - for (unsigned int i=0; iassemble_system(). We + // first evaluate the gradients of + // $u^n+\alpha^n\,\delta u^n$ at the + // quadrature points, then compute + // the coefficient $a_n$, and then + // plug it all into the formula for + // the residual: + fe_values.get_function_gradients (evaluation_point, + gradients); + + + for (unsigned int q_point=0; q_pointget_dof_indices (local_dof_indices); + for (unsigned int i=0; i boundary_dofs (dof_handler.n_dofs()); DoFTools::extract_boundary_dofs (dof_handler, - ComponentMask(), - boundary_dofs); + ComponentMask(), + boundary_dofs); for (unsigned int i=0; i double MinimalSurfaceProblem::determine_step_length() const { @@ -749,131 +749,131 @@ namespace Step15 - // @sect4{MinimalSurfaceProblem::run} + // @sect4{MinimalSurfaceProblem::run} - // In the run function, we build the first - // grid and then have the top-level logic - // for the Newton iteration. The function - // has two variables, one that indicates - // whether this is the first time we solve - // for a Newton update and one that - // indicates the refinement level of the - // mesh: + // In the run function, we build the first + // grid and then have the top-level logic + // for the Newton iteration. The function + // has two variables, one that indicates + // whether this is the first time we solve + // for a Newton update and one that + // indicates the refinement level of the + // mesh: template void MinimalSurfaceProblem::run () { unsigned int refinement = 0; bool first_step = true; - // As described in the introduction, the - // domain is the unit disk around the - // origin, created in the same way as - // shown in step-6. The mesh is globally - // refined twice followed later on by - // several adaptive cycles: + // As described in the introduction, the + // domain is the unit disk around the + // origin, created in the same way as + // shown in step-6. The mesh is globally + // refined twice followed later on by + // several adaptive cycles: GridGenerator::hyper_ball (triangulation); static const HyperBallBoundary boundary; triangulation.set_boundary (0, boundary); triangulation.refine_global(2); - // The Newton iteration starts - // next. During the first step we do not - // have information about the residual - // prior to this step and so we continue - // the Newton iteration until we have - // reached at least one iteration and - // until residual is less than $10^{-3}$. - // - // At the beginning of the loop, we do a - // bit of setup work. In the first go - // around, we compute the solution on the - // twice globally refined mesh after - // setting up the basic data - // structures. In all following mesh - // refinement loops, the mesh will be - // refined adaptively. + // The Newton iteration starts + // next. During the first step we do not + // have information about the residual + // prior to this step and so we continue + // the Newton iteration until we have + // reached at least one iteration and + // until residual is less than $10^{-3}$. + // + // At the beginning of the loop, we do a + // bit of setup work. In the first go + // around, we compute the solution on the + // twice globally refined mesh after + // setting up the basic data + // structures. In all following mesh + // refinement loops, the mesh will be + // refined adaptively. double previous_res = 0; while (first_step || (previous_res>1e-3)) { - if (first_step == true) - { - std::cout << "******** Initial mesh " - << " ********" - << std::endl; - - setup_system (true); - set_boundary_values (); - } - else - { - ++refinement; - std::cout << "******** Refined mesh " << refinement - << " ********" - << std::endl; - - refine_mesh(); - } - - // On every mesh we do exactly five - // Newton steps. We print the initial - // residual here and then start the - // iterations on this mesh. - // - // In every Newton step the system - // matrix and the right hand side - // have to be computed first, after - // which we store the norm of the - // right hand side as the residual to - // check against when deciding - // whether to stop the iterations. We - // then solve the linear system (the - // function also updates - // $u^{n+1}=u^n+\alpha^n\;\delta - // u^n$) and output the residual at - // the end of this Newton step: - std::cout << " Initial residual: " - << compute_residual(0) - << std::endl; - - for (unsigned int inner_iteration=0; inner_iteration<5; ++inner_iteration) - { - assemble_system (); - previous_res = system_rhs.l2_norm(); - - solve (); - - first_step = false; - std::cout << " Residual: " - << compute_residual(0) - << std::endl; - } - - // Every fifth iteration, i.e., just - // before we refine the mesh again, - // we output the solution as well as - // the Newton update. This happens as - // in all programs before: - DataOut data_out; - - data_out.attach_dof_handler (dof_handler); - data_out.add_data_vector (present_solution, "solution"); - data_out.add_data_vector (newton_update, "update"); - data_out.build_patches (); - const std::string filename = "solution-" + - Utilities::int_to_string (refinement, 2) + - ".vtk"; - std::ofstream output (filename.c_str()); - data_out.write_vtk (output); + if (first_step == true) + { + std::cout << "******** Initial mesh " + << " ********" + << std::endl; + + setup_system (true); + set_boundary_values (); + } + else + { + ++refinement; + std::cout << "******** Refined mesh " << refinement + << " ********" + << std::endl; + + refine_mesh(); + } + + // On every mesh we do exactly five + // Newton steps. We print the initial + // residual here and then start the + // iterations on this mesh. + // + // In every Newton step the system + // matrix and the right hand side + // have to be computed first, after + // which we store the norm of the + // right hand side as the residual to + // check against when deciding + // whether to stop the iterations. We + // then solve the linear system (the + // function also updates + // $u^{n+1}=u^n+\alpha^n\;\delta + // u^n$) and output the residual at + // the end of this Newton step: + std::cout << " Initial residual: " + << compute_residual(0) + << std::endl; + + for (unsigned int inner_iteration=0; inner_iteration<5; ++inner_iteration) + { + assemble_system (); + previous_res = system_rhs.l2_norm(); + + solve (); + + first_step = false; + std::cout << " Residual: " + << compute_residual(0) + << std::endl; + } + + // Every fifth iteration, i.e., just + // before we refine the mesh again, + // we output the solution as well as + // the Newton update. This happens as + // in all programs before: + DataOut data_out; + + data_out.attach_dof_handler (dof_handler); + data_out.add_data_vector (present_solution, "solution"); + data_out.add_data_vector (newton_update, "update"); + data_out.build_patches (); + const std::string filename = "solution-" + + Utilities::int_to_string (refinement, 2) + + ".vtk"; + std::ofstream output (filename.c_str()); + data_out.write_vtk (output); } } } - // @sect4{The main function} +// @sect4{The main function} - // Finally the main function. This - // follows the scheme of all other - // main functions: +// Finally the main function. This +// follows the scheme of all other +// main functions: int main () { try @@ -889,25 +889,25 @@ int main () catch (std::exception &exc) { std::cerr << std::endl << std::endl - << "----------------------------------------------------" - << std::endl; + << "----------------------------------------------------" + << std::endl; std::cerr << "Exception on processing: " << std::endl - << exc.what() << std::endl - << "Aborting!" << std::endl - << "----------------------------------------------------" - << std::endl; + << exc.what() << std::endl + << "Aborting!" << std::endl + << "----------------------------------------------------" + << std::endl; return 1; } catch (...) { std::cerr << std::endl << std::endl - << "----------------------------------------------------" - << std::endl; + << "----------------------------------------------------" + << std::endl; std::cerr << "Unknown exception!" << std::endl - << "Aborting!" << std::endl - << "----------------------------------------------------" - << std::endl; + << "Aborting!" << std::endl + << "----------------------------------------------------" + << std::endl; return 1; } return 0; diff --git a/deal.II/examples/step-16/step-16.cc b/deal.II/examples/step-16/step-16.cc index ce5053886f..3a45b37e73 100644 --- a/deal.II/examples/step-16/step-16.cc +++ b/deal.II/examples/step-16/step-16.cc @@ -11,25 +11,25 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // As discussed in the introduction, most of - // this program is copied almost verbatim - // from step-6, which itself is only a slight - // modification of step-5. Consequently, a - // significant part of this program is not - // new if you've read all the material up to - // step-6, and we won't comment on that part - // of the functionality that is - // unchanged. Rather, we will focus on those - // aspects of the program that have to do - // with the multigrid functionality which - // forms the new aspect of this tutorial - // program. - - // @sect3{Include files} - - // Again, the first few include files - // are already known, so we won't - // comment on them: +// As discussed in the introduction, most of +// this program is copied almost verbatim +// from step-6, which itself is only a slight +// modification of step-5. Consequently, a +// significant part of this program is not +// new if you've read all the material up to +// step-6, and we won't comment on that part +// of the functionality that is +// unchanged. Rather, we will focus on those +// aspects of the program that have to do +// with the multigrid functionality which +// forms the new aspect of this tutorial +// program. + +// @sect3{Include files} + +// Again, the first few include files +// are already known, so we won't +// comment on them: #include #include #include @@ -59,19 +59,19 @@ #include #include - // These, now, are the include necessary for - // the multi-level methods. The first two - // declare classes that allow us to enumerate - // degrees of freedom not only on the finest - // mesh level, but also on intermediate - // levels (that's what the MGDoFHandler class - // does) as well as allow to access this - // information (iterators and accessors over - // these cells). - // - // The rest of the include files deals with - // the mechanics of multigrid as a linear - // operator (solver or preconditioner). +// These, now, are the include necessary for +// the multi-level methods. The first two +// declare classes that allow us to enumerate +// degrees of freedom not only on the finest +// mesh level, but also on intermediate +// levels (that's what the MGDoFHandler class +// does) as well as allow to access this +// information (iterators and accessors over +// these cells). +// +// The rest of the include files deals with +// the mechanics of multigrid as a linear +// operator (solver or preconditioner). #include #include #include @@ -82,127 +82,127 @@ #include #include - // This is C++: +// This is C++: #include #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step16 { using namespace dealii; - // @sect3{The LaplaceProblem class template} + // @sect3{The LaplaceProblem class template} - // This main class is basically the same - // class as in step-6. As far as member - // functions is concerned, the only addition - // is the assemble_multigrid - // function that assembles the matrices that - // correspond to the discrete operators on - // intermediate levels: + // This main class is basically the same + // class as in step-6. As far as member + // functions is concerned, the only addition + // is the assemble_multigrid + // function that assembles the matrices that + // correspond to the discrete operators on + // intermediate levels: template class LaplaceProblem { - public: - LaplaceProblem (const unsigned int deg); - void run (); - - private: - void setup_system (); - void assemble_system (); - void assemble_multigrid (); - void solve (); - void refine_grid (); - void output_results (const unsigned int cycle) const; - - Triangulation triangulation; - FE_Q fe; - MGDoFHandler mg_dof_handler; - - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - - // We need an additional object for the - // hanging nodes constraints. They are - // handed to the transfer object in the - // multigrid. Since we call a compress - // inside the multigrid these constraints - // are not allowed to be inhomogeneous so - // we store them in different ConstraintMatrix - // objects. - ConstraintMatrix hanging_node_constraints; - ConstraintMatrix constraints; - - Vector solution; - Vector system_rhs; - - const unsigned int degree; - - // The following four objects are the - // only additional member variables, - // compared to step-6. They first three - // represent the - // operators that act on individual - // levels of the multilevel hierarchy, - // rather than on the finest mesh as do - // the objects above while the last object - // stores information about the boundary - // indices on each level and information - // about indices lying on a refinement - // edge between two different refinement - // levels. - // - // To facilitate having objects on each - // level of a multilevel hierarchy, - // deal.II has the MGLevelObject class - // template that provides storage for - // objects on each level. What we need - // here are matrices on each level, which - // implies that we also need sparsity - // patterns on each level. As outlined in - // the @ref mg_paper, the operators - // (matrices) that we need are actually - // twofold: one on the interior of each - // level, and one at the interface - // between each level and that part of - // the domain where the mesh is - // coarser. In fact, we will need the - // latter in two versions: for the - // direction from coarse to fine mesh and - // from fine to coarse. Fortunately, - // however, we here have a self-adjoint - // problem for which one of these is the - // transpose of the other, and so we only - // have to build one; we choose the one - // from coarse to fine. - MGLevelObject mg_sparsity_patterns; - MGLevelObject > mg_matrices; - MGLevelObject > mg_interface_matrices; - MGConstrainedDoFs mg_constrained_dofs; + public: + LaplaceProblem (const unsigned int deg); + void run (); + + private: + void setup_system (); + void assemble_system (); + void assemble_multigrid (); + void solve (); + void refine_grid (); + void output_results (const unsigned int cycle) const; + + Triangulation triangulation; + FE_Q fe; + MGDoFHandler mg_dof_handler; + + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + + // We need an additional object for the + // hanging nodes constraints. They are + // handed to the transfer object in the + // multigrid. Since we call a compress + // inside the multigrid these constraints + // are not allowed to be inhomogeneous so + // we store them in different ConstraintMatrix + // objects. + ConstraintMatrix hanging_node_constraints; + ConstraintMatrix constraints; + + Vector solution; + Vector system_rhs; + + const unsigned int degree; + + // The following four objects are the + // only additional member variables, + // compared to step-6. They first three + // represent the + // operators that act on individual + // levels of the multilevel hierarchy, + // rather than on the finest mesh as do + // the objects above while the last object + // stores information about the boundary + // indices on each level and information + // about indices lying on a refinement + // edge between two different refinement + // levels. + // + // To facilitate having objects on each + // level of a multilevel hierarchy, + // deal.II has the MGLevelObject class + // template that provides storage for + // objects on each level. What we need + // here are matrices on each level, which + // implies that we also need sparsity + // patterns on each level. As outlined in + // the @ref mg_paper, the operators + // (matrices) that we need are actually + // twofold: one on the interior of each + // level, and one at the interface + // between each level and that part of + // the domain where the mesh is + // coarser. In fact, we will need the + // latter in two versions: for the + // direction from coarse to fine mesh and + // from fine to coarse. Fortunately, + // however, we here have a self-adjoint + // problem for which one of these is the + // transpose of the other, and so we only + // have to build one; we choose the one + // from coarse to fine. + MGLevelObject mg_sparsity_patterns; + MGLevelObject > mg_matrices; + MGLevelObject > mg_interface_matrices; + MGConstrainedDoFs mg_constrained_dofs; }; - // @sect3{Nonconstant coefficients} + // @sect3{Nonconstant coefficients} - // The implementation of nonconstant - // coefficients is copied verbatim - // from step-5 and step-6: + // The implementation of nonconstant + // coefficients is copied verbatim + // from step-5 and step-6: template class Coefficient : public Function { - public: - Coefficient () : Function() {} + public: + Coefficient () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; @@ -237,64 +237,64 @@ namespace Step16 } - // @sect3{The LaplaceProblem class implementation} - - // @sect4{LaplaceProblem::LaplaceProblem} - - // The constructor is left mostly - // unchanged. We take the polynomial degree - // of the finite elements to be used as a - // constructor argument and store it in a - // member variable. - // - // By convention, all adaptively refined - // triangulations in deal.II never change by - // more than one level across a face between - // cells. For our multigrid algorithms, - // however, we need a slightly stricter - // guarantee, namely that the mesh also does - // not change by more than refinement level - // across vertices that might connect two - // cells. In other words, we must prevent the - // following situation: - // - // @image html limit_level_difference_at_vertices.png "" - // - // This is achieved by passing the - // Triangulation::limit_level_difference_at_vertices - // flag to the constructor of the - // triangulation class. + // @sect3{The LaplaceProblem class implementation} + + // @sect4{LaplaceProblem::LaplaceProblem} + + // The constructor is left mostly + // unchanged. We take the polynomial degree + // of the finite elements to be used as a + // constructor argument and store it in a + // member variable. + // + // By convention, all adaptively refined + // triangulations in deal.II never change by + // more than one level across a face between + // cells. For our multigrid algorithms, + // however, we need a slightly stricter + // guarantee, namely that the mesh also does + // not change by more than refinement level + // across vertices that might connect two + // cells. In other words, we must prevent the + // following situation: + // + // @image html limit_level_difference_at_vertices.png "" + // + // This is achieved by passing the + // Triangulation::limit_level_difference_at_vertices + // flag to the constructor of the + // triangulation class. template LaplaceProblem::LaplaceProblem (const unsigned int degree) - : - triangulation (Triangulation:: - limit_level_difference_at_vertices), - fe (degree), - mg_dof_handler (triangulation), - degree(degree) + : + triangulation (Triangulation:: + limit_level_difference_at_vertices), + fe (degree), + mg_dof_handler (triangulation), + degree(degree) {} - // @sect4{LaplaceProblem::setup_system} + // @sect4{LaplaceProblem::setup_system} - // The following function extends what the - // corresponding one in step-6 did. The top - // part, apart from the additional output, - // does the same: + // The following function extends what the + // corresponding one in step-6 did. The top + // part, apart from the additional output, + // does the same: template void LaplaceProblem::setup_system () { mg_dof_handler.distribute_dofs (fe); - // Here we output not only the - // degrees of freedom on the finest - // level, but also in the - // multilevel structure + // Here we output not only the + // degrees of freedom on the finest + // level, but also in the + // multilevel structure deallog << "Number of degrees of freedom: " << mg_dof_handler.n_dofs(); - for (unsigned int l=0;ldirichlet_boundary - // here as well. + // The multigrid constraints have to be + // initialized. They need to know about + // the boundary values as well, so we + // pass the dirichlet_boundary + // here as well. mg_constrained_dofs.clear(); mg_constrained_dofs.initialize(mg_dof_handler, dirichlet_boundary); - // Now for the things that concern the - // multigrid data structures. First, we - // resize the multi-level objects to hold - // matrices and sparsity patterns for every - // level. The coarse level is zero (this is - // mandatory right now but may change in a - // future revision). Note that these - // functions take a complete, inclusive - // range here (not a starting index and - // size), so the finest level is - // n_levels-1. We first have - // to resize the container holding the - // SparseMatrix classes, since they have to - // release their SparsityPattern before the - // can be destroyed upon resizing. + // Now for the things that concern the + // multigrid data structures. First, we + // resize the multi-level objects to hold + // matrices and sparsity patterns for every + // level. The coarse level is zero (this is + // mandatory right now but may change in a + // future revision). Note that these + // functions take a complete, inclusive + // range here (not a starting index and + // size), so the finest level is + // n_levels-1. We first have + // to resize the container holding the + // SparseMatrix classes, since they have to + // release their SparsityPattern before the + // can be destroyed upon resizing. const unsigned int n_levels = triangulation.n_levels(); mg_interface_matrices.resize(0, n_levels-1); @@ -376,32 +376,32 @@ namespace Step16 mg_matrices.clear (); mg_sparsity_patterns.resize(0, n_levels-1); - // Now, we have to provide a matrix on each - // level. To this end, we first use the - // MGTools::make_sparsity_pattern function - // to first generate a preliminary - // compressed sparsity pattern on each - // level (see the @ref Sparsity module for - // more information on this topic) and then - // copy it over to the one we really - // want. The next step is to initialize - // both kinds of level matrices with these - // sparsity patterns. - // - // It may be worth pointing out that the - // interface matrices only have entries for - // degrees of freedom that sit at or next - // to the interface between coarser and - // finer levels of the mesh. They are - // therefore even sparser than the matrices - // on the individual levels of our - // multigrid hierarchy. If we were more - // concerned about memory usage (and - // possibly the speed with which we can - // multiply with these matrices), we should - // use separate and different sparsity - // patterns for these two kinds of - // matrices. + // Now, we have to provide a matrix on each + // level. To this end, we first use the + // MGTools::make_sparsity_pattern function + // to first generate a preliminary + // compressed sparsity pattern on each + // level (see the @ref Sparsity module for + // more information on this topic) and then + // copy it over to the one we really + // want. The next step is to initialize + // both kinds of level matrices with these + // sparsity patterns. + // + // It may be worth pointing out that the + // interface matrices only have entries for + // degrees of freedom that sit at or next + // to the interface between coarser and + // finer levels of the mesh. They are + // therefore even sparser than the matrices + // on the individual levels of our + // multigrid hierarchy. If we were more + // concerned about memory usage (and + // possibly the speed with which we can + // multiply with these matrices), we should + // use separate and different sparsity + // patterns for these two kinds of + // matrices. for (unsigned int level=0; level void LaplaceProblem::assemble_system () { @@ -456,8 +456,8 @@ namespace Step16 std::vector coefficient_values (n_q_points); typename MGDoFHandler::active_cell_iterator - cell = mg_dof_handler.begin_active(), - endc = mg_dof_handler.end(); + cell = mg_dof_handler.begin_active(), + endc = mg_dof_handler.end(); for (; cell!=endc; ++cell) { cell_matrix = 0; @@ -490,23 +490,23 @@ namespace Step16 } - // @sect4{LaplaceProblem::assemble_multigrid} - - // The next function is the one that builds - // the linear operators (matrices) that - // define the multigrid method on each level - // of the mesh. The integration core is the - // same as above, but the loop below will go - // over all existing cells instead of just - // the active ones, and the results must be - // entered into the correct matrix. Note also - // that since we only do multi-level - // preconditioning, no right-hand side needs - // to be assembled here. - // - // Before we go there, however, we have to - // take care of a significant amount of book - // keeping: + // @sect4{LaplaceProblem::assemble_multigrid} + + // The next function is the one that builds + // the linear operators (matrices) that + // define the multigrid method on each level + // of the mesh. The integration core is the + // same as above, but the loop below will go + // over all existing cells instead of just + // the active ones, and the results must be + // entered into the correct matrix. Note also + // that since we only do multi-level + // preconditioning, no right-hand side needs + // to be assembled here. + // + // Before we go there, however, we have to + // take care of a significant amount of book + // keeping: template void LaplaceProblem::assemble_multigrid () { @@ -526,64 +526,64 @@ namespace Step16 const Coefficient coefficient; std::vector coefficient_values (n_q_points); - // Next a few things that are specific to - // building the multigrid data structures - // (since we only need them in the current - // function, rather than also elsewhere, we - // build them here instead of the - // setup_system - // function). Some of the following may be - // a bit obscure if you're not familiar - // with the algorithm actually implemented - // in deal.II to support multilevel - // algorithms on adaptive meshes; if some - // of the things below seem strange, take a - // look at the @ref mg_paper. - // - // Our first job is to identify those - // degrees of freedom on each level that - // are located on interfaces between - // adaptively refined levels, and those - // that lie on the interface but also on - // the exterior boundary of the domain. As - // in many other parts of the library, we - // do this by using boolean masks, - // i.e. vectors of booleans each element of - // which indicates whether the - // corresponding degree of freedom index is - // an interface DoF or not. The MGConstraints - // already computed the information for us - // when we called initialize in setup_system(). + // Next a few things that are specific to + // building the multigrid data structures + // (since we only need them in the current + // function, rather than also elsewhere, we + // build them here instead of the + // setup_system + // function). Some of the following may be + // a bit obscure if you're not familiar + // with the algorithm actually implemented + // in deal.II to support multilevel + // algorithms on adaptive meshes; if some + // of the things below seem strange, take a + // look at the @ref mg_paper. + // + // Our first job is to identify those + // degrees of freedom on each level that + // are located on interfaces between + // adaptively refined levels, and those + // that lie on the interface but also on + // the exterior boundary of the domain. As + // in many other parts of the library, we + // do this by using boolean masks, + // i.e. vectors of booleans each element of + // which indicates whether the + // corresponding degree of freedom index is + // an interface DoF or not. The MGConstraints + // already computed the information for us + // when we called initialize in setup_system(). std::vector > interface_dofs = mg_constrained_dofs.get_refinement_edge_indices (); std::vector > boundary_interface_dofs = mg_constrained_dofs.get_refinement_edge_boundary_indices (); - // The indices just identified will later - // be used to decide where the assembled value - // has to be added into on each level. - // On the other hand, - // we also have to impose zero boundary - // conditions on the external boundary of - // each level. But this the MGConstraints - // knows it. So we simply ask for them by calling - // get_boundary_indices (). - // The third step is to construct - // constraints on all those degrees of - // freedom: their value should be zero - // after each application of the level - // operators. To this end, we construct - // ConstraintMatrix objects for each level, - // and add to each of these constraints for - // each degree of freedom. Due to the way - // the ConstraintMatrix stores its data, - // the function to add a constraint on a - // single degree of freedom and force it to - // be zero is called - // Constraintmatrix::add_line(); doing so - // for several degrees of freedom at once - // can be done using - // Constraintmatrix::add_lines(): + // The indices just identified will later + // be used to decide where the assembled value + // has to be added into on each level. + // On the other hand, + // we also have to impose zero boundary + // conditions on the external boundary of + // each level. But this the MGConstraints + // knows it. So we simply ask for them by calling + // get_boundary_indices (). + // The third step is to construct + // constraints on all those degrees of + // freedom: their value should be zero + // after each application of the level + // operators. To this end, we construct + // ConstraintMatrix objects for each level, + // and add to each of these constraints for + // each degree of freedom. Due to the way + // the ConstraintMatrix stores its data, + // the function to add a constraint on a + // single degree of freedom and force it to + // be zero is called + // Constraintmatrix::add_line(); doing so + // for several degrees of freedom at once + // can be done using + // Constraintmatrix::add_lines(): std::vector boundary_constraints (triangulation.n_levels()); std::vector boundary_interface_constraints (triangulation.n_levels()); for (unsigned int level=0; levelassemble_system, with two - // exceptions: (i) we don't need a right - // hand side, and more significantly (ii) we - // don't just loop over all active cells, - // but in fact all cells, active or - // not. Consequently, the correct iterator - // to use is MGDoFHandler::cell_iterator - // rather than - // MGDoFHandler::active_cell_iterator. Let's - // go about it: + // Now that we're done with most of our + // preliminaries, let's start the + // integration loop. It looks mostly like + // the loop in + // assemble_system, with two + // exceptions: (i) we don't need a right + // hand side, and more significantly (ii) we + // don't just loop over all active cells, + // but in fact all cells, active or + // not. Consequently, the correct iterator + // to use is MGDoFHandler::cell_iterator + // rather than + // MGDoFHandler::active_cell_iterator. Let's + // go about it: typename MGDoFHandler::cell_iterator cell = mg_dof_handler.begin(), endc = mg_dof_handler.end(); @@ -630,147 +630,147 @@ namespace Step16 fe_values.shape_grad(j,q_point) * fe_values.JxW(q_point)); - // The rest of the assembly is again - // slightly different. This starts with - // a gotcha that is easily forgotten: - // The indices of global degrees of - // freedom we want here are the ones - // for current level, not for the - // global matrix. We therefore need the - // function - // MGDoFAccessorLLget_mg_dof_indices, - // not MGDoFAccessor::get_dof_indices - // as used in the assembly of the - // global system: + // The rest of the assembly is again + // slightly different. This starts with + // a gotcha that is easily forgotten: + // The indices of global degrees of + // freedom we want here are the ones + // for current level, not for the + // global matrix. We therefore need the + // function + // MGDoFAccessorLLget_mg_dof_indices, + // not MGDoFAccessor::get_dof_indices + // as used in the assembly of the + // global system: cell->get_mg_dof_indices (local_dof_indices); - // Next, we need to copy local - // contributions into the level - // objects. We can do this in the same - // way as in the global assembly, using - // a constraint object that takes care - // of constrained degrees (which here - // are only boundary nodes, as the - // individual levels have no hanging - // node constraints). Note that the - // boundary_constraints - // object makes sure that the level - // matrices contains no contributions - // from degrees of freedom at the - // interface between cells of different - // refinement level. + // Next, we need to copy local + // contributions into the level + // objects. We can do this in the same + // way as in the global assembly, using + // a constraint object that takes care + // of constrained degrees (which here + // are only boundary nodes, as the + // individual levels have no hanging + // node constraints). Note that the + // boundary_constraints + // object makes sure that the level + // matrices contains no contributions + // from degrees of freedom at the + // interface between cells of different + // refinement level. boundary_constraints[cell->level()] - .distribute_local_to_global (cell_matrix, - local_dof_indices, - mg_matrices[cell->level()]); - - // The next step is again slightly more - // obscure (but explained in the @ref - // mg_paper): We need the remainder of - // the operator that we just copied - // into the mg_matrices - // object, namely the part on the - // interface between cells at the - // current level and cells one level - // coarser. This matrix exists in two - // directions: for interior DoFs (index - // $i$) of the current level to those - // sitting on the interface (index - // $j$), and the other way around. Of - // course, since we have a symmetric - // operator, one of these matrices is - // the transpose of the other. - // - // The way we assemble these matrices - // is as follows: since the are formed - // from parts of the local - // contributions, we first delete all - // those parts of the local - // contributions that we are not - // interested in, namely all those - // elements of the local matrix for - // which not $i$ is an interface DoF - // and $j$ is not. The result is one of - // the two matrices that we are - // interested in, and we then copy it - // into the - // mg_interface_matrices - // object. The - // boundary_interface_constraints - // object at the same time makes sure - // that we delete contributions from - // all degrees of freedom that are not - // only on the interface but also on - // the external boundary of the domain. - // - // The last part to remember is how to - // get the other matrix. Since it is - // only the transpose, we will later - // (in the solve() - // function) be able to just pass the - // transpose matrix where necessary. + .distribute_local_to_global (cell_matrix, + local_dof_indices, + mg_matrices[cell->level()]); + + // The next step is again slightly more + // obscure (but explained in the @ref + // mg_paper): We need the remainder of + // the operator that we just copied + // into the mg_matrices + // object, namely the part on the + // interface between cells at the + // current level and cells one level + // coarser. This matrix exists in two + // directions: for interior DoFs (index + // $i$) of the current level to those + // sitting on the interface (index + // $j$), and the other way around. Of + // course, since we have a symmetric + // operator, one of these matrices is + // the transpose of the other. + // + // The way we assemble these matrices + // is as follows: since the are formed + // from parts of the local + // contributions, we first delete all + // those parts of the local + // contributions that we are not + // interested in, namely all those + // elements of the local matrix for + // which not $i$ is an interface DoF + // and $j$ is not. The result is one of + // the two matrices that we are + // interested in, and we then copy it + // into the + // mg_interface_matrices + // object. The + // boundary_interface_constraints + // object at the same time makes sure + // that we delete contributions from + // all degrees of freedom that are not + // only on the interface but also on + // the external boundary of the domain. + // + // The last part to remember is how to + // get the other matrix. Since it is + // only the transpose, we will later + // (in the solve() + // function) be able to just pass the + // transpose matrix where necessary. for (unsigned int i=0; ilevel()][local_dof_indices[i]]==true && - interface_dofs[cell->level()][local_dof_indices[j]]==false)) + if ( !(interface_dofs[cell->level()][local_dof_indices[i]]==true && + interface_dofs[cell->level()][local_dof_indices[j]]==false)) cell_matrix(i,j) = 0; boundary_interface_constraints[cell->level()] - .distribute_local_to_global (cell_matrix, - local_dof_indices, - mg_interface_matrices[cell->level()]); + .distribute_local_to_global (cell_matrix, + local_dof_indices, + mg_interface_matrices[cell->level()]); } } - // @sect4{LaplaceProblem::solve} - - // This is the other function that is - // significantly different in support of the - // multigrid solver (or, in fact, the - // preconditioner for which we use the - // multigrid method). - // - // Let us start out by setting up two of the - // components of multilevel methods: transfer - // operators between levels, and a solver on - // the coarsest level. In finite element - // methods, the transfer operators are - // derived from the finite element function - // spaces involved and can often be computed - // in a generic way independent of the - // problem under consideration. In that case, - // we can use the MGTransferPrebuilt class - // that, given the constraints on the global - // level and an MGDoFHandler object computes - // the matrices corresponding to these - // transfer operators. - // - // The second part of the following lines - // deals with the coarse grid solver. Since - // our coarse grid is very coarse indeed, we - // decide for a direct solver (a Householder - // decomposition of the coarsest level - // matrix), even if its implementation is not - // particularly sophisticated. If our coarse - // mesh had many more cells than the five we - // have here, something better suited would - // obviously be necessary here. + // @sect4{LaplaceProblem::solve} + + // This is the other function that is + // significantly different in support of the + // multigrid solver (or, in fact, the + // preconditioner for which we use the + // multigrid method). + // + // Let us start out by setting up two of the + // components of multilevel methods: transfer + // operators between levels, and a solver on + // the coarsest level. In finite element + // methods, the transfer operators are + // derived from the finite element function + // spaces involved and can often be computed + // in a generic way independent of the + // problem under consideration. In that case, + // we can use the MGTransferPrebuilt class + // that, given the constraints on the global + // level and an MGDoFHandler object computes + // the matrices corresponding to these + // transfer operators. + // + // The second part of the following lines + // deals with the coarse grid solver. Since + // our coarse grid is very coarse indeed, we + // decide for a direct solver (a Householder + // decomposition of the coarsest level + // matrix), even if its implementation is not + // particularly sophisticated. If our coarse + // mesh had many more cells than the five we + // have here, something better suited would + // obviously be necessary here. template void LaplaceProblem::solve () { - // Create the object that deals with the transfer - // between different refinement levels. We need to - // pass it the hanging node constraints. + // Create the object that deals with the transfer + // between different refinement levels. We need to + // pass it the hanging node constraints. MGTransferPrebuilt > mg_transfer(hanging_node_constraints, mg_constrained_dofs); - // Now the prolongation matrix has to be built. - // This matrix needs to take the boundary values on - // each level into account and needs to know about - // the indices at the refinement egdes. The - // MGConstraints knows about that so - // pass it as an argument. + // Now the prolongation matrix has to be built. + // This matrix needs to take the boundary values on + // each level into account and needs to know about + // the indices at the refinement egdes. The + // MGConstraints knows about that so + // pass it as an argument. mg_transfer.build_matrices(mg_dof_handler); FullMatrix coarse_matrix; @@ -778,88 +778,88 @@ namespace Step16 MGCoarseGridHouseholder<> coarse_grid_solver; coarse_grid_solver.initialize (coarse_matrix); - // The next component of a multilevel - // solver or preconditioner is that we need - // a smoother on each level. A common - // choice for this is to use the - // application of a relaxation method (such - // as the SOR, Jacobi or Richardson method) - // or a small number of iterations of a - // solver method (such as CG or GMRES). The - // MGSmootherRelaxation and - // MGSmootherPrecondition classes provide - // support for these two kinds of - // smoothers. Here, we opt for the - // application of a single SOR - // iteration. To this end, we define an - // appropriate typedef and - // then setup a smoother object. - // - // Since this smoother needs temporary - // vectors to store intermediate results, - // we need to provide a VectorMemory - // object. Since these vectors will be - // reused over and over, the - // GrowingVectorMemory is more time - // efficient than the PrimitiveVectorMemory - // class in the current case. - // - // The last step is to initialize the - // smoother object with our level matrices - // and to set some smoothing parameters. - // The initialize() function - // can optionally take additional arguments - // that will be passed to the smoother - // object on each level. In the current - // case for the SOR smoother, this could, - // for example, include a relaxation - // parameter. However, we here leave these - // at their default values. The call to - // set_steps() indicates that - // we will use two pre- and two - // post-smoothing steps on each level; to - // use a variable number of smoother steps - // on different levels, more options can be - // set in the constructor call to the - // mg_smoother object. - // - // The last step results from the fact that - // we use the SOR method as a smoother - - // which is not symmetric - but we use the - // conjugate gradient iteration (which - // requires a symmetric preconditioner) - // below, we need to let the multilevel - // preconditioner make sure that we get a - // symmetric operator even for nonsymmetric - // smoothers: + // The next component of a multilevel + // solver or preconditioner is that we need + // a smoother on each level. A common + // choice for this is to use the + // application of a relaxation method (such + // as the SOR, Jacobi or Richardson method) + // or a small number of iterations of a + // solver method (such as CG or GMRES). The + // MGSmootherRelaxation and + // MGSmootherPrecondition classes provide + // support for these two kinds of + // smoothers. Here, we opt for the + // application of a single SOR + // iteration. To this end, we define an + // appropriate typedef and + // then setup a smoother object. + // + // Since this smoother needs temporary + // vectors to store intermediate results, + // we need to provide a VectorMemory + // object. Since these vectors will be + // reused over and over, the + // GrowingVectorMemory is more time + // efficient than the PrimitiveVectorMemory + // class in the current case. + // + // The last step is to initialize the + // smoother object with our level matrices + // and to set some smoothing parameters. + // The initialize() function + // can optionally take additional arguments + // that will be passed to the smoother + // object on each level. In the current + // case for the SOR smoother, this could, + // for example, include a relaxation + // parameter. However, we here leave these + // at their default values. The call to + // set_steps() indicates that + // we will use two pre- and two + // post-smoothing steps on each level; to + // use a variable number of smoother steps + // on different levels, more options can be + // set in the constructor call to the + // mg_smoother object. + // + // The last step results from the fact that + // we use the SOR method as a smoother - + // which is not symmetric - but we use the + // conjugate gradient iteration (which + // requires a symmetric preconditioner) + // below, we need to let the multilevel + // preconditioner make sure that we get a + // symmetric operator even for nonsymmetric + // smoothers: typedef PreconditionSOR > Smoother; GrowingVectorMemory<> vector_memory; MGSmootherRelaxation, Smoother, Vector > - mg_smoother(vector_memory); + mg_smoother(vector_memory); mg_smoother.initialize(mg_matrices); mg_smoother.set_steps(2); mg_smoother.set_symmetric(true); - // The next preparatory step is that we - // must wrap our level and interface - // matrices in an object having the - // required multiplication functions. We - // will create two objects for the - // interface objects going from coarse to - // fine and the other way around; the - // multigrid algorithm will later use the - // transpose operator for the latter - // operation, allowing us to initialize - // both up and down versions of the - // operator with the matrices we already - // built: + // The next preparatory step is that we + // must wrap our level and interface + // matrices in an object having the + // required multiplication functions. We + // will create two objects for the + // interface objects going from coarse to + // fine and the other way around; the + // multigrid algorithm will later use the + // transpose operator for the latter + // operation, allowing us to initialize + // both up and down versions of the + // operator with the matrices we already + // built: MGMatrix<> mg_matrix(&mg_matrices); MGMatrix<> mg_interface_up(&mg_interface_matrices); MGMatrix<> mg_interface_down(&mg_interface_matrices); - // Now, we are ready to set up the - // V-cycle operator and the - // multilevel preconditioner. + // Now, we are ready to set up the + // V-cycle operator and the + // multilevel preconditioner. Multigrid > mg(mg_dof_handler, mg_matrix, coarse_grid_solver, @@ -869,11 +869,11 @@ namespace Step16 mg.set_edge_matrices(mg_interface_down, mg_interface_up); PreconditionMG, MGTransferPrebuilt > > - preconditioner(mg_dof_handler, mg, mg_transfer); + preconditioner(mg_dof_handler, mg, mg_transfer); - // With all this together, we can finally - // get about solving the linear system in - // the usual way: + // With all this together, we can finally + // get about solving the linear system in + // the usual way: SolverControl solver_control (1000, 1e-12); SolverCG<> cg (solver_control); @@ -890,24 +890,24 @@ namespace Step16 - // @sect4{Postprocessing} - - // The following two functions postprocess a - // solution once it is computed. In - // particular, the first one refines the mesh - // at the beginning of each cycle while the - // second one outputs results at the end of - // each such cycle. The functions are almost - // unchanged from those in step-6, with the - // exception of two minor differences: The - // KellyErrorEstimator::estimate function - // wants an argument of type DoFHandler, not - // MGDoFHandler, and so we have to cast from - // derived to base class; and we generate - // output in VTK format, to use the more - // modern visualization programs available - // today compared to those that were - // available when step-6 was written. + // @sect4{Postprocessing} + + // The following two functions postprocess a + // solution once it is computed. In + // particular, the first one refines the mesh + // at the beginning of each cycle while the + // second one outputs results at the end of + // each such cycle. The functions are almost + // unchanged from those in step-6, with the + // exception of two minor differences: The + // KellyErrorEstimator::estimate function + // wants an argument of type DoFHandler, not + // MGDoFHandler, and so we have to cast from + // derived to base class; and we generate + // output in VTK format, to use the more + // modern visualization programs available + // today compared to those that were + // available when step-6 was written. template void LaplaceProblem::refine_grid () { @@ -945,16 +945,16 @@ namespace Step16 } - // @sect4{LaplaceProblem::run} + // @sect4{LaplaceProblem::run} - // Like several of the functions above, this - // is almost exactly a copy of of the - // corresponding function in step-6. The only - // difference is the call to - // assemble_multigrid that takes - // care of forming the matrices on every - // level that we need in the multigrid - // method. + // Like several of the functions above, this + // is almost exactly a copy of of the + // corresponding function in step-6. The only + // difference is the call to + // assemble_multigrid that takes + // care of forming the matrices on every + // level that we need in the multigrid + // method. template void LaplaceProblem::run () { @@ -1000,10 +1000,10 @@ namespace Step16 } - // @sect3{The main() function} - // - // This is again the same function as - // in step-6: +// @sect3{The main() function} +// +// This is again the same function as +// in step-6: int main () { try diff --git a/deal.II/examples/step-17/step-17.cc b/deal.II/examples/step-17/step-17.cc index bdb6faaed6..46a7ea35eb 100644 --- a/deal.II/examples/step-17/step-17.cc +++ b/deal.II/examples/step-17/step-17.cc @@ -10,9 +10,9 @@ /* further information on this license. */ - // First the usual assortment of header files - // we have already used in previous example - // programs: +// First the usual assortment of header files +// we have already used in previous example +// programs: #include #include #include @@ -36,209 +36,209 @@ #include #include - // And here come the things that we - // need particularly for this example - // program and that weren't in - // step-8. First, we replace the - // standard output std::cout by a - // new stream pcout which is used - // in %parallel computations for - // generating output only on one of - // the MPI processes. +// And here come the things that we +// need particularly for this example +// program and that weren't in +// step-8. First, we replace the +// standard output std::cout by a +// new stream pcout which is used +// in %parallel computations for +// generating output only on one of +// the MPI processes. #include - // We are going to query the number - // of processes and the number of the - // present process by calling the - // respective functions in the - // Utilities::MPI namespace. +// We are going to query the number +// of processes and the number of the +// present process by calling the +// respective functions in the +// Utilities::MPI namespace. #include - // Then, we are - // going to replace all linear algebra - // components that involve the (global) - // linear system by classes that wrap - // interfaces similar to our own linear - // algebra classes around what PETSc offers - // (PETSc is a library written in C, and - // deal.II comes with wrapper classes that - // provide the PETSc functionality with an - // interface that is similar to the interface - // we already had for our own linear algebra - // classes). In particular, we need vectors - // and matrices that are distributed across - // several processes in MPI programs (and - // simply map to sequential, local vectors - // and matrices if there is only a single - // process, i.e. if you are running on only - // one machine, and without MPI support): +// Then, we are +// going to replace all linear algebra +// components that involve the (global) +// linear system by classes that wrap +// interfaces similar to our own linear +// algebra classes around what PETSc offers +// (PETSc is a library written in C, and +// deal.II comes with wrapper classes that +// provide the PETSc functionality with an +// interface that is similar to the interface +// we already had for our own linear algebra +// classes). In particular, we need vectors +// and matrices that are distributed across +// several processes in MPI programs (and +// simply map to sequential, local vectors +// and matrices if there is only a single +// process, i.e. if you are running on only +// one machine, and without MPI support): #include #include #include - // Then we also need interfaces for solvers - // and preconditioners that PETSc provides: +// Then we also need interfaces for solvers +// and preconditioners that PETSc provides: #include #include - // And in addition, we need some algorithms - // for partitioning our meshes so that they - // can be efficiently distributed across an - // MPI network. The partitioning algorithm is - // implemented in the GridTools class, - // and we need an additional include file for - // a function in DoFRenumbering that - // allows to sort the indices associated with - // degrees of freedom so that they are - // numbered according to the subdomain they - // are associated with: +// And in addition, we need some algorithms +// for partitioning our meshes so that they +// can be efficiently distributed across an +// MPI network. The partitioning algorithm is +// implemented in the GridTools class, +// and we need an additional include file for +// a function in DoFRenumbering that +// allows to sort the indices associated with +// degrees of freedom so that they are +// numbered according to the subdomain they +// are associated with: #include #include - // And this is simply C++ again: +// And this is simply C++ again: #include #include #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step17 { using namespace dealii; - // Now, here comes the declaration of the - // main class and of various other things - // below it. As mentioned in the - // introduction, almost all of this has been - // copied verbatim from step-8, so we only - // comment on the few things that are - // different. There is one (cosmetic) change - // in that we let solve return a value, - // namely the number of iterations it took to - // converge, so that we can output this to - // the screen at the appropriate place. In - // addition, we introduce a stream-like - // variable pcout, explained below: + // Now, here comes the declaration of the + // main class and of various other things + // below it. As mentioned in the + // introduction, almost all of this has been + // copied verbatim from step-8, so we only + // comment on the few things that are + // different. There is one (cosmetic) change + // in that we let solve return a value, + // namely the number of iterations it took to + // converge, so that we can output this to + // the screen at the appropriate place. In + // addition, we introduce a stream-like + // variable pcout, explained below: template class ElasticProblem { - public: - ElasticProblem (); - ~ElasticProblem (); - void run (); - - private: - void setup_system (); - void assemble_system (); - unsigned int solve (); - void refine_grid (); - void output_results (const unsigned int cycle) const; - - // The first variable is basically only - // for convenience: in %parallel program, - // if each process outputs status - // information, then there quickly is a - // lot of clutter. Rather, we would want - // to only have one process output - // everything once, for example the one - // with process number - // zero. ConditionalOStream does - // exactly this: it acts as if it were a - // stream, but only forwards to a real, - // underlying stream if a flag is set. By - // setting this condition to - // this_mpi_process==0, we make sure - // that output is only generated from the - // first process and that we don't get - // the same lines of output over and over - // again, once per process. - // - // With this simple trick, we make sure - // that we don't have to guard each and - // every write to std::cout by a - // prefixed if(this_mpi_process==0). - ConditionalOStream pcout; - - // The next few variables are taken - // verbatim from step-8: - Triangulation triangulation; - DoFHandler dof_handler; - - FESystem fe; - - ConstraintMatrix hanging_node_constraints; - - // In step-8, this would have been the - // place where we would have declared the - // member variables for the sparsity - // pattern, the system matrix, right - // hand, and solution vector. We change - // these declarations to use %parallel - // PETSc objects instead (note that the - // fact that we use the %parallel versions - // is denoted the fact that we use the - // classes from the - // PETScWrappers::MPI namespace; - // sequential versions of these classes - // are in the PETScWrappers - // namespace, i.e. without the MPI - // part). Note also that we do not use a - // separate sparsity pattern, since PETSc - // manages that as part of its matrix - // data structures. - PETScWrappers::MPI::SparseMatrix system_matrix; - - PETScWrappers::MPI::Vector solution; - PETScWrappers::MPI::Vector system_rhs; - - // The next change is that we have to - // declare a variable that indicates the - // MPI communicator over which we are - // supposed to distribute our - // computations. Note that if this is a - // sequential job without support by MPI, - // then PETSc provides some dummy type - // for MPI_Comm, so we do not have to - // care here whether the job is really a - // %parallel one: - MPI_Comm mpi_communicator; - - // Then we have two variables that tell - // us where in the %parallel world we - // are. The first of the following - // variables, n_mpi_processes tells - // us how many MPI processes there exist - // in total, while the second one, - // this_mpi_process, indicates which - // is the number of the present process - // within this space of processes. The - // latter variable will have a unique - // value for each process between zero - // and (less than) - // n_mpi_processes. If this program - // is run on a single machine without MPI - // support, then their values are 1 - // and 0, respectively. - const unsigned int n_mpi_processes; - const unsigned int this_mpi_process; + public: + ElasticProblem (); + ~ElasticProblem (); + void run (); + + private: + void setup_system (); + void assemble_system (); + unsigned int solve (); + void refine_grid (); + void output_results (const unsigned int cycle) const; + + // The first variable is basically only + // for convenience: in %parallel program, + // if each process outputs status + // information, then there quickly is a + // lot of clutter. Rather, we would want + // to only have one process output + // everything once, for example the one + // with process number + // zero. ConditionalOStream does + // exactly this: it acts as if it were a + // stream, but only forwards to a real, + // underlying stream if a flag is set. By + // setting this condition to + // this_mpi_process==0, we make sure + // that output is only generated from the + // first process and that we don't get + // the same lines of output over and over + // again, once per process. + // + // With this simple trick, we make sure + // that we don't have to guard each and + // every write to std::cout by a + // prefixed if(this_mpi_process==0). + ConditionalOStream pcout; + + // The next few variables are taken + // verbatim from step-8: + Triangulation triangulation; + DoFHandler dof_handler; + + FESystem fe; + + ConstraintMatrix hanging_node_constraints; + + // In step-8, this would have been the + // place where we would have declared the + // member variables for the sparsity + // pattern, the system matrix, right + // hand, and solution vector. We change + // these declarations to use %parallel + // PETSc objects instead (note that the + // fact that we use the %parallel versions + // is denoted the fact that we use the + // classes from the + // PETScWrappers::MPI namespace; + // sequential versions of these classes + // are in the PETScWrappers + // namespace, i.e. without the MPI + // part). Note also that we do not use a + // separate sparsity pattern, since PETSc + // manages that as part of its matrix + // data structures. + PETScWrappers::MPI::SparseMatrix system_matrix; + + PETScWrappers::MPI::Vector solution; + PETScWrappers::MPI::Vector system_rhs; + + // The next change is that we have to + // declare a variable that indicates the + // MPI communicator over which we are + // supposed to distribute our + // computations. Note that if this is a + // sequential job without support by MPI, + // then PETSc provides some dummy type + // for MPI_Comm, so we do not have to + // care here whether the job is really a + // %parallel one: + MPI_Comm mpi_communicator; + + // Then we have two variables that tell + // us where in the %parallel world we + // are. The first of the following + // variables, n_mpi_processes tells + // us how many MPI processes there exist + // in total, while the second one, + // this_mpi_process, indicates which + // is the number of the present process + // within this space of processes. The + // latter variable will have a unique + // value for each process between zero + // and (less than) + // n_mpi_processes. If this program + // is run on a single machine without MPI + // support, then their values are 1 + // and 0, respectively. + const unsigned int n_mpi_processes; + const unsigned int this_mpi_process; }; - // The following is again taken from step-8 - // without change: + // The following is again taken from step-8 + // without change: template class RightHandSide : public Function { - public: - RightHandSide (); + public: + RightHandSide (); - virtual void vector_value (const Point &p, - Vector &values) const; + virtual void vector_value (const Point &p, + Vector &values) const; - virtual void vector_value_list (const std::vector > &points, - std::vector > &value_list) const; + virtual void vector_value_list (const std::vector > &points, + std::vector > &value_list) const; }; template RightHandSide::RightHandSide () : - Function (dim) + Function (dim) {} @@ -284,37 +284,37 @@ namespace Step17 } - // The first step in the actual - // implementation of things is the - // constructor of the main class. Apart from - // initializing the same member variables - // that we already had in step-8, we here - // initialize the MPI communicator variable - // we shall use with the global MPI - // communicator linking all processes - // together (in more complex applications, - // one could here use a communicator object - // that only links a subset of all - // processes), and call the Utilities helper - // functions to determine the number of - // processes and where the present one fits - // into this picture. In addition, we make - // sure that output is only generated by the - // (globally) first process. As, - // this_mpi_process is determined after - // creation of pcout, we cannot set the - // condition through the constructor, i.e. by - // pcout(std::cout, this_mpi_process==0), but - // set the condition separately. + // The first step in the actual + // implementation of things is the + // constructor of the main class. Apart from + // initializing the same member variables + // that we already had in step-8, we here + // initialize the MPI communicator variable + // we shall use with the global MPI + // communicator linking all processes + // together (in more complex applications, + // one could here use a communicator object + // that only links a subset of all + // processes), and call the Utilities helper + // functions to determine the number of + // processes and where the present one fits + // into this picture. In addition, we make + // sure that output is only generated by the + // (globally) first process. As, + // this_mpi_process is determined after + // creation of pcout, we cannot set the + // condition through the constructor, i.e. by + // pcout(std::cout, this_mpi_process==0), but + // set the condition separately. template ElasticProblem::ElasticProblem () - : - pcout (std::cout), - dof_handler (triangulation), - fe (FE_Q(1), dim), - mpi_communicator (MPI_COMM_WORLD), - n_mpi_processes (Utilities::MPI::n_mpi_processes(mpi_communicator)), - this_mpi_process (Utilities::MPI::this_mpi_process(mpi_communicator)) + : + pcout (std::cout), + dof_handler (triangulation), + fe (FE_Q(1), dim), + mpi_communicator (MPI_COMM_WORLD), + n_mpi_processes (Utilities::MPI::n_mpi_processes(mpi_communicator)), + this_mpi_process (Utilities::MPI::this_mpi_process(mpi_communicator)) { pcout.set_condition(this_mpi_process == 0); } @@ -328,88 +328,88 @@ namespace Step17 } - // The second step is the function in which - // we set up the various variables for the - // global linear system to be solved. + // The second step is the function in which + // we set up the various variables for the + // global linear system to be solved. template void ElasticProblem::setup_system () { - // Before we even start out setting up the - // system, there is one thing to do for a - // %parallel program: we need to assign - // cells to each of the processes. We do - // this by splitting (partitioning) the - // mesh cells into as many chunks - // (subdomains) as there are processes - // in this MPI job (if this is a sequential - // job, then there is only one job and all - // cells will get a zero as subdomain - // indicator). This is done using an - // interface to the METIS library that does - // this in a very efficient way, trying to - // minimize the number of nodes on the - // interfaces between subdomains. All this - // is hidden behind the following call to a - // deal.II library function: + // Before we even start out setting up the + // system, there is one thing to do for a + // %parallel program: we need to assign + // cells to each of the processes. We do + // this by splitting (partitioning) the + // mesh cells into as many chunks + // (subdomains) as there are processes + // in this MPI job (if this is a sequential + // job, then there is only one job and all + // cells will get a zero as subdomain + // indicator). This is done using an + // interface to the METIS library that does + // this in a very efficient way, trying to + // minimize the number of nodes on the + // interfaces between subdomains. All this + // is hidden behind the following call to a + // deal.II library function: GridTools::partition_triangulation (n_mpi_processes, triangulation); - // As for the linear system: First, we need - // to generate an enumeration for the - // degrees of freedom in our - // problem. Further below, we will show how - // we assign each cell to one of the MPI - // processes before we even get here. What - // we then need to do is to enumerate the - // degrees of freedom in a way so that all - // degrees of freedom associated with cells - // in subdomain zero (which resides on - // process zero) come before all DoFs - // associated with cells on subdomain one, - // before those on cells on process two, - // and so on. We need this since we have to - // split the global vectors for right hand - // side and solution, as well as the matrix - // into contiguous chunks of rows that live - // on each of the processors, and we will - // want to do this in a way that requires - // minimal communication. This is done - // using the following two functions, which - // first generates an initial ordering of - // all degrees of freedom, and then re-sort - // them according to above criterion: + // As for the linear system: First, we need + // to generate an enumeration for the + // degrees of freedom in our + // problem. Further below, we will show how + // we assign each cell to one of the MPI + // processes before we even get here. What + // we then need to do is to enumerate the + // degrees of freedom in a way so that all + // degrees of freedom associated with cells + // in subdomain zero (which resides on + // process zero) come before all DoFs + // associated with cells on subdomain one, + // before those on cells on process two, + // and so on. We need this since we have to + // split the global vectors for right hand + // side and solution, as well as the matrix + // into contiguous chunks of rows that live + // on each of the processors, and we will + // want to do this in a way that requires + // minimal communication. This is done + // using the following two functions, which + // first generates an initial ordering of + // all degrees of freedom, and then re-sort + // them according to above criterion: dof_handler.distribute_dofs (fe); DoFRenumbering::subdomain_wise (dof_handler); - // While we're at it, let us also count how - // many degrees of freedom there exist on - // the present process: + // While we're at it, let us also count how + // many degrees of freedom there exist on + // the present process: const unsigned int n_local_dofs = DoFTools::count_dofs_with_subdomain_association (dof_handler, this_mpi_process); - // Then we initialize the system matrix, - // solution, and right hand side - // vectors. Since they all need to work in - // %parallel, we have to pass them an MPI - // communication object, as well as their - // global sizes (both dimensions are equal - // to the number of degrees of freedom), - // and also how many rows out of this - // global size are to be stored locally - // (n_local_dofs). In addition, PETSc - // needs to know how to partition the - // columns in the chunk of the matrix that - // is stored locally; for square matrices, - // the columns should be partitioned in the - // same way as the rows (indicated by the - // second n_local_dofs in the call) but - // in the case of rectangular matrices one - // has to partition the columns in the same - // way as vectors are partitioned with - // which the matrix is multiplied, while - // rows have to partitioned in the same way - // as destination vectors of matrix-vector - // multiplications: + // Then we initialize the system matrix, + // solution, and right hand side + // vectors. Since they all need to work in + // %parallel, we have to pass them an MPI + // communication object, as well as their + // global sizes (both dimensions are equal + // to the number of degrees of freedom), + // and also how many rows out of this + // global size are to be stored locally + // (n_local_dofs). In addition, PETSc + // needs to know how to partition the + // columns in the chunk of the matrix that + // is stored locally; for square matrices, + // the columns should be partitioned in the + // same way as the rows (indicated by the + // second n_local_dofs in the call) but + // in the case of rectangular matrices one + // has to partition the columns in the same + // way as vectors are partitioned with + // which the matrix is multiplied, while + // rows have to partitioned in the same way + // as destination vectors of matrix-vector + // multiplications: system_matrix.reinit (mpi_communicator, dof_handler.n_dofs(), dof_handler.n_dofs(), @@ -420,15 +420,15 @@ namespace Step17 solution.reinit (mpi_communicator, dof_handler.n_dofs(), n_local_dofs); system_rhs.reinit (mpi_communicator, dof_handler.n_dofs(), n_local_dofs); - // Finally, we need to initialize the - // objects denoting hanging node - // constraints for the present grid. Note - // that since PETSc handles the sparsity - // pattern internally to the matrix, there - // is no need to set up an independent - // sparsity pattern here, and to condense - // it for constraints, as we have done in - // all other example programs. + // Finally, we need to initialize the + // objects denoting hanging node + // constraints for the present grid. Note + // that since PETSc handles the sparsity + // pattern internally to the matrix, there + // is no need to set up an independent + // sparsity pattern here, and to condense + // it for constraints, as we have done in + // all other example programs. hanging_node_constraints.clear (); DoFTools::make_hanging_node_constraints (dof_handler, hanging_node_constraints); @@ -436,82 +436,82 @@ namespace Step17 } - // The third step is to actually assemble the - // matrix and right hand side of the - // problem. There are some things worth - // mentioning before we go into - // detail. First, we will be assembling the - // system in %parallel, i.e. each process will - // be responsible for assembling on cells - // that belong to this particular - // processor. Note that the degrees of - // freedom are split in a way such that all - // DoFs in the interior of cells and between - // cells belonging to the same subdomain - // belong to the process that owns the - // cell. However, even then we sometimes need - // to assemble on a cell with a neighbor that - // belongs to a different process, and in - // these cases when we write the local - // contributions into the global matrix or - // right hand side vector, we actually have - // to transfer these entries to the other - // process. Fortunately, we don't have to do - // this by hand, PETSc does all this for us - // by caching these elements locally, and - // sending them to the other processes as - // necessary when we call the compress() - // functions on the matrix and vector at the - // end of this function. - // - // The second point is that once we - // have handed over matrix and vector - // contributions to PETSc, it is a) - // hard, and b) very inefficient to - // get them back for - // modifications. This is not only - // the fault of PETSc, it is also a - // consequence of the distributed - // nature of this program: if an - // entry resides on another - // processor, then it is necessarily - // expensive to get it. The - // consequence of this is that where - // we previously first assembled the - // matrix and right hand side as if - // there were no hanging node - // constraints and boundary values, - // and then eliminated these in a - // second step, we should now try to - // do that while still assembling the - // local systems, and before handing - // these entries over to PETSc. At - // least as far as eliminating - // hanging nodes is concerned, this - // is actually possible, though - // removing boundary nodes isn't that - // simple. deal.II provides functions - // to do this first part: instead of - // copying elements by hand into the - // global matrix, we use the - // distribute_local_to_global - // functions below to take care of - // hanging nodes at the same - // time. The second step, elimination - // of boundary nodes, is then done in - // exactly the same way as in all - // previous example programs. - // - // So, here is the actual implementation: + // The third step is to actually assemble the + // matrix and right hand side of the + // problem. There are some things worth + // mentioning before we go into + // detail. First, we will be assembling the + // system in %parallel, i.e. each process will + // be responsible for assembling on cells + // that belong to this particular + // processor. Note that the degrees of + // freedom are split in a way such that all + // DoFs in the interior of cells and between + // cells belonging to the same subdomain + // belong to the process that owns the + // cell. However, even then we sometimes need + // to assemble on a cell with a neighbor that + // belongs to a different process, and in + // these cases when we write the local + // contributions into the global matrix or + // right hand side vector, we actually have + // to transfer these entries to the other + // process. Fortunately, we don't have to do + // this by hand, PETSc does all this for us + // by caching these elements locally, and + // sending them to the other processes as + // necessary when we call the compress() + // functions on the matrix and vector at the + // end of this function. + // + // The second point is that once we + // have handed over matrix and vector + // contributions to PETSc, it is a) + // hard, and b) very inefficient to + // get them back for + // modifications. This is not only + // the fault of PETSc, it is also a + // consequence of the distributed + // nature of this program: if an + // entry resides on another + // processor, then it is necessarily + // expensive to get it. The + // consequence of this is that where + // we previously first assembled the + // matrix and right hand side as if + // there were no hanging node + // constraints and boundary values, + // and then eliminated these in a + // second step, we should now try to + // do that while still assembling the + // local systems, and before handing + // these entries over to PETSc. At + // least as far as eliminating + // hanging nodes is concerned, this + // is actually possible, though + // removing boundary nodes isn't that + // simple. deal.II provides functions + // to do this first part: instead of + // copying elements by hand into the + // global matrix, we use the + // distribute_local_to_global + // functions below to take care of + // hanging nodes at the same + // time. The second step, elimination + // of boundary nodes, is then done in + // exactly the same way as in all + // previous example programs. + // + // So, here is the actual implementation: template void ElasticProblem::assemble_system () { - // The infrastructure to assemble linear - // systems is the same as in all the other - // programs, and in particular unchanged - // from step-8. Note that we still use the - // deal.II full matrix and vector types for - // the local systems. + // The infrastructure to assemble linear + // systems is the same as in all the other + // programs, and in particular unchanged + // from step-8. Note that we still use the + // deal.II full matrix and vector types for + // the local systems. QGauss quadrature_formula(2); FEValues fe_values (fe, quadrature_formula, update_values | update_gradients | @@ -535,38 +535,38 @@ namespace Step17 Vector(dim)); - // The next thing is the loop over all - // elements. Note that we do not have to do - // all the work: our job here is only to - // assemble the system on cells that - // actually belong to this MPI process, all - // other cells will be taken care of by - // other processes. This is what the - // if-clause immediately after the for-loop - // takes care of: it queries the subdomain - // identifier of each cell, which is a - // number associated with each cell that - // tells which process handles it. In more - // generality, the subdomain id is used to - // split a domain into several parts (we do - // this above, at the beginning of - // setup_system), and which allows to - // identify which subdomain a cell is - // living on. In this application, we have - // each process handle exactly one - // subdomain, so we identify the terms - // subdomain and MPI process with - // each other. - // - // Apart from this, assembling the local - // system is relatively uneventful if you - // have understood how this is done in - // step-8, and only becomes interesting - // again once we start distributing it into - // the global matrix and right hand sides. + // The next thing is the loop over all + // elements. Note that we do not have to do + // all the work: our job here is only to + // assemble the system on cells that + // actually belong to this MPI process, all + // other cells will be taken care of by + // other processes. This is what the + // if-clause immediately after the for-loop + // takes care of: it queries the subdomain + // identifier of each cell, which is a + // number associated with each cell that + // tells which process handles it. In more + // generality, the subdomain id is used to + // split a domain into several parts (we do + // this above, at the beginning of + // setup_system), and which allows to + // identify which subdomain a cell is + // living on. In this application, we have + // each process handle exactly one + // subdomain, so we identify the terms + // subdomain and MPI process with + // each other. + // + // Apart from this, assembling the local + // system is relatively uneventful if you + // have understood how this is done in + // step-8, and only becomes interesting + // again once we start distributing it into + // the global matrix and right hand sides. typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) if (cell->subdomain_id() == this_mpi_process) { @@ -581,19 +581,19 @@ namespace Step17 for (unsigned int i=0; iget_dof_indices (local_dof_indices); hanging_node_constraints - .distribute_local_to_global (cell_matrix, - local_dof_indices, - system_matrix); + .distribute_local_to_global (cell_matrix, + local_dof_indices, + system_matrix); hanging_node_constraints - .distribute_local_to_global (cell_rhs, - local_dof_indices, - system_rhs); + .distribute_local_to_global (cell_rhs, + local_dof_indices, + system_rhs); } - // The global matrix and right hand side - // vectors have now been formed. Note that - // since we took care of this already - // above, we do not have to condense away - // hanging node constraints any more. - // - // However, we still have to apply boundary - // values, in the same way as we always do: + // The global matrix and right hand side + // vectors have now been formed. Note that + // since we took care of this already + // above, we do not have to condense away + // hanging node constraints any more. + // + // However, we still have to apply boundary + // values, in the same way as we always do: std::map boundary_values; VectorTools::interpolate_boundary_values (dof_handler, 0, @@ -688,77 +688,77 @@ namespace Step17 MatrixTools::apply_boundary_values (boundary_values, system_matrix, solution, system_rhs, false); - // The last argument to the call just - // performed allows for some - // optimizations. It controls - // whether we should also delete the - // column corresponding to a boundary - // node, or keep it (and passing - // true as above means: yes, do - // eliminate the column). If we do, - // then the resulting matrix will be - // symmetric again if it was before; - // if we don't, then it won't. The - // solution of the resulting system - // should be the same, though. The - // only reason why we may want to - // make the system symmetric again is - // that we would like to use the CG - // method, which only works with - // symmetric matrices. Experience - // tells that CG also works (and - // works almost as well) if we don't - // remove the columns associated with - // boundary nodes, which can be - // easily explained by the special - // structure of the - // non-symmetry. Since eliminating - // columns from dense matrices is not - // expensive, though, we let the - // function do it; not doing so is - // more important if the linear - // system is either non-symmetric - // anyway, or we are using the - // non-local version of this function - // (as in all the other example - // programs before) and want to save - // a few cycles during this - // operation. + // The last argument to the call just + // performed allows for some + // optimizations. It controls + // whether we should also delete the + // column corresponding to a boundary + // node, or keep it (and passing + // true as above means: yes, do + // eliminate the column). If we do, + // then the resulting matrix will be + // symmetric again if it was before; + // if we don't, then it won't. The + // solution of the resulting system + // should be the same, though. The + // only reason why we may want to + // make the system symmetric again is + // that we would like to use the CG + // method, which only works with + // symmetric matrices. Experience + // tells that CG also works (and + // works almost as well) if we don't + // remove the columns associated with + // boundary nodes, which can be + // easily explained by the special + // structure of the + // non-symmetry. Since eliminating + // columns from dense matrices is not + // expensive, though, we let the + // function do it; not doing so is + // more important if the linear + // system is either non-symmetric + // anyway, or we are using the + // non-local version of this function + // (as in all the other example + // programs before) and want to save + // a few cycles during this + // operation. } - // The fourth step is to solve the linear - // system, with its distributed matrix and - // vector objects. Fortunately, PETSc offers - // a variety of sequential and %parallel - // solvers, for which we have written - // wrappers that have almost the same - // interface as is used for the deal.II - // solvers used in all previous example - // programs. + // The fourth step is to solve the linear + // system, with its distributed matrix and + // vector objects. Fortunately, PETSc offers + // a variety of sequential and %parallel + // solvers, for which we have written + // wrappers that have almost the same + // interface as is used for the deal.II + // solvers used in all previous example + // programs. template unsigned int ElasticProblem::solve () { - // First, we have to set up a convergence - // monitor, and assign it the accuracy to - // which we would like to solve the linear - // system. Next, an actual solver object - // using PETSc's CG solver which also works - // with %parallel (distributed) vectors and - // matrices. And finally a preconditioner; - // we choose to use a block Jacobi - // preconditioner which works by computing - // an incomplete LU decomposition on each - // block (i.e. the chunk of matrix that is - // stored on each MPI process). That means - // that if you run the program with only - // one process, then you will use an ILU(0) - // as a preconditioner, while if it is run - // on many processes, then we will have a - // number of blocks on the diagonal and the - // preconditioner is the ILU(0) of each of - // these blocks. + // First, we have to set up a convergence + // monitor, and assign it the accuracy to + // which we would like to solve the linear + // system. Next, an actual solver object + // using PETSc's CG solver which also works + // with %parallel (distributed) vectors and + // matrices. And finally a preconditioner; + // we choose to use a block Jacobi + // preconditioner which works by computing + // an incomplete LU decomposition on each + // block (i.e. the chunk of matrix that is + // stored on each MPI process). That means + // that if you run the program with only + // one process, then you will use an ILU(0) + // as a preconditioner, while if it is run + // on many processes, then we will have a + // number of blocks on the diagonal and the + // preconditioner is the ILU(0) of each of + // these blocks. SolverControl solver_control (solution.size(), 1e-8*system_rhs.l2_norm()); PETScWrappers::SolverCG cg (solver_control, @@ -766,182 +766,182 @@ namespace Step17 PETScWrappers::PreconditionBlockJacobi preconditioner(system_matrix); - // Then solve the system: + // Then solve the system: cg.solve (system_matrix, solution, system_rhs, preconditioner); - // The next step is to distribute hanging - // node constraints. This is a little - // tricky, since to fill in the value of a - // constrained node you need access to the - // values of the nodes to which it is - // constrained (for example, for a Q1 - // element in 2d, we need access to the two - // nodes on the big side of a hanging node - // face, to compute the value of the - // constrained node in the middle). Since - // PETSc (and, for that matter, the MPI - // model on which it is built) does not - // allow to query the value of another node - // in a simple way if we should need it, - // what we do here is to get a copy of the - // distributed vector where we keep all - // elements locally. This is simple, since - // the deal.II wrappers have a conversion - // constructor for the non-MPI vector - // class: + // The next step is to distribute hanging + // node constraints. This is a little + // tricky, since to fill in the value of a + // constrained node you need access to the + // values of the nodes to which it is + // constrained (for example, for a Q1 + // element in 2d, we need access to the two + // nodes on the big side of a hanging node + // face, to compute the value of the + // constrained node in the middle). Since + // PETSc (and, for that matter, the MPI + // model on which it is built) does not + // allow to query the value of another node + // in a simple way if we should need it, + // what we do here is to get a copy of the + // distributed vector where we keep all + // elements locally. This is simple, since + // the deal.II wrappers have a conversion + // constructor for the non-MPI vector + // class: PETScWrappers::Vector localized_solution (solution); - // Then we distribute hanging node - // constraints on this local copy, i.e. we - // compute the values of all constrained - // nodes: + // Then we distribute hanging node + // constraints on this local copy, i.e. we + // compute the values of all constrained + // nodes: hanging_node_constraints.distribute (localized_solution); - // Then transfer everything back - // into the global vector. The - // following operation copies those - // elements of the localized - // solution that we store locally - // in the distributed solution, and - // does not touch the other - // ones. Since we do the same - // operation on all processors, we - // end up with a distributed vector - // that has all the constrained - // nodes fixed. + // Then transfer everything back + // into the global vector. The + // following operation copies those + // elements of the localized + // solution that we store locally + // in the distributed solution, and + // does not touch the other + // ones. Since we do the same + // operation on all processors, we + // end up with a distributed vector + // that has all the constrained + // nodes fixed. solution = localized_solution; - // After this has happened, flush the PETSc - // buffers. This may or may not be strictly - // necessary here (the PETSc documentation - // is not very verbose on these things), - // but certainly doesn't hurt either. + // After this has happened, flush the PETSc + // buffers. This may or may not be strictly + // necessary here (the PETSc documentation + // is not very verbose on these things), + // but certainly doesn't hurt either. solution.compress (); - // Finally return the number of iterations - // it took to converge, to allow for some - // output: + // Finally return the number of iterations + // it took to converge, to allow for some + // output: return solver_control.last_step(); } - // Step five is to output the results we - // computed in this iteration. This is - // actually the same as done in step-8 - // before, with two small differences. First, - // all processes call this function, but not - // all of them need to do the work associated - // with generating output. In fact, they - // shouldn't, since we would try to write to - // the same file multiple times at once. So - // we let only the first job do this, and all - // the other ones idle around during this - // time (or start their work for the next - // iteration, or simply yield their CPUs to - // other jobs that happen to run at the same - // time). The second thing is that we not - // only output the solution vector, but also - // a vector that indicates which subdomain - // each cell belongs to. This will make for - // some nice pictures of partitioned domains. - // - // In practice, the present implementation of - // the output function is a major bottleneck - // of this program, since generating - // graphical output is expensive and doing so - // only on one process does, of course, not - // scale if we significantly increase the - // number of processes. In effect, this - // function will consume most of the run-time - // if you go to very large numbers of - // unknowns and processes, and real - // applications should limit the number of - // times they generate output through this - // function. - // - // The solution to this is to have - // each process generate output data - // only for it's own local cells, and - // write them to separate files, one - // file per process. This would - // distribute the work of generating - // the output to all processes - // equally. In a second step, - // separate from running this - // program, we would then take all - // the output files for a given cycle - // and merge these parts into one - // single output file. This has to be - // done sequentially, but can be done - // on a different machine, and should - // be relatively cheap. However, the - // necessary functionality for this - // is not yet implemented in the - // library, and since we are too - // close to the next release, we do - // not want to do such major - // destabilizing changes any - // more. This has been fixed in the - // meantime, though, and a better way - // to do things is explained in the - // step-18 example program. + // Step five is to output the results we + // computed in this iteration. This is + // actually the same as done in step-8 + // before, with two small differences. First, + // all processes call this function, but not + // all of them need to do the work associated + // with generating output. In fact, they + // shouldn't, since we would try to write to + // the same file multiple times at once. So + // we let only the first job do this, and all + // the other ones idle around during this + // time (or start their work for the next + // iteration, or simply yield their CPUs to + // other jobs that happen to run at the same + // time). The second thing is that we not + // only output the solution vector, but also + // a vector that indicates which subdomain + // each cell belongs to. This will make for + // some nice pictures of partitioned domains. + // + // In practice, the present implementation of + // the output function is a major bottleneck + // of this program, since generating + // graphical output is expensive and doing so + // only on one process does, of course, not + // scale if we significantly increase the + // number of processes. In effect, this + // function will consume most of the run-time + // if you go to very large numbers of + // unknowns and processes, and real + // applications should limit the number of + // times they generate output through this + // function. + // + // The solution to this is to have + // each process generate output data + // only for it's own local cells, and + // write them to separate files, one + // file per process. This would + // distribute the work of generating + // the output to all processes + // equally. In a second step, + // separate from running this + // program, we would then take all + // the output files for a given cycle + // and merge these parts into one + // single output file. This has to be + // done sequentially, but can be done + // on a different machine, and should + // be relatively cheap. However, the + // necessary functionality for this + // is not yet implemented in the + // library, and since we are too + // close to the next release, we do + // not want to do such major + // destabilizing changes any + // more. This has been fixed in the + // meantime, though, and a better way + // to do things is explained in the + // step-18 example program. template void ElasticProblem::output_results (const unsigned int cycle) const { - // One point to realize is that when we - // want to generate output on process zero - // only, we need to have access to all - // elements of the solution vector. So we - // need to get a local copy of the - // distributed vector, which is in fact - // simple: + // One point to realize is that when we + // want to generate output on process zero + // only, we need to have access to all + // elements of the solution vector. So we + // need to get a local copy of the + // distributed vector, which is in fact + // simple: const PETScWrappers::Vector localized_solution (solution); - // The thing to notice, however, is that - // we do this localization operation on all - // processes, not only the one that - // actually needs the data. This can't be - // avoided, however, with the communication - // model of MPI: MPI does not have a way to - // query data on another process, both - // sides have to initiate a communication - // at the same time. So even though most of - // the processes do not need the localized - // solution, we have to place the call here - // so that all processes execute it. - // - // (In reality, part of this work can in - // fact be avoided. What we do is send the - // local parts of all processes to all - // other processes. What we would really - // need to do is to initiate an operation - // on all processes where each process - // simply sends its local chunk of data to - // process zero, since this is the only one - // that actually needs it, i.e. we need - // something like a gather operation. PETSc - // can do this, but for simplicity's sake - // we don't attempt to make use of this - // here. We don't, since what we do is not - // very expensive in the grand scheme of - // things: it is one vector communication - // among all processes , which has to be - // compared to the number of communications - // we have to do when solving the linear - // system, setting up the block-ILU for the - // preconditioner, and other operations.) - - // This being done, process zero goes ahead - // with setting up the output file as in - // step-8, and attaching the (localized) - // solution vector to the output - // object:. (The code to generate the output - // file name is stolen and slightly - // modified from step-5, since we expect - // that we can do a number of cycles - // greater than 10, which is the maximum of - // what the code in step-8 could handle.) + // The thing to notice, however, is that + // we do this localization operation on all + // processes, not only the one that + // actually needs the data. This can't be + // avoided, however, with the communication + // model of MPI: MPI does not have a way to + // query data on another process, both + // sides have to initiate a communication + // at the same time. So even though most of + // the processes do not need the localized + // solution, we have to place the call here + // so that all processes execute it. + // + // (In reality, part of this work can in + // fact be avoided. What we do is send the + // local parts of all processes to all + // other processes. What we would really + // need to do is to initiate an operation + // on all processes where each process + // simply sends its local chunk of data to + // process zero, since this is the only one + // that actually needs it, i.e. we need + // something like a gather operation. PETSc + // can do this, but for simplicity's sake + // we don't attempt to make use of this + // here. We don't, since what we do is not + // very expensive in the grand scheme of + // things: it is one vector communication + // among all processes , which has to be + // compared to the number of communications + // we have to do when solving the linear + // system, setting up the block-ILU for the + // preconditioner, and other operations.) + + // This being done, process zero goes ahead + // with setting up the output file as in + // step-8, and attaching the (localized) + // solution vector to the output + // object:. (The code to generate the output + // file name is stolen and slightly + // modified from step-5, since we expect + // that we can do a number of cycles + // greater than 10, which is the maximum of + // what the code in step-8 could handle.) if (this_mpi_process == 0) { std::ostringstream filename; @@ -955,51 +955,51 @@ namespace Step17 std::vector solution_names; switch (dim) { - case 1: - solution_names.push_back ("displacement"); - break; - case 2: - solution_names.push_back ("x_displacement"); - solution_names.push_back ("y_displacement"); - break; - case 3: - solution_names.push_back ("x_displacement"); - solution_names.push_back ("y_displacement"); - solution_names.push_back ("z_displacement"); - break; - default: - Assert (false, ExcInternalError()); + case 1: + solution_names.push_back ("displacement"); + break; + case 2: + solution_names.push_back ("x_displacement"); + solution_names.push_back ("y_displacement"); + break; + case 3: + solution_names.push_back ("x_displacement"); + solution_names.push_back ("y_displacement"); + solution_names.push_back ("z_displacement"); + break; + default: + Assert (false, ExcInternalError()); } data_out.add_data_vector (localized_solution, solution_names); - // The only thing we do here - // additionally is that we also output - // one value per cell indicating which - // subdomain (i.e. MPI process) it - // belongs to. This requires some - // conversion work, since the data the - // library provides us with is not the - // one the output class expects, but - // this is not difficult. First, set up - // a vector of integers, one per cell, - // that is then filled by the number of - // subdomain each cell is in: + // The only thing we do here + // additionally is that we also output + // one value per cell indicating which + // subdomain (i.e. MPI process) it + // belongs to. This requires some + // conversion work, since the data the + // library provides us with is not the + // one the output class expects, but + // this is not difficult. First, set up + // a vector of integers, one per cell, + // that is then filled by the number of + // subdomain each cell is in: std::vector partition_int (triangulation.n_active_cells()); GridTools::get_subdomain_association (triangulation, partition_int); - // Then convert this integer vector - // into a floating point vector just as - // the output functions want to see: + // Then convert this integer vector + // into a floating point vector just as + // the output functions want to see: const Vector partitioning(partition_int.begin(), partition_int.end()); - // And finally add this vector as well: + // And finally add this vector as well: data_out.add_data_vector (partitioning, "partitioning"); - // This all being done, generate the - // intermediate format and write it out - // in GMV output format: + // This all being done, generate the + // intermediate format and write it out + // in GMV output format: data_out.build_patches (); data_out.write_gmv (output); } @@ -1007,55 +1007,55 @@ namespace Step17 - // The sixth step is to take the solution - // just computed, and evaluate some kind of - // refinement indicator to refine the - // mesh. The problem is basically the same as - // with distributing hanging node - // constraints: in order to compute the error - // indicator, we need access to all elements - // of the solution vector. We then compute - // the indicators for the cells that belong - // to the present process, but then we need - // to distribute the refinement indicators - // into a distributed vector so that all - // processes have the values of the - // refinement indicator for all cells. But - // then, in order for each process to refine - // its copy of the mesh, they need to have - // acces to all refinement indicators - // locally, so they have to copy the global - // vector back into a local one. That's a - // little convoluted, but thinking about it - // quite straightforward nevertheless. So - // here's how we do it: + // The sixth step is to take the solution + // just computed, and evaluate some kind of + // refinement indicator to refine the + // mesh. The problem is basically the same as + // with distributing hanging node + // constraints: in order to compute the error + // indicator, we need access to all elements + // of the solution vector. We then compute + // the indicators for the cells that belong + // to the present process, but then we need + // to distribute the refinement indicators + // into a distributed vector so that all + // processes have the values of the + // refinement indicator for all cells. But + // then, in order for each process to refine + // its copy of the mesh, they need to have + // acces to all refinement indicators + // locally, so they have to copy the global + // vector back into a local one. That's a + // little convoluted, but thinking about it + // quite straightforward nevertheless. So + // here's how we do it: template void ElasticProblem::refine_grid () { - // So, first part: get a local copy of the - // distributed solution vector. This is - // necessary since the error estimator - // needs to get at the value of neighboring - // cells even if they do not belong to the - // subdomain associated with the present - // MPI process: + // So, first part: get a local copy of the + // distributed solution vector. This is + // necessary since the error estimator + // needs to get at the value of neighboring + // cells even if they do not belong to the + // subdomain associated with the present + // MPI process: const PETScWrappers::Vector localized_solution (solution); - // Second part: set up a vector of error - // indicators for all cells and let the - // Kelly class compute refinement - // indicators for all cells belonging to - // the present subdomain/process. Note that - // the last argument of the call indicates - // which subdomain we are interested - // in. The three arguments before it are - // various other default arguments that one - // usually doesn't need (and doesn't state - // values for, but rather uses the - // defaults), but which we have to state - // here explicitly since we want to modify - // the value of a following argument - // (i.e. the one indicating the subdomain): + // Second part: set up a vector of error + // indicators for all cells and let the + // Kelly class compute refinement + // indicators for all cells belonging to + // the present subdomain/process. Note that + // the last argument of the call indicates + // which subdomain we are interested + // in. The three arguments before it are + // various other default arguments that one + // usually doesn't need (and doesn't state + // values for, but rather uses the + // defaults), but which we have to state + // here explicitly since we want to modify + // the value of a following argument + // (i.e. the one indicating the subdomain): Vector local_error_per_cell (triangulation.n_active_cells()); KellyErrorEstimator::estimate (dof_handler, QGauss(2), @@ -1067,75 +1067,75 @@ namespace Step17 multithread_info.n_default_threads, this_mpi_process); - // Now all processes have computed error - // indicators for their own cells and - // stored them in the respective elements - // of the local_error_per_cell - // vector. The elements of this vector for - // cells not on the present process are - // zero. However, since all processes have - // a copy of a copy of the entire - // triangulation and need to keep these - // copies in synch, they need the values of - // refinement indicators for all cells of - // the triangulation. Thus, we need to - // distribute our results. We do this by - // creating a distributed vector where each - // process has its share, and sets the - // elements it has computed. We will then - // later generate a local sequential copy - // of this distributed vector to allow each - // process to access all elements of this - // vector. - // - // So in the first step, we need to set up - // a %parallel vector. For simplicity, every - // process will own a chunk with as many - // elements as this process owns cells, so - // that the first chunk of elements is - // stored with process zero, the next chunk - // with process one, and so on. It is - // important to remark, however, that these - // elements are not necessarily the ones we - // will write to. This is so, since the - // order in which cells are arranged, - // i.e. the order in which the elements of - // the vector correspond to cells, is not - // ordered according to the subdomain these - // cells belong to. In other words, if on - // this process we compute indicators for - // cells of a certain subdomain, we may - // write the results to more or less random - // elements if the distributed vector, that - // do not necessarily lie within the chunk - // of vector we own on the present - // process. They will subsequently have to - // be copied into another process's memory - // space then, an operation that PETSc does - // for us when we call the compress - // function. This inefficiency could be - // avoided with some more code, but we - // refrain from it since it is not a major - // factor in the program's total runtime. - // - // So here's how we do it: count how many - // cells belong to this process, set up a - // distributed vector with that many - // elements to be stored locally, and copy - // over the elements we computed locally, - // then compress the result. In fact, we - // really only copy the elements that are - // nonzero, so we may miss a few that we - // computed to zero, but this won't hurt - // since the original values of the vector - // is zero anyway. + // Now all processes have computed error + // indicators for their own cells and + // stored them in the respective elements + // of the local_error_per_cell + // vector. The elements of this vector for + // cells not on the present process are + // zero. However, since all processes have + // a copy of a copy of the entire + // triangulation and need to keep these + // copies in synch, they need the values of + // refinement indicators for all cells of + // the triangulation. Thus, we need to + // distribute our results. We do this by + // creating a distributed vector where each + // process has its share, and sets the + // elements it has computed. We will then + // later generate a local sequential copy + // of this distributed vector to allow each + // process to access all elements of this + // vector. + // + // So in the first step, we need to set up + // a %parallel vector. For simplicity, every + // process will own a chunk with as many + // elements as this process owns cells, so + // that the first chunk of elements is + // stored with process zero, the next chunk + // with process one, and so on. It is + // important to remark, however, that these + // elements are not necessarily the ones we + // will write to. This is so, since the + // order in which cells are arranged, + // i.e. the order in which the elements of + // the vector correspond to cells, is not + // ordered according to the subdomain these + // cells belong to. In other words, if on + // this process we compute indicators for + // cells of a certain subdomain, we may + // write the results to more or less random + // elements if the distributed vector, that + // do not necessarily lie within the chunk + // of vector we own on the present + // process. They will subsequently have to + // be copied into another process's memory + // space then, an operation that PETSc does + // for us when we call the compress + // function. This inefficiency could be + // avoided with some more code, but we + // refrain from it since it is not a major + // factor in the program's total runtime. + // + // So here's how we do it: count how many + // cells belong to this process, set up a + // distributed vector with that many + // elements to be stored locally, and copy + // over the elements we computed locally, + // then compress the result. In fact, we + // really only copy the elements that are + // nonzero, so we may miss a few that we + // computed to zero, but this won't hurt + // since the original values of the vector + // is zero anyway. const unsigned int n_local_cells = GridTools::count_cells_with_subdomain_association (triangulation, this_mpi_process); PETScWrappers::MPI::Vector - distributed_all_errors (mpi_communicator, - triangulation.n_active_cells(), - n_local_cells); + distributed_all_errors (mpi_communicator, + triangulation.n_active_cells(), + n_local_cells); for (unsigned int i=0; i localized_all_errors (distributed_all_errors); - // ...which we can the subsequently use to - // finally refine the grid: + // ...which we can the subsequently use to + // finally refine the grid: GridRefinement::refine_and_coarsen_fixed_number (triangulation, localized_all_errors, 0.3, 0.03); @@ -1159,14 +1159,14 @@ namespace Step17 - // Lastly, here is the driver function. It is - // almost unchanged from step-8, with the - // exception that we replace std::cout by - // the pcout stream. Apart from this, the - // only other cosmetic change is that we - // output how many degrees of freedom there - // are per process, and how many iterations - // it took for the linear solver to converge: + // Lastly, here is the driver function. It is + // almost unchanged from step-8, with the + // exception that we replace std::cout by + // the pcout stream. Apart from this, the + // only other cosmetic change is that we + // output how many degrees of freedom there + // are per process, and how many iterations + // it took for the linear solver to converge: template void ElasticProblem::run () { @@ -1210,12 +1210,12 @@ namespace Step17 } - // So that's it, almost. main() works the - // same way as most of the main functions in - // the other example programs, i.e. it - // delegates work to the run function of - // a master object, and only wraps everything - // into some code to catch exceptions: +// So that's it, almost. main() works the +// same way as most of the main functions in +// the other example programs, i.e. it +// delegates work to the run function of +// a master object, and only wraps everything +// into some code to catch exceptions: int main (int argc, char **argv) { try @@ -1223,24 +1223,24 @@ int main (int argc, char **argv) using namespace dealii; using namespace Step17; - // Here is the only real difference: - // PETSc requires that we initialize it - // at the beginning of the program, and - // un-initialize it at the end. The - // class MPI_InitFinalize takes care - // of that. The original code - // sits in between, enclosed in braces - // to make sure that the - // elastic_problem variable goes - // out of scope (and is destroyed) - // before PETSc is closed with - // PetscFinalize. (If we wouldn't - // use braces, the destructor of - // elastic_problem would run after - // PetscFinalize; since the - // destructor involves calls to PETSc - // functions, we would get strange - // error messages from PETSc.) + // Here is the only real difference: + // PETSc requires that we initialize it + // at the beginning of the program, and + // un-initialize it at the end. The + // class MPI_InitFinalize takes care + // of that. The original code + // sits in between, enclosed in braces + // to make sure that the + // elastic_problem variable goes + // out of scope (and is destroyed) + // before PETSc is closed with + // PetscFinalize. (If we wouldn't + // use braces, the destructor of + // elastic_problem would run after + // PetscFinalize; since the + // destructor involves calls to PETSc + // functions, we would get strange + // error messages from PETSc.) Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv); { diff --git a/deal.II/examples/step-18/step-18.cc b/deal.II/examples/step-18/step-18.cc index 4b41046f84..af124c3f5b 100644 --- a/deal.II/examples/step-18/step-18.cc +++ b/deal.II/examples/step-18/step-18.cc @@ -10,9 +10,9 @@ /* further information on this license. */ - // First the usual list of header files that - // have already been used in previous example - // programs: +// First the usual list of header files that +// have already been used in previous example +// programs: #include #include #include @@ -46,81 +46,81 @@ #include #include - // And here the only two new things among the - // header files: an include file in which - // symmetric tensors of rank 2 and 4 are - // implemented, as introduced in the - // introduction: +// And here the only two new things among the +// header files: an include file in which +// symmetric tensors of rank 2 and 4 are +// implemented, as introduced in the +// introduction: #include - // And a header that implements filters for - // iterators looping over all cells. We will - // use this when selecting only those cells - // for output that are owned by the present - // process in a %parallel program: +// And a header that implements filters for +// iterators looping over all cells. We will +// use this when selecting only those cells +// for output that are owned by the present +// process in a %parallel program: #include - // This is then simply C++ again: +// This is then simply C++ again: #include #include #include #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step18 { using namespace dealii; - // @sect3{The PointHistory class} - - // As was mentioned in the introduction, we - // have to store the old stress in - // quadrature point so that we can compute - // the residual forces at this point during - // the next time step. This alone would not - // warrant a structure with only one - // member, but in more complicated - // applications, we would have to store - // more information in quadrature points as - // well, such as the history variables of - // plasticity, etc. In essence, we have to - // store everything that affects the - // present state of the material here, - // which in plasticity is determined by the - // deformation history variables. - // - // We will not give this class any - // meaningful functionality beyond being - // able to store data, i.e. there are no - // constructors, destructors, or other - // member functions. In such cases of - // `dumb' classes, we usually opt to - // declare them as struct rather than - // class, to indicate that they are - // closer to C-style structures than - // C++-style classes. + // @sect3{The PointHistory class} + + // As was mentioned in the introduction, we + // have to store the old stress in + // quadrature point so that we can compute + // the residual forces at this point during + // the next time step. This alone would not + // warrant a structure with only one + // member, but in more complicated + // applications, we would have to store + // more information in quadrature points as + // well, such as the history variables of + // plasticity, etc. In essence, we have to + // store everything that affects the + // present state of the material here, + // which in plasticity is determined by the + // deformation history variables. + // + // We will not give this class any + // meaningful functionality beyond being + // able to store data, i.e. there are no + // constructors, destructors, or other + // member functions. In such cases of + // `dumb' classes, we usually opt to + // declare them as struct rather than + // class, to indicate that they are + // closer to C-style structures than + // C++-style classes. template struct PointHistory { - SymmetricTensor<2,dim> old_stress; + SymmetricTensor<2,dim> old_stress; }; - // @sect3{The stress-strain tensor} - - // Next, we define the linear relationship - // between the stress and the strain in - // elasticity. It is given by a tensor of - // rank 4 that is usually written in the - // form $C_{ijkl} = \mu (\delta_{ik} - // \delta_{jl} + \delta_{il} \delta_{jk}) + - // \lambda \delta_{ij} \delta_{kl}$. This - // tensor maps symmetric tensor of rank 2 - // to symmetric tensors of rank 2. A - // function implementing its creation for - // given values of the Lame constants - // lambda and mu is straightforward: + // @sect3{The stress-strain tensor} + + // Next, we define the linear relationship + // between the stress and the strain in + // elasticity. It is given by a tensor of + // rank 4 that is usually written in the + // form $C_{ijkl} = \mu (\delta_{ik} + // \delta_{jl} + \delta_{il} \delta_{jk}) + + // \lambda \delta_{ij} \delta_{kl}$. This + // tensor maps symmetric tensor of rank 2 + // to symmetric tensors of rank 2. A + // function implementing its creation for + // given values of the Lame constants + // lambda and mu is straightforward: template SymmetricTensor<4,dim> get_stress_strain_tensor (const double lambda, const double mu) @@ -128,204 +128,204 @@ namespace Step18 SymmetricTensor<4,dim> tmp; for (unsigned int i=0; iinline. - // - // The first one computes the - // symmetric strain tensor for - // shape function shape_func at - // quadrature point q_point by - // forming the symmetric gradient - // of this shape function. We need - // that when we want to form the - // matrix, for example. - // - // We should note that in previous - // examples where we have treated - // vector-valued problems, we have - // always asked the finite element - // object in which of the vector - // component the shape function is - // actually non-zero, and thereby - // avoided to compute any terms - // that we could prove were zero - // anyway. For this, we used the - // fe.system_to_component_index - // function that returns in which - // component a shape function was - // zero, and also that the - // fe_values.shape_value and - // fe_values.shape_grad - // functions only returned the - // value and gradient of the single - // non-zero component of a shape - // function if this is a - // vector-valued element. - // - // This was an optimization, and if - // it isn't terribly time critical, - // we can get away with a simpler - // technique: just ask the - // fe_values for the value or - // gradient of a given component of - // a given shape function at a - // given quadrature point. This is - // what the - // fe_values.shape_grad_component(shape_func,q_point,i) - // call does: return the full - // gradient of the ith - // component of shape function - // shape_func at quadrature - // point q_point. If a certain - // component of a certain shape - // function is always zero, then - // this will simply always return - // zero. - // - // As mentioned, using - // fe_values.shape_grad_component - // instead of the combination of - // fe.system_to_component_index - // and fe_values.shape_grad may - // be less efficient, but its - // implementation is optimized for - // such cases and shouldn't be a - // big slowdown. We demonstrate the - // technique here since it is so - // much simpler and - // straightforward. + // With this function, we will + // define a static member variable + // of the main class below that + // will be used throughout the + // program as the stress-strain + // tensor. Note that + // in more elaborate programs, this will + // probably be a member variable of some + // class instead, or a function that + // returns the stress-strain relationship + // depending on other input. For example in + // damage theory models, the Lame constants + // are considered a function of the prior + // stress/strain history of a + // point. Conversely, in plasticity the + // form of the stress-strain tensor is + // modified if the material has reached the + // yield stress in a certain point, and + // possibly also depending on its prior + // history. + // + // In the present program, however, we + // assume that the material is completely + // elastic and linear, and a constant + // stress-strain tensor is sufficient for + // our present purposes. + + + + // @sect3{Auxiliary functions} + + // Before the rest of the program, + // here are a few functions that we + // need as tools. These are small + // functions that are called in + // inner loops, so we mark them as + // inline. + // + // The first one computes the + // symmetric strain tensor for + // shape function shape_func at + // quadrature point q_point by + // forming the symmetric gradient + // of this shape function. We need + // that when we want to form the + // matrix, for example. + // + // We should note that in previous + // examples where we have treated + // vector-valued problems, we have + // always asked the finite element + // object in which of the vector + // component the shape function is + // actually non-zero, and thereby + // avoided to compute any terms + // that we could prove were zero + // anyway. For this, we used the + // fe.system_to_component_index + // function that returns in which + // component a shape function was + // zero, and also that the + // fe_values.shape_value and + // fe_values.shape_grad + // functions only returned the + // value and gradient of the single + // non-zero component of a shape + // function if this is a + // vector-valued element. + // + // This was an optimization, and if + // it isn't terribly time critical, + // we can get away with a simpler + // technique: just ask the + // fe_values for the value or + // gradient of a given component of + // a given shape function at a + // given quadrature point. This is + // what the + // fe_values.shape_grad_component(shape_func,q_point,i) + // call does: return the full + // gradient of the ith + // component of shape function + // shape_func at quadrature + // point q_point. If a certain + // component of a certain shape + // function is always zero, then + // this will simply always return + // zero. + // + // As mentioned, using + // fe_values.shape_grad_component + // instead of the combination of + // fe.system_to_component_index + // and fe_values.shape_grad may + // be less efficient, but its + // implementation is optimized for + // such cases and shouldn't be a + // big slowdown. We demonstrate the + // technique here since it is so + // much simpler and + // straightforward. template inline SymmetricTensor<2,dim> get_strain (const FEValues &fe_values, - const unsigned int shape_func, - const unsigned int q_point) + const unsigned int shape_func, + const unsigned int q_point) { - // Declare a temporary that will - // hold the return value: + // Declare a temporary that will + // hold the return value: SymmetricTensor<2,dim> tmp; - // First, fill diagonal terms - // which are simply the - // derivatives in direction i - // of the i component of the - // vector-valued shape - // function: + // First, fill diagonal terms + // which are simply the + // derivatives in direction i + // of the i component of the + // vector-valued shape + // function: for (unsigned int i=0; iSymmetricTensor class - // makes sure that at least to - // the outside the symmetric - // entries are also filled (in - // practice, the class of course - // stores only one copy). Here, - // we have picked the upper right - // half of the tensor, but the - // lower left one would have been - // just as good: + // Then fill the rest of the + // strain tensor. Note that since + // the tensor is symmetric, we + // only have to compute one half + // (here: the upper right corner) + // of the off-diagonal elements, + // and the implementation of the + // SymmetricTensor class + // makes sure that at least to + // the outside the symmetric + // entries are also filled (in + // practice, the class of course + // stores only one copy). Here, + // we have picked the upper right + // half of the tensor, but the + // lower left one would have been + // just as good: for (unsigned int i=0; ife_values.get_function_grads - // function allows you to extract - // the gradients of each component - // of your solution field at a - // quadrature point. It returns - // this as a vector of rank-1 - // tensors: one rank-1 tensor - // (gradient) per vector component - // of the solution. From this we - // have to reconstruct the - // (symmetric) strain tensor by - // transforming the data storage - // format and symmetrization. We do - // this in the same way as above, - // i.e. we avoid a few computations - // by filling first the diagonal - // and then only one half of the - // symmetric tensor (the - // SymmetricTensor class makes - // sure that it is sufficient to - // write only one of the two - // symmetric components). - // - // Before we do this, though, we - // make sure that the input has the - // kind of structure we expect: - // that is that there are dim - // vector components, i.e. one - // displacement component for each - // coordinate direction. We test - // this with the Assert macro - // that will simply abort our - // program if the condition is not - // met. + // The second function does + // something very similar (and + // therefore is given the same + // name): compute the symmetric + // strain tensor from the gradient + // of a vector-valued field. If you + // already have a solution field, + // the + // fe_values.get_function_grads + // function allows you to extract + // the gradients of each component + // of your solution field at a + // quadrature point. It returns + // this as a vector of rank-1 + // tensors: one rank-1 tensor + // (gradient) per vector component + // of the solution. From this we + // have to reconstruct the + // (symmetric) strain tensor by + // transforming the data storage + // format and symmetrization. We do + // this in the same way as above, + // i.e. we avoid a few computations + // by filling first the diagonal + // and then only one half of the + // symmetric tensor (the + // SymmetricTensor class makes + // sure that it is sufficient to + // write only one of the two + // symmetric components). + // + // Before we do this, though, we + // make sure that the input has the + // kind of structure we expect: + // that is that there are dim + // vector components, i.e. one + // displacement component for each + // coordinate direction. We test + // this with the Assert macro + // that will simply abort our + // program if the condition is not + // met. template inline SymmetricTensor<2,dim> @@ -339,506 +339,514 @@ namespace Step18 for (unsigned int i=0; i get_rotation_matrix (const std::vector > &grad_u) { - // First, compute the curl of the - // velocity field from the - // gradients. Note that we are in 2d, so - // the rotation is a scalar: + // First, compute the curl of the + // velocity field from the + // gradients. Note that we are in 2d, so + // the rotation is a scalar: const double curl = (grad_u[1][0] - grad_u[0][1]); - // From this, compute the angle of - // rotation: + // From this, compute the angle of + // rotation: const double angle = std::atan (curl); - // And from this, build the antisymmetric - // rotation matrix: + // And from this, build the antisymmetric + // rotation matrix: const double t[2][2] = {{ cos(angle), sin(angle) }, - {-sin(angle), cos(angle) }}; + {-sin(angle), cos(angle) } + }; return Tensor<2,2>(t); } - // The 3d case is a little more contrived: + // The 3d case is a little more contrived: Tensor<2,3> get_rotation_matrix (const std::vector > &grad_u) { - // Again first compute the curl of the - // velocity field. This time, it is a - // real vector: + // Again first compute the curl of the + // velocity field. This time, it is a + // real vector: const Point<3> curl (grad_u[2][1] - grad_u[1][2], - grad_u[0][2] - grad_u[2][0], - grad_u[1][0] - grad_u[0][1]); + grad_u[0][2] - grad_u[2][0], + grad_u[1][0] - grad_u[0][1]); - // From this vector, using its magnitude, - // compute the tangent of the angle of - // rotation, and from it the actual - // angle: + // From this vector, using its magnitude, + // compute the tangent of the angle of + // rotation, and from it the actual + // angle: const double tan_angle = std::sqrt(curl*curl); const double angle = std::atan (tan_angle); - // Now, here's one problem: if the angle - // of rotation is too small, that means - // that there is no rotation going on - // (for example a translational - // motion). In that case, the rotation - // matrix is the identity matrix. - // - // The reason why we stress that is that - // in this case we have that - // tan_angle==0. Further down, we - // need to divide by that number in the - // computation of the axis of rotation, - // and we would get into trouble when - // dividing doing so. Therefore, let's - // shortcut this and simply return the - // identity matrix if the angle of - // rotation is really small: + // Now, here's one problem: if the angle + // of rotation is too small, that means + // that there is no rotation going on + // (for example a translational + // motion). In that case, the rotation + // matrix is the identity matrix. + // + // The reason why we stress that is that + // in this case we have that + // tan_angle==0. Further down, we + // need to divide by that number in the + // computation of the axis of rotation, + // and we would get into trouble when + // dividing doing so. Therefore, let's + // shortcut this and simply return the + // identity matrix if the angle of + // rotation is really small: if (angle < 1e-9) { - static const double rotation[3][3] - = {{ 1, 0, 0}, { 0, 1, 0 }, { 0, 0, 1 } }; - static const Tensor<2,3> rot(rotation); - return rot; + static const double rotation[3][3] + = {{ 1, 0, 0}, { 0, 1, 0 }, { 0, 0, 1 } }; + static const Tensor<2,3> rot(rotation); + return rot; } - // Otherwise compute the real rotation - // matrix. The algorithm for this is not - // exactly obvious, but can be found in a - // number of books, particularly on - // computer games where rotation is a - // very frequent operation. Online, you - // can find a description at - // http://www.makegames.com/3drotation/ - // and (this particular form, with the - // signs as here) at - // http://www.gamedev.net/reference/articles/article1199.asp: + // Otherwise compute the real rotation + // matrix. The algorithm for this is not + // exactly obvious, but can be found in a + // number of books, particularly on + // computer games where rotation is a + // very frequent operation. Online, you + // can find a description at + // http://www.makegames.com/3drotation/ + // and (this particular form, with the + // signs as here) at + // http://www.gamedev.net/reference/articles/article1199.asp: const double c = std::cos(angle); const double s = std::sin(angle); const double t = 1-c; const Point<3> axis = curl/tan_angle; const double rotation[3][3] - = {{ t*axis[0]*axis[0]+c, - t*axis[0]*axis[1]+s*axis[2], - t*axis[0]*axis[2]-s*axis[1]}, - { t*axis[0]*axis[1]-s*axis[2], - t*axis[1]*axis[1]+c, - t*axis[1]*axis[2]+s*axis[0]}, - { t*axis[0]*axis[2]+s*axis[1], - t*axis[1]*axis[1]-s*axis[0], - t*axis[2]*axis[2]+c } }; + = {{ + t *axis[0] *axis[0]+c, + t *axis[0] *axis[1]+s *axis[2], + t *axis[0] *axis[2]-s *axis[1] + }, + { + t *axis[0] *axis[1]-s *axis[2], + t *axis[1] *axis[1]+c, + t *axis[1] *axis[2]+s *axis[0] + }, + { + t *axis[0] *axis[2]+s *axis[1], + t *axis[1] *axis[1]-s *axis[0], + t *axis[2] *axis[2]+c + } + }; return Tensor<2,3>(rotation); } - // @sect3{The TopLevel class} - - // This is the main class of the - // program. Since the namespace already - // indicates what problem we are solving, - // let's call it by what it does: it - // directs the flow of the program, i.e. it - // is the toplevel driver. - // - // The member variables of this class are - // essentially as before, i.e. it has to - // have a triangulation, a DoF handler and - // associated objects such as constraints, - // variables that describe the linear - // system, etc. There are a good number of - // more member functions now, which we will - // explain below. - // - // The external interface of the class, - // however, is unchanged: it has a public - // constructor and desctructor, and it has - // a run function that initiated all - // the work. + // @sect3{The TopLevel class} + + // This is the main class of the + // program. Since the namespace already + // indicates what problem we are solving, + // let's call it by what it does: it + // directs the flow of the program, i.e. it + // is the toplevel driver. + // + // The member variables of this class are + // essentially as before, i.e. it has to + // have a triangulation, a DoF handler and + // associated objects such as constraints, + // variables that describe the linear + // system, etc. There are a good number of + // more member functions now, which we will + // explain below. + // + // The external interface of the class, + // however, is unchanged: it has a public + // constructor and desctructor, and it has + // a run function that initiated all + // the work. template class TopLevel { - public: - TopLevel (); - ~TopLevel (); - void run (); - - private: - // The private interface is more - // extensive than in step-17. First, we - // obviously need functions that create - // the initial mesh, set up the - // variables that describe the linear - // system on the present mesh - // (i.e. matrices and vectors), and - // then functions that actually - // assemble the system, direct what has - // to be solved in each time step, a - // function that solves the linear - // system that arises in each timestep - // (and returns the number of - // iterations it took), and finally - // output the solution vector on the - // currect mesh: - void create_coarse_grid (); - - void setup_system (); - - void assemble_system (); - - void solve_timestep (); - - unsigned int solve_linear_problem (); - - void output_results () const; - - // All, except for the first two, of - // these functions are called in each - // timestep. Since the first time step - // is a little special, we have - // separate functions that describe - // what has to happen in a timestep: - // one for the first, and one for all - // following timesteps: - void do_initial_timestep (); - - void do_timestep (); - - // Then we need a whole bunch of - // functions that do various - // things. The first one refines the - // initial grid: we start on the coarse - // grid with a pristine state, solve - // the problem, then look at it and - // refine the mesh accordingly, and - // start the same process over again, - // again with a pristine state. Thus, - // refining the initial mesh is - // somewhat simpler than refining a - // grid between two successive time - // steps, since it does not involve - // transferring data from the old to - // the new triangulation, in particular - // the history data that is stored in - // each quadrature point. - void refine_initial_grid (); - - // At the end of each time step, we - // want to move the mesh vertices - // around according to the incremental - // displacement computed in this time - // step. This is the function in which - // this is done: - void move_mesh (); - - // Next are two functions that handle - // the history variables stored in each - // quadrature point. The first one is - // called before the first timestep to - // set up a pristine state for the - // history variables. It only works on - // those quadrature points on cells - // that belong to the present - // processor: - void setup_quadrature_point_history (); - - // The second one updates the history - // variables at the end of each - // timestep: - void update_quadrature_point_history (); - - // After the member functions, here are - // the member variables. The first ones - // have all been discussed in more - // detail in previous example programs: - Triangulation triangulation; - - FESystem fe; - - DoFHandler dof_handler; - - ConstraintMatrix hanging_node_constraints; - - // One difference of this program is - // that we declare the quadrature - // formula in the class - // declaration. The reason is that in - // all the other programs, it didn't do - // much harm if we had used different - // quadrature formulas when computing - // the matrix and the righ hand side, - // for example. However, in the present - // case it does: we store information - // in the quadrature points, so we - // have to make sure all parts of the - // program agree on where they are and - // how many there are on each - // cell. Thus, let us first declare the - // quadrature formula that will be used - // throughout... - const QGauss quadrature_formula; - - // ... and then also have a vector of - // history objects, one per quadrature - // point on those cells for which we - // are responsible (i.e. we don't store - // history data for quadrature points - // on cells that are owned by other - // processors). - std::vector > quadrature_point_history; - - // The way this object is accessed is - // through a user pointer that each - // cell, face, or edge holds: it is a - // void* pointer that can be used - // by application programs to associate - // arbitrary data to cells, faces, or - // edges. What the program actually - // does with this data is within its - // own responsibility, the library just - // allocates some space for these - // pointers, and application programs - // can set and read the pointers for - // each of these objects. - - - // Further: we need the objects of - // linear systems to be solved, - // i.e. matrix, right hand side vector, - // and the solution vector. Since we - // anticipate solving big problems, we - // use the same types as in step-17, - // i.e. distributed %parallel matrices - // and vectors built on top of the - // PETSc library. Conveniently, they - // can also be used when running on - // only a single machine, in which case - // this machine happens to be the only - // one in our %parallel universe. - // - // However, as a difference to step-17, - // we do not store the solution vector - // -- which here is the incremental - // displacements computed in each time - // step -- in a distributed - // fashion. I.e., of course it must be - // a distributed vector when computing - // it, but immediately after that we - // make sure each processor has a - // complete copy. The reason is that we - // had already seen in step-17 that - // many functions needed a complete - // copy. While it is not hard to get - // it, this requires communication on - // the network, and is thus slow. In - // addition, these were repeatedly the - // same operations, which is certainly - // undesirable unless the gains of not - // always having to store the entire - // vector outweighs it. When writing - // this program, it turned out that we - // need a complete copy of the solution - // in so many places that it did not - // seem worthwhile to only get it when - // necessary. Instead, we opted to - // obtain the complete copy once and - // for all, and instead get rid of the - // distributed copy immediately. Thus, - // note that the declaration of - // inremental_displacement does not - // denote a distribute vector as would - // be indicated by the middle namespace - // MPI: - PETScWrappers::MPI::SparseMatrix system_matrix; - - PETScWrappers::MPI::Vector system_rhs; - - PETScWrappers::Vector incremental_displacement; - - // The next block of variables is then - // related to the time dependent nature - // of the problem: they denote the - // length of the time interval which we - // want to simulate, the present time - // and number of time step, and length - // of present timestep: - double present_time; - double present_timestep; - double end_time; - unsigned int timestep_no; - - // Then a few variables that have to do - // with %parallel processing: first, a - // variable denoting the MPI - // communicator we use, and then two - // numbers telling us how many - // participating processors there are, - // and where in this world we - // are. Finally, a stream object that - // makes sure only one processor is - // actually generating output to the - // console. This is all the same as in - // step-17: - MPI_Comm mpi_communicator; - - const unsigned int n_mpi_processes; - - const unsigned int this_mpi_process; - - ConditionalOStream pcout; - - // Here is a vector where each entry - // denotes the numbers of degrees of - // freedom that are stored on the - // processor with that particular - // number: - std::vector local_dofs_per_process; - - // Next, how many degrees of freedom - // the present processor stores. This - // is, of course, an abbreviation to - // local_dofs_per_process[this_mpi_process]. - unsigned int n_local_dofs; - - // In the same direction, also - // cache how many cells the - // present processor owns. Note - // that the cells that belong - // to a processor are not - // necessarily contiguously - // numbered (when iterating - // over them using - // active_cell_iterator). - unsigned int n_local_cells; - - // Finally, we have a - // static variable that denotes - // the linear relationship - // between the stress and - // strain. Since it is a - // constant object that does - // not depend on any input (at - // least not in this program), - // we make it a static variable - // and will initialize it in - // the same place where we - // define the constructor of - // this class: - static const SymmetricTensor<4,dim> stress_strain_tensor; + public: + TopLevel (); + ~TopLevel (); + void run (); + + private: + // The private interface is more + // extensive than in step-17. First, we + // obviously need functions that create + // the initial mesh, set up the + // variables that describe the linear + // system on the present mesh + // (i.e. matrices and vectors), and + // then functions that actually + // assemble the system, direct what has + // to be solved in each time step, a + // function that solves the linear + // system that arises in each timestep + // (and returns the number of + // iterations it took), and finally + // output the solution vector on the + // currect mesh: + void create_coarse_grid (); + + void setup_system (); + + void assemble_system (); + + void solve_timestep (); + + unsigned int solve_linear_problem (); + + void output_results () const; + + // All, except for the first two, of + // these functions are called in each + // timestep. Since the first time step + // is a little special, we have + // separate functions that describe + // what has to happen in a timestep: + // one for the first, and one for all + // following timesteps: + void do_initial_timestep (); + + void do_timestep (); + + // Then we need a whole bunch of + // functions that do various + // things. The first one refines the + // initial grid: we start on the coarse + // grid with a pristine state, solve + // the problem, then look at it and + // refine the mesh accordingly, and + // start the same process over again, + // again with a pristine state. Thus, + // refining the initial mesh is + // somewhat simpler than refining a + // grid between two successive time + // steps, since it does not involve + // transferring data from the old to + // the new triangulation, in particular + // the history data that is stored in + // each quadrature point. + void refine_initial_grid (); + + // At the end of each time step, we + // want to move the mesh vertices + // around according to the incremental + // displacement computed in this time + // step. This is the function in which + // this is done: + void move_mesh (); + + // Next are two functions that handle + // the history variables stored in each + // quadrature point. The first one is + // called before the first timestep to + // set up a pristine state for the + // history variables. It only works on + // those quadrature points on cells + // that belong to the present + // processor: + void setup_quadrature_point_history (); + + // The second one updates the history + // variables at the end of each + // timestep: + void update_quadrature_point_history (); + + // After the member functions, here are + // the member variables. The first ones + // have all been discussed in more + // detail in previous example programs: + Triangulation triangulation; + + FESystem fe; + + DoFHandler dof_handler; + + ConstraintMatrix hanging_node_constraints; + + // One difference of this program is + // that we declare the quadrature + // formula in the class + // declaration. The reason is that in + // all the other programs, it didn't do + // much harm if we had used different + // quadrature formulas when computing + // the matrix and the righ hand side, + // for example. However, in the present + // case it does: we store information + // in the quadrature points, so we + // have to make sure all parts of the + // program agree on where they are and + // how many there are on each + // cell. Thus, let us first declare the + // quadrature formula that will be used + // throughout... + const QGauss quadrature_formula; + + // ... and then also have a vector of + // history objects, one per quadrature + // point on those cells for which we + // are responsible (i.e. we don't store + // history data for quadrature points + // on cells that are owned by other + // processors). + std::vector > quadrature_point_history; + + // The way this object is accessed is + // through a user pointer that each + // cell, face, or edge holds: it is a + // void* pointer that can be used + // by application programs to associate + // arbitrary data to cells, faces, or + // edges. What the program actually + // does with this data is within its + // own responsibility, the library just + // allocates some space for these + // pointers, and application programs + // can set and read the pointers for + // each of these objects. + + + // Further: we need the objects of + // linear systems to be solved, + // i.e. matrix, right hand side vector, + // and the solution vector. Since we + // anticipate solving big problems, we + // use the same types as in step-17, + // i.e. distributed %parallel matrices + // and vectors built on top of the + // PETSc library. Conveniently, they + // can also be used when running on + // only a single machine, in which case + // this machine happens to be the only + // one in our %parallel universe. + // + // However, as a difference to step-17, + // we do not store the solution vector + // -- which here is the incremental + // displacements computed in each time + // step -- in a distributed + // fashion. I.e., of course it must be + // a distributed vector when computing + // it, but immediately after that we + // make sure each processor has a + // complete copy. The reason is that we + // had already seen in step-17 that + // many functions needed a complete + // copy. While it is not hard to get + // it, this requires communication on + // the network, and is thus slow. In + // addition, these were repeatedly the + // same operations, which is certainly + // undesirable unless the gains of not + // always having to store the entire + // vector outweighs it. When writing + // this program, it turned out that we + // need a complete copy of the solution + // in so many places that it did not + // seem worthwhile to only get it when + // necessary. Instead, we opted to + // obtain the complete copy once and + // for all, and instead get rid of the + // distributed copy immediately. Thus, + // note that the declaration of + // inremental_displacement does not + // denote a distribute vector as would + // be indicated by the middle namespace + // MPI: + PETScWrappers::MPI::SparseMatrix system_matrix; + + PETScWrappers::MPI::Vector system_rhs; + + PETScWrappers::Vector incremental_displacement; + + // The next block of variables is then + // related to the time dependent nature + // of the problem: they denote the + // length of the time interval which we + // want to simulate, the present time + // and number of time step, and length + // of present timestep: + double present_time; + double present_timestep; + double end_time; + unsigned int timestep_no; + + // Then a few variables that have to do + // with %parallel processing: first, a + // variable denoting the MPI + // communicator we use, and then two + // numbers telling us how many + // participating processors there are, + // and where in this world we + // are. Finally, a stream object that + // makes sure only one processor is + // actually generating output to the + // console. This is all the same as in + // step-17: + MPI_Comm mpi_communicator; + + const unsigned int n_mpi_processes; + + const unsigned int this_mpi_process; + + ConditionalOStream pcout; + + // Here is a vector where each entry + // denotes the numbers of degrees of + // freedom that are stored on the + // processor with that particular + // number: + std::vector local_dofs_per_process; + + // Next, how many degrees of freedom + // the present processor stores. This + // is, of course, an abbreviation to + // local_dofs_per_process[this_mpi_process]. + unsigned int n_local_dofs; + + // In the same direction, also + // cache how many cells the + // present processor owns. Note + // that the cells that belong + // to a processor are not + // necessarily contiguously + // numbered (when iterating + // over them using + // active_cell_iterator). + unsigned int n_local_cells; + + // Finally, we have a + // static variable that denotes + // the linear relationship + // between the stress and + // strain. Since it is a + // constant object that does + // not depend on any input (at + // least not in this program), + // we make it a static variable + // and will initialize it in + // the same place where we + // define the constructor of + // this class: + static const SymmetricTensor<4,dim> stress_strain_tensor; }; - // @sect3{The BodyForce class} - - // Before we go on to the main - // functionality of this program, we have - // to define what forces will act on the - // body whose deformation we want to - // study. These may either be body forces - // or boundary forces. Body forces are - // generally mediated by one of the four - // basic physical types of forces: gravity, - // strong and weak interaction, and - // electromagnetism. Unless one wants to - // consider subatomic objects (for which - // quasistatic deformation is irrelevant - // and an inappropriate description - // anyway), only gravity and - // electromagnetic forces need to be - // considered. Let us, for simplicity - // assume that our body has a certain mass - // density, but is either non-magnetic and - // not electrically conducting or that - // there are no significant electromagnetic - // fields around. In that case, the body - // forces are simply rho g, where - // rho is the material density and - // g is a vector in negative - // z-direction with magnitude 9.81 m/s^2. - // Both the density and g are defined - // in the function, and we take as the - // density 7700 kg/m^3, a value commonly - // assumed for steel. - // - // To be a little more general and to be - // able to do computations in 2d as well, - // we realize that the body force is always - // a function returning a dim - // dimensional vector. We assume that - // gravity acts along the negative - // direction of the last, i.e. dim-1th - // coordinate. The rest of the - // implementation of this function should - // be mostly self-explanatory given similar - // definitions in previous example - // programs. Note that the body force is - // independent of the location; to avoid - // compiler warnings about unused function - // arguments, we therefore comment out the - // name of the first argument of the - // vector_value function: + // @sect3{The BodyForce class} + + // Before we go on to the main + // functionality of this program, we have + // to define what forces will act on the + // body whose deformation we want to + // study. These may either be body forces + // or boundary forces. Body forces are + // generally mediated by one of the four + // basic physical types of forces: gravity, + // strong and weak interaction, and + // electromagnetism. Unless one wants to + // consider subatomic objects (for which + // quasistatic deformation is irrelevant + // and an inappropriate description + // anyway), only gravity and + // electromagnetic forces need to be + // considered. Let us, for simplicity + // assume that our body has a certain mass + // density, but is either non-magnetic and + // not electrically conducting or that + // there are no significant electromagnetic + // fields around. In that case, the body + // forces are simply rho g, where + // rho is the material density and + // g is a vector in negative + // z-direction with magnitude 9.81 m/s^2. + // Both the density and g are defined + // in the function, and we take as the + // density 7700 kg/m^3, a value commonly + // assumed for steel. + // + // To be a little more general and to be + // able to do computations in 2d as well, + // we realize that the body force is always + // a function returning a dim + // dimensional vector. We assume that + // gravity acts along the negative + // direction of the last, i.e. dim-1th + // coordinate. The rest of the + // implementation of this function should + // be mostly self-explanatory given similar + // definitions in previous example + // programs. Note that the body force is + // independent of the location; to avoid + // compiler warnings about unused function + // arguments, we therefore comment out the + // name of the first argument of the + // vector_value function: template class BodyForce : public Function { - public: - BodyForce (); - - virtual - void - vector_value (const Point &p, - Vector &values) const; - - virtual - void - vector_value_list (const std::vector > &points, - std::vector > &value_list) const; + public: + BodyForce (); + + virtual + void + vector_value (const Point &p, + Vector &values) const; + + virtual + void + vector_value_list (const std::vector > &points, + std::vector > &value_list) const; }; template BodyForce::BodyForce () - : - Function (dim) + : + Function (dim) {} @@ -846,10 +854,10 @@ namespace Step18 inline void BodyForce::vector_value (const Point &/*p*/, - Vector &values) const + Vector &values) const { Assert (values.size() == dim, - ExcDimensionMismatch (values.size(), dim)); + ExcDimensionMismatch (values.size(), dim)); const double g = 9.81; const double rho = 7700; @@ -863,103 +871,103 @@ namespace Step18 template void BodyForce::vector_value_list (const std::vector > &points, - std::vector > &value_list) const + std::vector > &value_list) const { const unsigned int n_points = points.size(); Assert (value_list.size() == n_points, - ExcDimensionMismatch (value_list.size(), n_points)); + ExcDimensionMismatch (value_list.size(), n_points)); for (unsigned int p=0; p::vector_value (points[p], - value_list[p]); + value_list[p]); } - // @sect3{The IncrementalBoundaryValue class} - - // In addition to body forces, movement can - // be induced by boundary forces and forced - // boundary displacement. The latter case - // is equivalent to forces being chosen in - // such a way that they induce certain - // displacement. - // - // For quasistatic displacement, typical - // boundary forces would be pressure on a - // body, or tangential friction against - // another body. We chose a somewhat - // simpler case here: we prescribe a - // certain movement of (parts of) the - // boundary, or at least of certain - // components of the displacement - // vector. We describe this by another - // vector-valued function that, for a given - // point on the boundary, returns the - // prescribed displacement. - // - // Since we have a time-dependent problem, - // the displacement increment of the - // boundary equals the displacement - // accumulated during the length of the - // timestep. The class therefore has to - // know both the present time and the - // length of the present time step, and can - // then approximate the incremental - // displacement as the present velocity - // times the present timestep. - // - // For the purposes of this - // program, we choose a simple form - // of boundary displacement: we - // displace the top boundary with - // constant velocity downwards. The - // rest of the boundary is either - // going to be fixed (and is then - // described using an object of - // type ZeroFunction) or free - // (Neumann-type, in which case - // nothing special has to be done). - // The implementation of the - // class describing the constant - // downward motion should then be - // obvious using the knowledge we - // gained through all the previous - // example programs: + // @sect3{The IncrementalBoundaryValue class} + + // In addition to body forces, movement can + // be induced by boundary forces and forced + // boundary displacement. The latter case + // is equivalent to forces being chosen in + // such a way that they induce certain + // displacement. + // + // For quasistatic displacement, typical + // boundary forces would be pressure on a + // body, or tangential friction against + // another body. We chose a somewhat + // simpler case here: we prescribe a + // certain movement of (parts of) the + // boundary, or at least of certain + // components of the displacement + // vector. We describe this by another + // vector-valued function that, for a given + // point on the boundary, returns the + // prescribed displacement. + // + // Since we have a time-dependent problem, + // the displacement increment of the + // boundary equals the displacement + // accumulated during the length of the + // timestep. The class therefore has to + // know both the present time and the + // length of the present time step, and can + // then approximate the incremental + // displacement as the present velocity + // times the present timestep. + // + // For the purposes of this + // program, we choose a simple form + // of boundary displacement: we + // displace the top boundary with + // constant velocity downwards. The + // rest of the boundary is either + // going to be fixed (and is then + // described using an object of + // type ZeroFunction) or free + // (Neumann-type, in which case + // nothing special has to be done). + // The implementation of the + // class describing the constant + // downward motion should then be + // obvious using the knowledge we + // gained through all the previous + // example programs: template class IncrementalBoundaryValues : public Function { - public: - IncrementalBoundaryValues (const double present_time, - const double present_timestep); - - virtual - void - vector_value (const Point &p, - Vector &values) const; - - virtual - void - vector_value_list (const std::vector > &points, - std::vector > &value_list) const; - - private: - const double velocity; - const double present_time; - const double present_timestep; + public: + IncrementalBoundaryValues (const double present_time, + const double present_timestep); + + virtual + void + vector_value (const Point &p, + Vector &values) const; + + virtual + void + vector_value_list (const std::vector > &points, + std::vector > &value_list) const; + + private: + const double velocity; + const double present_time; + const double present_timestep; }; template IncrementalBoundaryValues:: IncrementalBoundaryValues (const double present_time, - const double present_timestep) - : - Function (dim), - velocity (.1), - present_time (present_time), - present_timestep (present_timestep) + const double present_timestep) + : + Function (dim), + velocity (.1), + present_time (present_time), + present_timestep (present_timestep) {} @@ -967,10 +975,10 @@ namespace Step18 void IncrementalBoundaryValues:: vector_value (const Point &/*p*/, - Vector &values) const + Vector &values) const { Assert (values.size() == dim, - ExcDimensionMismatch (values.size(), dim)); + ExcDimensionMismatch (values.size(), dim)); values = 0; values(2) = -present_timestep * velocity; @@ -982,58 +990,58 @@ namespace Step18 void IncrementalBoundaryValues:: vector_value_list (const std::vector > &points, - std::vector > &value_list) const + std::vector > &value_list) const { const unsigned int n_points = points.size(); Assert (value_list.size() == n_points, - ExcDimensionMismatch (value_list.size(), n_points)); + ExcDimensionMismatch (value_list.size(), n_points)); for (unsigned int p=0; p::vector_value (points[p], - value_list[p]); + value_list[p]); } - // @sect3{Implementation of the TopLevel class} + // @sect3{Implementation of the TopLevel class} - // Now for the implementation of the main - // class. First, we initialize the - // stress-strain tensor, which we - // have declared as a static const - // variable. We chose Lame - // constants that are appropriate - // for steel: + // Now for the implementation of the main + // class. First, we initialize the + // stress-strain tensor, which we + // have declared as a static const + // variable. We chose Lame + // constants that are appropriate + // for steel: template const SymmetricTensor<4,dim> TopLevel::stress_strain_tensor - = get_stress_strain_tensor (/*lambda = */ 9.695e10, - /*mu = */ 7.617e10); + = get_stress_strain_tensor (/*lambda = */ 9.695e10, + /*mu = */ 7.617e10); - // @sect4{The public interface} + // @sect4{The public interface} - // The next step is the definition of - // constructors and descructors. There are - // no surprises here: we choose linear and - // continuous finite elements for each of - // the dim vector components of the - // solution, and a Gaussian quadrature - // formula with 2 points in each coordinate - // direction. The destructor should be - // obvious: + // The next step is the definition of + // constructors and descructors. There are + // no surprises here: we choose linear and + // continuous finite elements for each of + // the dim vector components of the + // solution, and a Gaussian quadrature + // formula with 2 points in each coordinate + // direction. The destructor should be + // obvious: template TopLevel::TopLevel () - : - fe (FE_Q(1), dim), - dof_handler (triangulation), - quadrature_formula (2), - mpi_communicator (MPI_COMM_WORLD), - n_mpi_processes (Utilities::MPI::n_mpi_processes(mpi_communicator)), - this_mpi_process (Utilities::MPI::this_mpi_process(mpi_communicator)), - pcout (std::cout, this_mpi_process == 0) + : + fe (FE_Q(1), dim), + dof_handler (triangulation), + quadrature_formula (2), + mpi_communicator (MPI_COMM_WORLD), + n_mpi_processes (Utilities::MPI::n_mpi_processes(mpi_communicator)), + this_mpi_process (Utilities::MPI::this_mpi_process(mpi_communicator)), + pcout (std::cout, this_mpi_process == 0) {} @@ -1046,17 +1054,17 @@ namespace Step18 - // The last of the public functions is the - // one that directs all the work, - // run(). It initializes the variables - // that describe where in time we presently - // are, then runs the first time step, then - // loops over all the other time - // steps. Note that for simplicity we use a - // fixed time step, whereas a more - // sophisticated program would of course - // have to choose it in some more - // reasonable way adaptively: + // The last of the public functions is the + // one that directs all the work, + // run(). It initializes the variables + // that describe where in time we presently + // are, then runs the first time step, then + // loops over all the other time + // steps. Note that for simplicity we use a + // fixed time step, whereas a more + // sophisticated program would of course + // have to choose it in some more + // reasonable way adaptively: template void TopLevel::run () { @@ -1072,169 +1080,169 @@ namespace Step18 } - // @sect4{TopLevel::create_coarse_grid} - - // The next function in the order - // in which they were declared - // above is the one that creates - // the coarse grid from which we - // start. For this example program, - // we want to compute the - // deformation of a cylinder under - // axial compression. The first - // step therefore is to generate a - // mesh for a cylinder of length 3 - // and with inner and outer radii - // of 0.8 and 1, - // respectively. Fortunately, there - // is a library function for such a - // mesh. - // - // In a second step, we have to associated - // boundary conditions with the upper and - // lower faces of the cylinder. We choose a - // boundary indicator of 0 for the boundary - // faces that are characterized by their - // midpoints having z-coordinates of either - // 0 (bottom face), an indicator of 1 for - // z=3 (top face); finally, we use boundary - // indicator 2 for all faces on the inside - // of the cylinder shell, and 3 for the - // outside. + // @sect4{TopLevel::create_coarse_grid} + + // The next function in the order + // in which they were declared + // above is the one that creates + // the coarse grid from which we + // start. For this example program, + // we want to compute the + // deformation of a cylinder under + // axial compression. The first + // step therefore is to generate a + // mesh for a cylinder of length 3 + // and with inner and outer radii + // of 0.8 and 1, + // respectively. Fortunately, there + // is a library function for such a + // mesh. + // + // In a second step, we have to associated + // boundary conditions with the upper and + // lower faces of the cylinder. We choose a + // boundary indicator of 0 for the boundary + // faces that are characterized by their + // midpoints having z-coordinates of either + // 0 (bottom face), an indicator of 1 for + // z=3 (top face); finally, we use boundary + // indicator 2 for all faces on the inside + // of the cylinder shell, and 3 for the + // outside. template void TopLevel::create_coarse_grid () { const double inner_radius = 0.8, - outer_radius = 1; + outer_radius = 1; GridGenerator::cylinder_shell (triangulation, - 3, inner_radius, outer_radius); + 3, inner_radius, outer_radius); for (typename Triangulation::active_cell_iterator - cell=triangulation.begin_active(); - cell!=triangulation.end(); ++cell) + cell=triangulation.begin_active(); + cell!=triangulation.end(); ++cell) for (unsigned int f=0; f::faces_per_cell; ++f) - if (cell->face(f)->at_boundary()) - { - const Point face_center = cell->face(f)->center(); - - if (face_center[2] == 0) - cell->face(f)->set_boundary_indicator (0); - else if (face_center[2] == 3) - cell->face(f)->set_boundary_indicator (1); - else if (std::sqrt(face_center[0]*face_center[0] + - face_center[1]*face_center[1]) - < - (inner_radius + outer_radius) / 2) - cell->face(f)->set_boundary_indicator (2); - else - cell->face(f)->set_boundary_indicator (3); - } - - // In order to make sure that new - // vertices are placed correctly on mesh - // refinement, we have to associate - // objects describing those parts of the - // boundary that do not consist of - // straight parts. Corresponding to the - // cylinder shell generator function used - // above, there are classes that can be - // used to describe the geometry of - // cylinders. We need to use different - // objects for the inner and outer parts - // of the cylinder, with different radii; - // the second argument to the constructor - // indicates the axis around which the - // cylinder revolves -- in this case the - // z-axis. Note that the boundary objects - // need to live as long as the - // triangulation does; we can achieve - // this by making the objects static, - // which means that they live as long as - // the program runs: + if (cell->face(f)->at_boundary()) + { + const Point face_center = cell->face(f)->center(); + + if (face_center[2] == 0) + cell->face(f)->set_boundary_indicator (0); + else if (face_center[2] == 3) + cell->face(f)->set_boundary_indicator (1); + else if (std::sqrt(face_center[0]*face_center[0] + + face_center[1]*face_center[1]) + < + (inner_radius + outer_radius) / 2) + cell->face(f)->set_boundary_indicator (2); + else + cell->face(f)->set_boundary_indicator (3); + } + + // In order to make sure that new + // vertices are placed correctly on mesh + // refinement, we have to associate + // objects describing those parts of the + // boundary that do not consist of + // straight parts. Corresponding to the + // cylinder shell generator function used + // above, there are classes that can be + // used to describe the geometry of + // cylinders. We need to use different + // objects for the inner and outer parts + // of the cylinder, with different radii; + // the second argument to the constructor + // indicates the axis around which the + // cylinder revolves -- in this case the + // z-axis. Note that the boundary objects + // need to live as long as the + // triangulation does; we can achieve + // this by making the objects static, + // which means that they live as long as + // the program runs: static const CylinderBoundary inner_cylinder (inner_radius, 2); static const CylinderBoundary outer_cylinder (outer_radius, 2); - // We then attach these two objects to - // the triangulation, and make them - // correspond to boundary indicators 2 - // and 3: + // We then attach these two objects to + // the triangulation, and make them + // correspond to boundary indicators 2 + // and 3: triangulation.set_boundary (2, inner_cylinder); triangulation.set_boundary (3, outer_cylinder); - // There's one more thing we have to take - // care of (we should have done so above - // already, but for didactic reasons it - // was more appropriate to handle it - // after discussing boundary - // objects). %Boundary indicators in - // deal.II, for mostly historic reasons, - // serve a dual purpose: they describe - // the type of a boundary for other - // places in a program where different - // boundary conditions are implemented; - // and they describe which boundary - // object (as the ones associated above) - // should be queried when new boundary - // points need to be placed upon mesh - // refinement. In the prefix to this - // function, we have discussed the - // boundary condition issue, and the - // boundary geometry issue was mentioned - // just above. But there is a case where - // we have to be careful with geometry: - // what happens if a cell is refined that - // has two faces with different boundary - // indicators? For example one at the - // edges of the cylinder? In that case, - // the library wouldn't know where to put - // new points in the middle of edges (one - // of the twelve lines of a - // hexahedron). In fact, the library - // doesn't even care about the boundary - // indicator of adjacent faces when - // refining edges: it considers the - // boundary indicators associated with - // the edges themselves. So what do we - // want to happen with the edges of the - // cylinder shell: they sit on both faces - // with boundary indicators 2 or 3 (inner - // or outer shell) and 0 or 1 (for which - // no boundary objects have been - // specified, and for which the library - // therefore assumes straight - // lines). Obviously, we want these lines - // to follow the curved shells, so we - // have to assign all edges along faces - // with boundary indicators 2 or 3 these - // same boundary indicators to make sure - // they are refined using the appropriate - // geometry objects. This is easily done: + // There's one more thing we have to take + // care of (we should have done so above + // already, but for didactic reasons it + // was more appropriate to handle it + // after discussing boundary + // objects). %Boundary indicators in + // deal.II, for mostly historic reasons, + // serve a dual purpose: they describe + // the type of a boundary for other + // places in a program where different + // boundary conditions are implemented; + // and they describe which boundary + // object (as the ones associated above) + // should be queried when new boundary + // points need to be placed upon mesh + // refinement. In the prefix to this + // function, we have discussed the + // boundary condition issue, and the + // boundary geometry issue was mentioned + // just above. But there is a case where + // we have to be careful with geometry: + // what happens if a cell is refined that + // has two faces with different boundary + // indicators? For example one at the + // edges of the cylinder? In that case, + // the library wouldn't know where to put + // new points in the middle of edges (one + // of the twelve lines of a + // hexahedron). In fact, the library + // doesn't even care about the boundary + // indicator of adjacent faces when + // refining edges: it considers the + // boundary indicators associated with + // the edges themselves. So what do we + // want to happen with the edges of the + // cylinder shell: they sit on both faces + // with boundary indicators 2 or 3 (inner + // or outer shell) and 0 or 1 (for which + // no boundary objects have been + // specified, and for which the library + // therefore assumes straight + // lines). Obviously, we want these lines + // to follow the curved shells, so we + // have to assign all edges along faces + // with boundary indicators 2 or 3 these + // same boundary indicators to make sure + // they are refined using the appropriate + // geometry objects. This is easily done: for (typename Triangulation::active_face_iterator - face=triangulation.begin_active_face(); - face!=triangulation.end_face(); ++face) + face=triangulation.begin_active_face(); + face!=triangulation.end_face(); ++face) if (face->at_boundary()) - if ((face->boundary_indicator() == 2) - || - (face->boundary_indicator() == 3)) - for (unsigned int edge = 0; edge::lines_per_face; - ++edge) - face->line(edge) - ->set_boundary_indicator (face->boundary_indicator()); - - // Once all this is done, we can refine - // the mesh once globally: + if ((face->boundary_indicator() == 2) + || + (face->boundary_indicator() == 3)) + for (unsigned int edge = 0; edge::lines_per_face; + ++edge) + face->line(edge) + ->set_boundary_indicator (face->boundary_indicator()); + + // Once all this is done, we can refine + // the mesh once globally: triangulation.refine_global (1); - // As the final step, we need to - // set up a clean state of the - // data that we store in the - // quadrature points on all cells - // that are treated on the - // present processor. To do so, - // we also have to know which - // processors are ours in the - // first place. This is done in - // the following two function - // calls: + // As the final step, we need to + // set up a clean state of the + // data that we store in the + // quadrature points on all cells + // that are treated on the + // present processor. To do so, + // we also have to know which + // processors are ours in the + // first place. This is done in + // the following two function + // calls: GridTools::partition_triangulation (n_mpi_processes, triangulation); setup_quadrature_point_history (); } @@ -1242,229 +1250,229 @@ namespace Step18 - // @sect4{TopLevel::setup_system} - - // The next function is the one - // that sets up the data structures - // for a given mesh. This is done - // in most the same way as in - // step-17: distribute the degrees - // of freedom, then sort these - // degrees of freedom in such a way - // that each processor gets a - // contiguous chunk of them. Note - // that subdivions into chunks for - // each processor is handled in the - // functions that create or refine - // grids, unlike in the previous - // example program (the point where - // this happens is mostly a matter - // of taste; here, we chose to do - // it when grids are created since - // in the do_initial_timestep - // and do_timestep functions we - // want to output the number of - // cells on each processor at a - // point where we haven't called - // the present function yet). + // @sect4{TopLevel::setup_system} + + // The next function is the one + // that sets up the data structures + // for a given mesh. This is done + // in most the same way as in + // step-17: distribute the degrees + // of freedom, then sort these + // degrees of freedom in such a way + // that each processor gets a + // contiguous chunk of them. Note + // that subdivions into chunks for + // each processor is handled in the + // functions that create or refine + // grids, unlike in the previous + // example program (the point where + // this happens is mostly a matter + // of taste; here, we chose to do + // it when grids are created since + // in the do_initial_timestep + // and do_timestep functions we + // want to output the number of + // cells on each processor at a + // point where we haven't called + // the present function yet). template void TopLevel::setup_system () { dof_handler.distribute_dofs (fe); DoFRenumbering::subdomain_wise (dof_handler); - // The next thing is to store some - // information for later use on how many - // cells or degrees of freedom the - // present processor, or any of the - // processors has to work on. First the - // cells local to this processor... + // The next thing is to store some + // information for later use on how many + // cells or degrees of freedom the + // present processor, or any of the + // processors has to work on. First the + // cells local to this processor... n_local_cells = GridTools::count_cells_with_subdomain_association (triangulation, - this_mpi_process); + this_mpi_process); - // ...and then a list of numbers of how - // many degrees of freedom each processor - // has to handle: + // ...and then a list of numbers of how + // many degrees of freedom each processor + // has to handle: local_dofs_per_process.resize (n_mpi_processes); for (unsigned int i=0; iCompressedSparsityPattern class - // here that was already introduced in - // step-11, rather than the - // SparsityPattern class that we have - // used in all other cases. The reason - // for this is that for the latter class - // to work we have to give an initial - // upper bound for the number of entries - // in each row, a task that is - // traditionally done by - // DoFHandler::max_couplings_between_dofs(). However, - // this function suffers from a serious - // problem: it has to compute an upper - // bound to the number of nonzero entries - // in each row, and this is a rather - // complicated task, in particular in - // 3d. In effect, while it is quite - // accurate in 2d, it often comes up with - // much too large a number in 3d, and in - // that case the SparsityPattern - // allocates much too much memory at - // first, often several 100 MBs. This is - // later corrected when - // DoFTools::make_sparsity_pattern is - // called and we realize that we don't - // need all that much memory, but at time - // it is already too late: for large - // problems, the temporary allocation of - // too much memory can lead to - // out-of-memory situations. - // - // In order to avoid this, we resort to - // the CompressedSparsityPattern - // class that is slower but does not - // require any up-front estimate on the - // number of nonzero entries per row. It - // therefore only ever allocates as much - // memory as it needs at any given time, - // and we can build it even for large 3d - // problems. - // - // It is also worth noting that the - // sparsity pattern we construct is - // global, i.e. comprises all degrees of - // freedom whether they will be owned by - // the processor we are on or another one - // (in case this program is run in - // %parallel via MPI). This of course is - // not optimal -- it limits the size of - // the problems we can solve, since - // storing the entire sparsity pattern - // (even if only for a short time) on - // each processor does not scale - // well. However, there are several more - // places in the program in which we do - // this, for example we always keep the - // global triangulation and DoF handler - // objects around, even if we only work - // on part of them. At present, deal.II - // does not have the necessary facilities - // to completely distribute these objects - // (a task that, indeed, is very hard to - // achieve with adaptive meshes, since - // well-balanced subdivisions of a domain - // tend to become unbalanced as the mesh - // is adaptively refined). - // - // With this data structure, we can then - // go to the PETSc sparse matrix and tell - // it to pre-allocate all the entries we - // will later want to write to: + // Note that we have used the + // CompressedSparsityPattern class + // here that was already introduced in + // step-11, rather than the + // SparsityPattern class that we have + // used in all other cases. The reason + // for this is that for the latter class + // to work we have to give an initial + // upper bound for the number of entries + // in each row, a task that is + // traditionally done by + // DoFHandler::max_couplings_between_dofs(). However, + // this function suffers from a serious + // problem: it has to compute an upper + // bound to the number of nonzero entries + // in each row, and this is a rather + // complicated task, in particular in + // 3d. In effect, while it is quite + // accurate in 2d, it often comes up with + // much too large a number in 3d, and in + // that case the SparsityPattern + // allocates much too much memory at + // first, often several 100 MBs. This is + // later corrected when + // DoFTools::make_sparsity_pattern is + // called and we realize that we don't + // need all that much memory, but at time + // it is already too late: for large + // problems, the temporary allocation of + // too much memory can lead to + // out-of-memory situations. + // + // In order to avoid this, we resort to + // the CompressedSparsityPattern + // class that is slower but does not + // require any up-front estimate on the + // number of nonzero entries per row. It + // therefore only ever allocates as much + // memory as it needs at any given time, + // and we can build it even for large 3d + // problems. + // + // It is also worth noting that the + // sparsity pattern we construct is + // global, i.e. comprises all degrees of + // freedom whether they will be owned by + // the processor we are on or another one + // (in case this program is run in + // %parallel via MPI). This of course is + // not optimal -- it limits the size of + // the problems we can solve, since + // storing the entire sparsity pattern + // (even if only for a short time) on + // each processor does not scale + // well. However, there are several more + // places in the program in which we do + // this, for example we always keep the + // global triangulation and DoF handler + // objects around, even if we only work + // on part of them. At present, deal.II + // does not have the necessary facilities + // to completely distribute these objects + // (a task that, indeed, is very hard to + // achieve with adaptive meshes, since + // well-balanced subdivisions of a domain + // tend to become unbalanced as the mesh + // is adaptively refined). + // + // With this data structure, we can then + // go to the PETSc sparse matrix and tell + // it to pre-allocate all the entries we + // will later want to write to: system_matrix.reinit (mpi_communicator, - sparsity_pattern, - local_dofs_per_process, - local_dofs_per_process, - this_mpi_process); - // After this point, no further explicit - // knowledge of the sparsity pattern is - // required any more and we can let the - // sparsity_pattern variable go out - // of scope without any problem. - - // The last task in this function - // is then only to reset the - // right hand side vector as well - // as the solution vector to its - // correct size; remember that - // the solution vector is a local - // one, unlike the right hand - // side that is a distributed - // %parallel one and therefore - // needs to know the MPI - // communicator over which it is - // supposed to transmit messages: + sparsity_pattern, + local_dofs_per_process, + local_dofs_per_process, + this_mpi_process); + // After this point, no further explicit + // knowledge of the sparsity pattern is + // required any more and we can let the + // sparsity_pattern variable go out + // of scope without any problem. + + // The last task in this function + // is then only to reset the + // right hand side vector as well + // as the solution vector to its + // correct size; remember that + // the solution vector is a local + // one, unlike the right hand + // side that is a distributed + // %parallel one and therefore + // needs to know the MPI + // communicator over which it is + // supposed to transmit messages: system_rhs.reinit (mpi_communicator, dof_handler.n_dofs(), n_local_dofs); incremental_displacement.reinit (dof_handler.n_dofs()); } - // @sect4{TopLevel::assemble_system} - - // Again, assembling the system - // matrix and right hand side - // follows the same structure as in - // many example programs before. In - // particular, it is mostly - // equivalent to step-17, except - // for the different right hand - // side that now only has to take - // into account internal - // stresses. In addition, - // assembling the matrix is made - // significantly more transparent - // by using the SymmetricTensor - // class: note the elegance of - // forming the scalar products of - // symmetric tensors of rank 2 and - // 4. The implementation is also - // more general since it is - // independent of the fact that we - // may or may not be using an - // isotropic elasticity tensor. - // - // The first part of the assembly routine - // is as always: + // @sect4{TopLevel::assemble_system} + + // Again, assembling the system + // matrix and right hand side + // follows the same structure as in + // many example programs before. In + // particular, it is mostly + // equivalent to step-17, except + // for the different right hand + // side that now only has to take + // into account internal + // stresses. In addition, + // assembling the matrix is made + // significantly more transparent + // by using the SymmetricTensor + // class: note the elegance of + // forming the scalar products of + // symmetric tensors of rank 2 and + // 4. The implementation is also + // more general since it is + // independent of the fact that we + // may or may not be using an + // isotropic elasticity tensor. + // + // The first part of the assembly routine + // is as always: template void TopLevel::assemble_system () { @@ -1472,8 +1480,8 @@ namespace Step18 system_matrix = 0; FEValues fe_values (fe, quadrature_formula, - update_values | update_gradients | - update_quadrature_points | update_JxW_values); + update_values | update_gradients | + update_quadrature_points | update_JxW_values); const unsigned int dofs_per_cell = fe.dofs_per_cell; const unsigned int n_q_points = quadrature_formula.size(); @@ -1485,246 +1493,246 @@ namespace Step18 BodyForce body_force; std::vector > body_force_values (n_q_points, - Vector(dim)); + Vector(dim)); - // As in step-17, we only need to loop - // over all cells that belong to the - // present processor: + // As in step-17, we only need to loop + // over all cells that belong to the + // present processor: typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) if (cell->subdomain_id() == this_mpi_process) - { - cell_matrix = 0; - cell_rhs = 0; - - fe_values.reinit (cell); - - // Then loop over all indices i,j - // and quadrature points and - // assemble the system matrix - // contributions from this cell. - // Note how we extract the - // symmetric gradients (strains) of - // the shape functions at a given - // quadrature point from the - // FEValues object, and the - // elegance with which we form the - // triple contraction eps_phi_i : - // C : eps_phi_j; the latter - // needs to be compared to the - // clumsy computations needed in - // step-17, both in the - // introduction as well as in the - // respective place in the program: - for (unsigned int i=0; i - eps_phi_i = get_strain (fe_values, i, q_point), - eps_phi_j = get_strain (fe_values, j, q_point); - - cell_matrix(i,j) - += (eps_phi_i * stress_strain_tensor * eps_phi_j - * - fe_values.JxW (q_point)); - } - - - // Then also assemble the local - // right hand side - // contributions. For this, we need - // to access the prior stress value - // in this quadrature point. To get - // it, we use the user pointer of - // this cell that points into the - // global array to the quadrature - // point data corresponding to the - // first quadrature point of the - // present cell, and then add an - // offset corresponding to the - // index of the quadrature point we - // presently consider: - const PointHistory *local_quadrature_points_data - = reinterpret_cast*>(cell->user_pointer()); - // In addition, we need the values - // of the external body forces at - // the quadrature points on this - // cell: - body_force.vector_value_list (fe_values.get_quadrature_points(), - body_force_values); - // Then we can loop over all - // degrees of freedom on this cell - // and compute local contributions - // to the right hand side: - for (unsigned int i=0; i &old_stress - = local_quadrature_points_data[q_point].old_stress; - - cell_rhs(i) += (body_force_values[q_point](component_i) * - fe_values.shape_value (i,q_point) - - - old_stress * - get_strain (fe_values,i,q_point)) - * - fe_values.JxW (q_point); - } - } - - // Now that we have the local - // contributions to the linear - // system, we need to transfer it - // into the global objects. This is - // done exactly as in step-17: - cell->get_dof_indices (local_dof_indices); - - hanging_node_constraints - .distribute_local_to_global (cell_matrix, - local_dof_indices, - system_matrix); - - hanging_node_constraints - .distribute_local_to_global (cell_rhs, - local_dof_indices, - system_rhs); - } - - // The last step is to again fix - // up boundary values, just as we - // already did in previous - // programs. A slight - // complication is that the - // apply_boundary_values - // function wants to have a - // solution vector compatible - // with the matrix and right hand - // side (i.e. here a distributed - // %parallel vector, rather than - // the sequential vector we use - // in this program) in order to - // preset the entries of the - // solution vector with the - // correct boundary values. We - // provide such a compatible - // vector in the form of a - // temporary vector which we then - // copy into the sequential one. - - // We make up for this - // complication by showing how - // boundary values can be used - // flexibly: following the way we - // create the triangulation, - // there are three distinct - // boundary indicators used to - // describe the domain, - // corresponding to the bottom - // and top faces, as well as the - // inner/outer surfaces. We would - // like to impose boundary - // conditions of the following - // type: The inner and outer - // cylinder surfaces are free of - // external forces, a fact that - // corresponds to natural - // (Neumann-type) boundary - // conditions for which we don't - // have to do anything. At the - // bottom, we want no movement at - // all, corresponding to the - // cylinder being clamped or - // cemented in at this part of - // the boundary. At the top, - // however, we want a prescribed - // vertical downward motion - // compressing the cylinder; in - // addition, we only want to - // restrict the vertical - // movement, but not the - // horizontal ones -- one can - // think of this situation as a - // well-greased plate sitting on - // top of the cylinder pushing it - // downwards: the atoms of the - // cylinder are forced to move - // downward, but they are free to - // slide horizontally along the - // plate. - - // The way to describe this is as - // follows: for boundary - // indicator zero (bottom face) - // we use a dim-dimensional zero - // function representing no - // motion in any coordinate - // direction. For the boundary - // with indicator 1 (top - // surface), we use the - // IncrementalBoundaryValues - // class, but we specify an - // additional argument to the - // VectorTools::interpolate_boundary_values - // function denoting which vector - // components it should apply to; - // this is a vector of bools for - // each vector component and - // because we only want to - // restrict vertical motion, it - // has only its last component - // set: + { + cell_matrix = 0; + cell_rhs = 0; + + fe_values.reinit (cell); + + // Then loop over all indices i,j + // and quadrature points and + // assemble the system matrix + // contributions from this cell. + // Note how we extract the + // symmetric gradients (strains) of + // the shape functions at a given + // quadrature point from the + // FEValues object, and the + // elegance with which we form the + // triple contraction eps_phi_i : + // C : eps_phi_j; the latter + // needs to be compared to the + // clumsy computations needed in + // step-17, both in the + // introduction as well as in the + // respective place in the program: + for (unsigned int i=0; i + eps_phi_i = get_strain (fe_values, i, q_point), + eps_phi_j = get_strain (fe_values, j, q_point); + + cell_matrix(i,j) + += (eps_phi_i * stress_strain_tensor * eps_phi_j + * + fe_values.JxW (q_point)); + } + + + // Then also assemble the local + // right hand side + // contributions. For this, we need + // to access the prior stress value + // in this quadrature point. To get + // it, we use the user pointer of + // this cell that points into the + // global array to the quadrature + // point data corresponding to the + // first quadrature point of the + // present cell, and then add an + // offset corresponding to the + // index of the quadrature point we + // presently consider: + const PointHistory *local_quadrature_points_data + = reinterpret_cast*>(cell->user_pointer()); + // In addition, we need the values + // of the external body forces at + // the quadrature points on this + // cell: + body_force.vector_value_list (fe_values.get_quadrature_points(), + body_force_values); + // Then we can loop over all + // degrees of freedom on this cell + // and compute local contributions + // to the right hand side: + for (unsigned int i=0; i &old_stress + = local_quadrature_points_data[q_point].old_stress; + + cell_rhs(i) += (body_force_values[q_point](component_i) * + fe_values.shape_value (i,q_point) + - + old_stress * + get_strain (fe_values,i,q_point)) + * + fe_values.JxW (q_point); + } + } + + // Now that we have the local + // contributions to the linear + // system, we need to transfer it + // into the global objects. This is + // done exactly as in step-17: + cell->get_dof_indices (local_dof_indices); + + hanging_node_constraints + .distribute_local_to_global (cell_matrix, + local_dof_indices, + system_matrix); + + hanging_node_constraints + .distribute_local_to_global (cell_rhs, + local_dof_indices, + system_rhs); + } + + // The last step is to again fix + // up boundary values, just as we + // already did in previous + // programs. A slight + // complication is that the + // apply_boundary_values + // function wants to have a + // solution vector compatible + // with the matrix and right hand + // side (i.e. here a distributed + // %parallel vector, rather than + // the sequential vector we use + // in this program) in order to + // preset the entries of the + // solution vector with the + // correct boundary values. We + // provide such a compatible + // vector in the form of a + // temporary vector which we then + // copy into the sequential one. + + // We make up for this + // complication by showing how + // boundary values can be used + // flexibly: following the way we + // create the triangulation, + // there are three distinct + // boundary indicators used to + // describe the domain, + // corresponding to the bottom + // and top faces, as well as the + // inner/outer surfaces. We would + // like to impose boundary + // conditions of the following + // type: The inner and outer + // cylinder surfaces are free of + // external forces, a fact that + // corresponds to natural + // (Neumann-type) boundary + // conditions for which we don't + // have to do anything. At the + // bottom, we want no movement at + // all, corresponding to the + // cylinder being clamped or + // cemented in at this part of + // the boundary. At the top, + // however, we want a prescribed + // vertical downward motion + // compressing the cylinder; in + // addition, we only want to + // restrict the vertical + // movement, but not the + // horizontal ones -- one can + // think of this situation as a + // well-greased plate sitting on + // top of the cylinder pushing it + // downwards: the atoms of the + // cylinder are forced to move + // downward, but they are free to + // slide horizontally along the + // plate. + + // The way to describe this is as + // follows: for boundary + // indicator zero (bottom face) + // we use a dim-dimensional zero + // function representing no + // motion in any coordinate + // direction. For the boundary + // with indicator 1 (top + // surface), we use the + // IncrementalBoundaryValues + // class, but we specify an + // additional argument to the + // VectorTools::interpolate_boundary_values + // function denoting which vector + // components it should apply to; + // this is a vector of bools for + // each vector component and + // because we only want to + // restrict vertical motion, it + // has only its last component + // set: FEValuesExtractors::Scalar z_component (dim-1); std::map boundary_values; VectorTools:: - interpolate_boundary_values (dof_handler, - 0, - ZeroFunction (dim), - boundary_values); + interpolate_boundary_values (dof_handler, + 0, + ZeroFunction (dim), + boundary_values); VectorTools:: - interpolate_boundary_values (dof_handler, - 1, - IncrementalBoundaryValues(present_time, - present_timestep), - boundary_values, - fe.component_mask(z_component)); + interpolate_boundary_values (dof_handler, + 1, + IncrementalBoundaryValues(present_time, + present_timestep), + boundary_values, + fe.component_mask(z_component)); PETScWrappers::MPI::Vector tmp (mpi_communicator, dof_handler.n_dofs(), - n_local_dofs); + n_local_dofs); MatrixTools::apply_boundary_values (boundary_values, - system_matrix, tmp, - system_rhs, false); + system_matrix, tmp, + system_rhs, false); incremental_displacement = tmp; } - // @sect4{TopLevel::solve_timestep} + // @sect4{TopLevel::solve_timestep} - // The next function is the one that - // controls what all has to happen within a - // timestep. The order of things should be - // relatively self-explanatory from the - // function names: + // The next function is the one that + // controls what all has to happen within a + // timestep. The order of things should be + // relatively self-explanatory from the + // function names: template void TopLevel::solve_timestep () { pcout << " Assembling system..." << std::flush; assemble_system (); pcout << " norm of rhs is " << system_rhs.l2_norm() - << std::endl; + << std::endl; const unsigned int n_iterations = solve_linear_problem (); pcout << " Solver converged in " << n_iterations - << " iterations." << std::endl; + << " iterations." << std::endl; pcout << " Updating quadrature point data..." << std::flush; update_quadrature_point_history (); @@ -1733,52 +1741,52 @@ namespace Step18 - // @sect4{TopLevel::solve_linear_problem} - - // Solving the linear system again - // works mostly as before. The only - // difference is that we want to - // only keep a complete local copy - // of the solution vector instead - // of the distributed one that we - // get as output from PETSc's - // solver routines. To this end, we - // declare a local temporary - // variable for the distributed - // vector and initialize it with - // the contents of the local - // variable (remember that the - // apply_boundary_values - // function called in - // assemble_system preset the - // values of boundary nodes in this - // vector), solve with it, and at - // the end of the function copy it - // again into the complete local - // vector that we declared as a - // member variable. Hanging node - // constraints are then distributed - // only on the local copy, - // i.e. independently of each other - // on each of the processors: + // @sect4{TopLevel::solve_linear_problem} + + // Solving the linear system again + // works mostly as before. The only + // difference is that we want to + // only keep a complete local copy + // of the solution vector instead + // of the distributed one that we + // get as output from PETSc's + // solver routines. To this end, we + // declare a local temporary + // variable for the distributed + // vector and initialize it with + // the contents of the local + // variable (remember that the + // apply_boundary_values + // function called in + // assemble_system preset the + // values of boundary nodes in this + // vector), solve with it, and at + // the end of the function copy it + // again into the complete local + // vector that we declared as a + // member variable. Hanging node + // constraints are then distributed + // only on the local copy, + // i.e. independently of each other + // on each of the processors: template unsigned int TopLevel::solve_linear_problem () { PETScWrappers::MPI::Vector - distributed_incremental_displacement (mpi_communicator, - dof_handler.n_dofs(), - n_local_dofs); + distributed_incremental_displacement (mpi_communicator, + dof_handler.n_dofs(), + n_local_dofs); distributed_incremental_displacement = incremental_displacement; SolverControl solver_control (dof_handler.n_dofs(), - 1e-16*system_rhs.l2_norm()); + 1e-16*system_rhs.l2_norm()); PETScWrappers::SolverCG cg (solver_control, - mpi_communicator); + mpi_communicator); PETScWrappers::PreconditionBlockJacobi preconditioner(system_matrix); cg.solve (system_matrix, distributed_incremental_displacement, system_rhs, - preconditioner); + preconditioner); incremental_displacement = distributed_incremental_displacement; @@ -1789,116 +1797,116 @@ namespace Step18 - // @sect4{TopLevel::output_results} - - // This function generates the - // graphical output in intermediate - // format as explained in the - // introduction. Each process will - // only work on the cells it owns, - // and then write the result into a - // file of its own. These files may - // later be merged to get a single - // file in any of the supported - // output files, as mentioned in - // the introduction. - // - // The crucial part of this function is to - // give the DataOut class a way to only - // work on the cells that the present - // process owns. This class is already - // well-equipped for that: it has two - // virtual functions first_cell and - // next_cell that return the first cell - // to be worked on, and given one cell - // return the next cell to be worked on. By - // default, these functions return the - // first active cell (i.e. the first one - // that has no children) and the next - // active cell. What we have to do here is - // derive a class from DataOut that - // overloads these two functions to only - // iterate over those cells with the right - // subdomain indicator. - // - // We do this at the beginning of this - // function. The first_cell function - // just starts with the first active cell, - // and then iterates to the next cells - // while the cell presently under - // consideration does not yet have the - // correct subdomain id. The only thing - // that needs to be taken care of is that - // we don't try to keep iterating when we - // have hit the end iterator. - // - // The next_cell function could be - // implemented in a similar way. However, - // we use this occasion as a pretext to - // introduce one more thing that the - // library offers: filtered - // iterators. These are wrappers for the - // iterator classes that just skip all - // cells (or faces, lines, etc) that do not - // satisfy a certain predicate (a predicate - // in computer-lingo is a function that - // when applied to a data element either - // returns true or false). In the present - // case, the predicate is that the cell has - // to have a certain subdomain id, and the - // library already has this predicate built - // in. If the cell iterator is not the end - // iterator, what we then have to do is to - // initialize such a filtered iterator with - // the present cell and the predicate, and - // then increase the iterator exactly - // once. While the more conventional loop - // would probably not have been much - // longer, this is definitely the more - // elegant way -- and then, these example - // programs also serve the purpose of - // introducing what is available in - // deal.II. + // @sect4{TopLevel::output_results} + + // This function generates the + // graphical output in intermediate + // format as explained in the + // introduction. Each process will + // only work on the cells it owns, + // and then write the result into a + // file of its own. These files may + // later be merged to get a single + // file in any of the supported + // output files, as mentioned in + // the introduction. + // + // The crucial part of this function is to + // give the DataOut class a way to only + // work on the cells that the present + // process owns. This class is already + // well-equipped for that: it has two + // virtual functions first_cell and + // next_cell that return the first cell + // to be worked on, and given one cell + // return the next cell to be worked on. By + // default, these functions return the + // first active cell (i.e. the first one + // that has no children) and the next + // active cell. What we have to do here is + // derive a class from DataOut that + // overloads these two functions to only + // iterate over those cells with the right + // subdomain indicator. + // + // We do this at the beginning of this + // function. The first_cell function + // just starts with the first active cell, + // and then iterates to the next cells + // while the cell presently under + // consideration does not yet have the + // correct subdomain id. The only thing + // that needs to be taken care of is that + // we don't try to keep iterating when we + // have hit the end iterator. + // + // The next_cell function could be + // implemented in a similar way. However, + // we use this occasion as a pretext to + // introduce one more thing that the + // library offers: filtered + // iterators. These are wrappers for the + // iterator classes that just skip all + // cells (or faces, lines, etc) that do not + // satisfy a certain predicate (a predicate + // in computer-lingo is a function that + // when applied to a data element either + // returns true or false). In the present + // case, the predicate is that the cell has + // to have a certain subdomain id, and the + // library already has this predicate built + // in. If the cell iterator is not the end + // iterator, what we then have to do is to + // initialize such a filtered iterator with + // the present cell and the predicate, and + // then increase the iterator exactly + // once. While the more conventional loop + // would probably not have been much + // longer, this is definitely the more + // elegant way -- and then, these example + // programs also serve the purpose of + // introducing what is available in + // deal.II. template class FilteredDataOut : public DataOut { - public: - FilteredDataOut (const unsigned int subdomain_id) - : - subdomain_id (subdomain_id) - {} - - virtual typename DoFHandler::cell_iterator - first_cell () - { - typename DoFHandler::active_cell_iterator - cell = this->dofs->begin_active(); - while ((cell != this->dofs->end()) && - (cell->subdomain_id() != subdomain_id)) - ++cell; - - return cell; - } - - virtual typename DoFHandler::cell_iterator - next_cell (const typename DoFHandler::cell_iterator &old_cell) - { - if (old_cell != this->dofs->end()) - { - const IteratorFilters::SubdomainEqualTo - predicate(subdomain_id); - - return - ++(FilteredIterator - ::active_cell_iterator> - (predicate,old_cell)); - } - else - return old_cell; - } - - private: - const unsigned int subdomain_id; + public: + FilteredDataOut (const unsigned int subdomain_id) + : + subdomain_id (subdomain_id) + {} + + virtual typename DoFHandler::cell_iterator + first_cell () + { + typename DoFHandler::active_cell_iterator + cell = this->dofs->begin_active(); + while ((cell != this->dofs->end()) && + (cell->subdomain_id() != subdomain_id)) + ++cell; + + return cell; + } + + virtual typename DoFHandler::cell_iterator + next_cell (const typename DoFHandler::cell_iterator &old_cell) + { + if (old_cell != this->dofs->end()) + { + const IteratorFilters::SubdomainEqualTo + predicate(subdomain_id); + + return + ++(FilteredIterator + ::active_cell_iterator> + (predicate,old_cell)); + } + else + return old_cell; + } + + private: + const unsigned int subdomain_id; }; @@ -1906,290 +1914,290 @@ namespace Step18 template void TopLevel::output_results () const { - // With this newly defined class, declare - // an object that is going to generate - // the graphical output and attach the - // dof handler with it from which to get - // the solution vector: + // With this newly defined class, declare + // an object that is going to generate + // the graphical output and attach the + // dof handler with it from which to get + // the solution vector: FilteredDataOut data_out(this_mpi_process); data_out.attach_dof_handler (dof_handler); - // Then, just as in step-17, define the - // names of solution variables (which - // here are the displacement increments) - // and queue the solution vector for - // output. Note in the following switch - // how we make sure that if the space - // dimension should be unhandled that we - // throw an exception saying that we - // haven't implemented this case yet - // (another case of defensive - // programming): + // Then, just as in step-17, define the + // names of solution variables (which + // here are the displacement increments) + // and queue the solution vector for + // output. Note in the following switch + // how we make sure that if the space + // dimension should be unhandled that we + // throw an exception saying that we + // haven't implemented this case yet + // (another case of defensive + // programming): std::vector solution_names; switch (dim) { - case 1: - solution_names.push_back ("delta_x"); - break; - case 2: - solution_names.push_back ("delta_x"); - solution_names.push_back ("delta_y"); - break; - case 3: - solution_names.push_back ("delta_x"); - solution_names.push_back ("delta_y"); - solution_names.push_back ("delta_z"); - break; - default: - Assert (false, ExcNotImplemented()); + case 1: + solution_names.push_back ("delta_x"); + break; + case 2: + solution_names.push_back ("delta_x"); + solution_names.push_back ("delta_y"); + break; + case 3: + solution_names.push_back ("delta_x"); + solution_names.push_back ("delta_y"); + solution_names.push_back ("delta_z"); + break; + default: + Assert (false, ExcNotImplemented()); } data_out.add_data_vector (incremental_displacement, - solution_names); - - - // The next thing is that we wanted to - // output something like the average norm - // of the stresses that we have stored in - // each cell. This may seem complicated, - // since on the present processor we only - // store the stresses in quadrature - // points on those cells that actually - // belong to the present process. In - // other words, it seems as if we can't - // compute the average stresses for all - // cells. However, remember that our - // class derived from DataOut only - // iterates over those cells that - // actually do belong to the present - // processor, i.e. we don't have to - // compute anything for all the other - // cells as this information would not be - // touched. The following little loop - // does this. We enclose the entire block - // into a pair of braces to make sure - // that the iterator variables do not - // remain accidentally visible beyond the - // end of the block in which they are - // used: + solution_names); + + + // The next thing is that we wanted to + // output something like the average norm + // of the stresses that we have stored in + // each cell. This may seem complicated, + // since on the present processor we only + // store the stresses in quadrature + // points on those cells that actually + // belong to the present process. In + // other words, it seems as if we can't + // compute the average stresses for all + // cells. However, remember that our + // class derived from DataOut only + // iterates over those cells that + // actually do belong to the present + // processor, i.e. we don't have to + // compute anything for all the other + // cells as this information would not be + // touched. The following little loop + // does this. We enclose the entire block + // into a pair of braces to make sure + // that the iterator variables do not + // remain accidentally visible beyond the + // end of the block in which they are + // used: Vector norm_of_stress (triangulation.n_active_cells()); { - // Loop over all the cells... + // Loop over all the cells... typename Triangulation::active_cell_iterator - cell = triangulation.begin_active(), - endc = triangulation.end(); + cell = triangulation.begin_active(), + endc = triangulation.end(); for (unsigned int index=0; cell!=endc; ++cell, ++index) - // ... and pick those that are - // relevant to us: - if (cell->subdomain_id() == this_mpi_process) - { - // On these cells, add up the - // stresses over all quadrature - // points... - SymmetricTensor<2,dim> accumulated_stress; - for (unsigned int q=0; - q*>(cell->user_pointer())[q] - .old_stress; - - // ...then write the norm of the - // average to their destination: - norm_of_stress(index) - = (accumulated_stress / - quadrature_formula.size()).norm(); - } - // And on the cells that we are not - // interested in, set the respective - // value in the vector to a bogus value - // (norms must be positive, and a large - // negative value should catch your - // eye) in order to make sure that if - // we were somehow wrong about our - // assumption that these elements would - // not appear in the output file, that - // we would find out by looking at the - // graphical output: - else - norm_of_stress(index) = -1e+20; + // ... and pick those that are + // relevant to us: + if (cell->subdomain_id() == this_mpi_process) + { + // On these cells, add up the + // stresses over all quadrature + // points... + SymmetricTensor<2,dim> accumulated_stress; + for (unsigned int q=0; + q*>(cell->user_pointer())[q] + .old_stress; + + // ...then write the norm of the + // average to their destination: + norm_of_stress(index) + = (accumulated_stress / + quadrature_formula.size()).norm(); + } + // And on the cells that we are not + // interested in, set the respective + // value in the vector to a bogus value + // (norms must be positive, and a large + // negative value should catch your + // eye) in order to make sure that if + // we were somehow wrong about our + // assumption that these elements would + // not appear in the output file, that + // we would find out by looking at the + // graphical output: + else + norm_of_stress(index) = -1e+20; } - // Finally attach this vector as well to - // be treated for output: + // Finally attach this vector as well to + // be treated for output: data_out.add_data_vector (norm_of_stress, "norm_of_stress"); - // As a last piece of data, let - // us also add the partitioning - // of the domain into subdomains - // associated with the processors - // if this is a parallel - // job. This works in the exact - // same way as in the step-17 - // program: + // As a last piece of data, let + // us also add the partitioning + // of the domain into subdomains + // associated with the processors + // if this is a parallel + // job. This works in the exact + // same way as in the step-17 + // program: std::vector partition_int (triangulation.n_active_cells()); GridTools::get_subdomain_association (triangulation, partition_int); const Vector partitioning(partition_int.begin(), - partition_int.end()); + partition_int.end()); data_out.add_data_vector (partitioning, "partitioning"); - // Finally, with all this data, - // we can instruct deal.II to - // munge the information and - // produce some intermediate data - // structures that contain all - // these solution and other data - // vectors: + // Finally, with all this data, + // we can instruct deal.II to + // munge the information and + // produce some intermediate data + // structures that contain all + // these solution and other data + // vectors: data_out.build_patches (); - // Now that we have generated the - // intermediate format, let us - // determine the name of the file - // we will want to write it - // to. We compose it of the - // prefix solution-, followed - // by a representation of the - // present time written as a - // fixed point number so that - // file names sort naturally: + // Now that we have generated the + // intermediate format, let us + // determine the name of the file + // we will want to write it + // to. We compose it of the + // prefix solution-, followed + // by a representation of the + // present time written as a + // fixed point number so that + // file names sort naturally: std::ostringstream filename; filename << "solution-"; filename << std::setfill('0'); filename.setf(std::ios::fixed, std::ios::floatfield); filename << std::setw(9) << std::setprecision(4) << present_time; - // Next, in case there are - // multiple processes working - // together, we have to generate - // different file names for the - // output of each process. In our - // case, we encode the process - // number as a three-digit - // integer, padded with - // zeros. The assertion in the - // first line of the block makes - // sure that there are less than - // 1000 processes (a very - // conservative check, but worth - // having anyway) as our scheme - // of generating process numbers - // would overflow if there were - // 1000 processes or more. Note - // that we choose to use - // AssertThrow rather than - // Assert since the number of - // processes is a variable that - // depends on input files or the - // way the process is started, - // rather than static assumptions - // in the program - // code. Therefore, it is - // inappropriate to use - // Assert that is optimized - // away in optimized mode, - // whereas here we actually can - // assume that users will run the - // largest computations with the - // most processors in optimized - // mode, and we should check our - // assumptions in this particular - // case, and not only when - // running in debug mode: + // Next, in case there are + // multiple processes working + // together, we have to generate + // different file names for the + // output of each process. In our + // case, we encode the process + // number as a three-digit + // integer, padded with + // zeros. The assertion in the + // first line of the block makes + // sure that there are less than + // 1000 processes (a very + // conservative check, but worth + // having anyway) as our scheme + // of generating process numbers + // would overflow if there were + // 1000 processes or more. Note + // that we choose to use + // AssertThrow rather than + // Assert since the number of + // processes is a variable that + // depends on input files or the + // way the process is started, + // rather than static assumptions + // in the program + // code. Therefore, it is + // inappropriate to use + // Assert that is optimized + // away in optimized mode, + // whereas here we actually can + // assume that users will run the + // largest computations with the + // most processors in optimized + // mode, and we should check our + // assumptions in this particular + // case, and not only when + // running in debug mode: if (n_mpi_processes != 1) { - AssertThrow (n_mpi_processes < 1000, ExcNotImplemented()); + AssertThrow (n_mpi_processes < 1000, ExcNotImplemented()); - filename << '-'; - filename << std::setfill('0'); - filename << std::setw(3) << this_mpi_process; + filename << '-'; + filename << std::setfill('0'); + filename << std::setw(3) << this_mpi_process; } - // To the file name, attach the - // file name suffix usually used - // for the deal.II intermediate - // format. To determine it, we - // use the same function that has - // already been used in step-13: + // To the file name, attach the + // file name suffix usually used + // for the deal.II intermediate + // format. To determine it, we + // use the same function that has + // already been used in step-13: filename << data_out.default_suffix(DataOut::deal_II_intermediate); - // With the so-completed - // filename, let us open a file - // and write the data we have - // generated into it, using the - // intermediate format: + // With the so-completed + // filename, let us open a file + // and write the data we have + // generated into it, using the + // intermediate format: std::ofstream output (filename.str().c_str()); data_out.write_deal_II_intermediate (output); } - // @sect4{TopLevel::do_initial_timestep} - - // This and the next function handle the - // overall structure of the first and - // following timesteps, respectively. The - // first timestep is slightly more involved - // because we want to compute it multiple - // times on successively refined meshes, - // each time starting from a clean - // state. At the end of these computations, - // in which we compute the incremental - // displacements each time, we use the last - // results obtained for the incremental - // displacements to compute the resulting - // stress updates and move the mesh - // accordingly. On this new mesh, we then - // output the solution and any additional - // data we consider important. - // - // All this is interspersed by generating - // output to the console to update the - // person watching the screen on what is - // going on. As in step-17, the use of - // pcout instead of std::cout makes - // sure that only one of the parallel - // processes is actually writing to the - // console, without having to explicitly - // code an if-statement in each place where - // we generate output: + // @sect4{TopLevel::do_initial_timestep} + + // This and the next function handle the + // overall structure of the first and + // following timesteps, respectively. The + // first timestep is slightly more involved + // because we want to compute it multiple + // times on successively refined meshes, + // each time starting from a clean + // state. At the end of these computations, + // in which we compute the incremental + // displacements each time, we use the last + // results obtained for the incremental + // displacements to compute the resulting + // stress updates and move the mesh + // accordingly. On this new mesh, we then + // output the solution and any additional + // data we consider important. + // + // All this is interspersed by generating + // output to the console to update the + // person watching the screen on what is + // going on. As in step-17, the use of + // pcout instead of std::cout makes + // sure that only one of the parallel + // processes is actually writing to the + // console, without having to explicitly + // code an if-statement in each place where + // we generate output: template void TopLevel::do_initial_timestep () { present_time += present_timestep; ++timestep_no; pcout << "Timestep " << timestep_no << " at time " << present_time - << std::endl; + << std::endl; for (unsigned int cycle=0; cycle<2; ++cycle) { - pcout << " Cycle " << cycle << ':' << std::endl; - - if (cycle == 0) - create_coarse_grid (); - else - refine_initial_grid (); - - pcout << " Number of active cells: " - << triangulation.n_active_cells() - << " (by partition:"; - for (unsigned int p=0; p void TopLevel::do_timestep () { present_time += present_timestep; ++timestep_no; pcout << "Timestep " << timestep_no << " at time " << present_time - << std::endl; + << std::endl; if (present_time > end_time) { - present_timestep -= (present_time - end_time); - present_time = end_time; + present_timestep -= (present_time - end_time); + present_time = end_time; } @@ -2229,588 +2237,588 @@ namespace Step18 } - // @sect4{TopLevel::refine_initial_grid} + // @sect4{TopLevel::refine_initial_grid} - // The following function is called when - // solving the first time step on - // successively refined meshes. After each - // iteration, it computes a refinement - // criterion, refines the mesh, and sets up - // the history variables in each quadrature - // point again to a clean state. + // The following function is called when + // solving the first time step on + // successively refined meshes. After each + // iteration, it computes a refinement + // criterion, refines the mesh, and sets up + // the history variables in each quadrature + // point again to a clean state. template void TopLevel::refine_initial_grid () { - // First, let each process compute error - // indicators for the cells it owns: + // First, let each process compute error + // indicators for the cells it owns: Vector error_per_cell (triangulation.n_active_cells()); KellyErrorEstimator::estimate (dof_handler, - QGauss(2), - typename FunctionMap::type(), - incremental_displacement, - error_per_cell, - ComponentMask(), - 0, - multithread_info.n_default_threads, - this_mpi_process); - - // Then set up a global vector into which - // we merge the local indicators from - // each of the %parallel processes: + QGauss(2), + typename FunctionMap::type(), + incremental_displacement, + error_per_cell, + ComponentMask(), + 0, + multithread_info.n_default_threads, + this_mpi_process); + + // Then set up a global vector into which + // we merge the local indicators from + // each of the %parallel processes: const unsigned int n_local_cells = GridTools::count_cells_with_subdomain_association (triangulation, - this_mpi_process); + this_mpi_process); PETScWrappers::MPI::Vector - distributed_error_per_cell (mpi_communicator, - triangulation.n_active_cells(), - n_local_cells); + distributed_error_per_cell (mpi_communicator, + triangulation.n_active_cells(), + n_local_cells); for (unsigned int i=0; icell-@>vertex_dof_index(v,d) function - // that returns the index of the dth - // degree of freedom at vertex v of the - // given cell. In the present case, - // displacement in the k-th coordinate - // direction corresonds to the kth - // component of the finite element. Using a - // function like this bears a certain risk, - // because it uses knowledge of the order - // of elements that we have taken together - // for this program in the FESystem - // element. If we decided to add an - // additional variable, for example a - // pressure variable for stabilization, and - // happened to insert it as the first - // variable of the element, then the - // computation below will start to produce - // non-sensical results. In addition, this - // computation rests on other assumptions: - // first, that the element we use has, - // indeed, degrees of freedom that are - // associated with vertices. This is indeed - // the case for the present Q1 element, as - // would be for all Qp elements of - // polynomial order p. However, it - // would not hold for discontinuous - // elements, or elements for mixed - // formulations. Secondly, it also rests on - // the assumption that the displacement at - // a vertex is determined solely by the - // value of the degree of freedom - // associated with this vertex; in other - // words, all shape functions corresponding - // to other degrees of freedom are zero at - // this particular vertex. Again, this is - // the case for the present element, but is - // not so for all elements that are - // presently available in deal.II. Despite - // its risks, we choose to use this way in - // order to present a way to query - // individual degrees of freedom associated - // with vertices. - // - // In this context, it is instructive to - // point out what a more general way would - // be. For general finite elements, the way - // to go would be to take a quadrature - // formula with the quadrature points in - // the vertices of a cell. The QTrapez - // formula for the trapezoidal rule does - // exactly this. With this quadrature - // formula, we would then initialize an - // FEValues object in each cell, and - // use the - // FEValues::get_function_values - // function to obtain the values of the - // solution function in the quadrature - // points, i.e. the vertices of the - // cell. These are the only values that we - // really need, i.e. we are not at all - // interested in the weights (or the - // JxW values) associated with this - // particular quadrature formula, and this - // can be specified as the last argument in - // the constructor to FEValues. The - // only point of minor inconvenience in - // this scheme is that we have to figure - // out which quadrature point corresponds - // to the vertex we consider at present, as - // they may or may not be ordered in the - // same order. - // - // Another point worth explaining about - // this short function is the way in which - // the triangulation class exports - // information about its vertices: through - // the Triangulation::n_vertices - // function, it advertises how many - // vertices there are in the - // triangulation. Not all of them are - // actually in use all the time -- some are - // left-overs from cells that have been - // coarsened previously and remain in - // existence since deal.II never changes - // the number of a vertex once it has come - // into existence, even if vertices with - // lower number go away. Secondly, the - // location returned by cell-@>vertex(v) - // is not only a read-only object of type - // Point@, but in fact a reference - // that can be written to. This allows to - // move around the nodes of a mesh with - // relative ease, but it is worth pointing - // out that it is the responsibility of an - // application program using this feature - // to make sure that the resulting cells - // are still useful, i.e. are not distorted - // so much that the cell is degenerated - // (indicated, for example, by negative - // Jacobians). Note that we do not have any - // provisions in this function to actually - // ensure this, we just have faith. - // - // After this lengthy introduction, here - // are the full 20 or so lines of code: + // @sect4{TopLevel::move_mesh} + + // At the end of each time step, we move + // the nodes of the mesh according to the + // incremental displacements computed in + // this time step. To do this, we keep a + // vector of flags that indicate for each + // vertex whether we have already moved it + // around, and then loop over all cells and + // move those vertices of the cell that + // have not been moved yet. It is worth + // noting that it does not matter from + // which of the cells adjacent to a vertex + // we move this vertex: since we compute + // the displacement using a continuous + // finite element, the displacement field + // is continuous as well and we can compute + // the displacement of a given vertex from + // each of the adjacent cells. We only have + // to make sure that we move each node + // exactly once, which is why we keep the + // vector of flags. + // + // There are two noteworthy things in this + // function. First, how we get the + // displacement field at a given vertex + // using the + // cell-@>vertex_dof_index(v,d) function + // that returns the index of the dth + // degree of freedom at vertex v of the + // given cell. In the present case, + // displacement in the k-th coordinate + // direction corresonds to the kth + // component of the finite element. Using a + // function like this bears a certain risk, + // because it uses knowledge of the order + // of elements that we have taken together + // for this program in the FESystem + // element. If we decided to add an + // additional variable, for example a + // pressure variable for stabilization, and + // happened to insert it as the first + // variable of the element, then the + // computation below will start to produce + // non-sensical results. In addition, this + // computation rests on other assumptions: + // first, that the element we use has, + // indeed, degrees of freedom that are + // associated with vertices. This is indeed + // the case for the present Q1 element, as + // would be for all Qp elements of + // polynomial order p. However, it + // would not hold for discontinuous + // elements, or elements for mixed + // formulations. Secondly, it also rests on + // the assumption that the displacement at + // a vertex is determined solely by the + // value of the degree of freedom + // associated with this vertex; in other + // words, all shape functions corresponding + // to other degrees of freedom are zero at + // this particular vertex. Again, this is + // the case for the present element, but is + // not so for all elements that are + // presently available in deal.II. Despite + // its risks, we choose to use this way in + // order to present a way to query + // individual degrees of freedom associated + // with vertices. + // + // In this context, it is instructive to + // point out what a more general way would + // be. For general finite elements, the way + // to go would be to take a quadrature + // formula with the quadrature points in + // the vertices of a cell. The QTrapez + // formula for the trapezoidal rule does + // exactly this. With this quadrature + // formula, we would then initialize an + // FEValues object in each cell, and + // use the + // FEValues::get_function_values + // function to obtain the values of the + // solution function in the quadrature + // points, i.e. the vertices of the + // cell. These are the only values that we + // really need, i.e. we are not at all + // interested in the weights (or the + // JxW values) associated with this + // particular quadrature formula, and this + // can be specified as the last argument in + // the constructor to FEValues. The + // only point of minor inconvenience in + // this scheme is that we have to figure + // out which quadrature point corresponds + // to the vertex we consider at present, as + // they may or may not be ordered in the + // same order. + // + // Another point worth explaining about + // this short function is the way in which + // the triangulation class exports + // information about its vertices: through + // the Triangulation::n_vertices + // function, it advertises how many + // vertices there are in the + // triangulation. Not all of them are + // actually in use all the time -- some are + // left-overs from cells that have been + // coarsened previously and remain in + // existence since deal.II never changes + // the number of a vertex once it has come + // into existence, even if vertices with + // lower number go away. Secondly, the + // location returned by cell-@>vertex(v) + // is not only a read-only object of type + // Point@, but in fact a reference + // that can be written to. This allows to + // move around the nodes of a mesh with + // relative ease, but it is worth pointing + // out that it is the responsibility of an + // application program using this feature + // to make sure that the resulting cells + // are still useful, i.e. are not distorted + // so much that the cell is degenerated + // (indicated, for example, by negative + // Jacobians). Note that we do not have any + // provisions in this function to actually + // ensure this, we just have faith. + // + // After this lengthy introduction, here + // are the full 20 or so lines of code: template void TopLevel::move_mesh () { pcout << " Moving mesh..." << std::endl; std::vector vertex_touched (triangulation.n_vertices(), - false); + false); for (typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active (); - cell != dof_handler.end(); ++cell) + cell = dof_handler.begin_active (); + cell != dof_handler.end(); ++cell) for (unsigned int v=0; v::vertices_per_cell; ++v) - if (vertex_touched[cell->vertex_index(v)] == false) - { - vertex_touched[cell->vertex_index(v)] = true; + if (vertex_touched[cell->vertex_index(v)] == false) + { + vertex_touched[cell->vertex_index(v)] = true; - Point vertex_displacement; - for (unsigned int d=0; dvertex_dof_index(v,d)); + Point vertex_displacement; + for (unsigned int d=0; dvertex_dof_index(v,d)); - cell->vertex(v) += vertex_displacement; - } + cell->vertex(v) += vertex_displacement; + } } - // @sect4{TopLevel::setup_quadrature_point_history} - - // At the beginning of our computations, we - // needed to set up initial values of the - // history variables, such as the existing - // stresses in the material, that we store - // in each quadrature point. As mentioned - // above, we use the user_pointer for - // this that is available in each cell. - // - // To put this into larger perspective, we - // note that if we had previously available - // stresses in our model (which we assume - // do not exist for the purpose of this - // program), then we would need to - // interpolate the field of pre-existing - // stresses to the quadrature - // points. Likewise, if we were to simulate - // elasto-plastic materials with - // hardening/softening, then we would have - // to store additional history variables - // like the present yield stress of the - // accumulated plastic strains in each - // quadrature points. Pre-existing - // hardening or weakening would then be - // implemented by interpolating these - // variables in the present function as - // well. + // @sect4{TopLevel::setup_quadrature_point_history} + + // At the beginning of our computations, we + // needed to set up initial values of the + // history variables, such as the existing + // stresses in the material, that we store + // in each quadrature point. As mentioned + // above, we use the user_pointer for + // this that is available in each cell. + // + // To put this into larger perspective, we + // note that if we had previously available + // stresses in our model (which we assume + // do not exist for the purpose of this + // program), then we would need to + // interpolate the field of pre-existing + // stresses to the quadrature + // points. Likewise, if we were to simulate + // elasto-plastic materials with + // hardening/softening, then we would have + // to store additional history variables + // like the present yield stress of the + // accumulated plastic strains in each + // quadrature points. Pre-existing + // hardening or weakening would then be + // implemented by interpolating these + // variables in the present function as + // well. template void TopLevel::setup_quadrature_point_history () { - // What we need to do here is to first - // count how many quadrature points are - // within the responsibility of this - // processor. This, of course, equals the - // number of cells that belong to this - // processor times the number of - // quadrature points our quadrature - // formula has on each cell. - // - // For good measure, we also set all user - // pointers of all cells, whether ours of - // not, to the null pointer. This way, if - // we ever access the user pointer of a - // cell which we should not have - // accessed, a segmentation fault will - // let us know that this should not have - // happened: + // What we need to do here is to first + // count how many quadrature points are + // within the responsibility of this + // processor. This, of course, equals the + // number of cells that belong to this + // processor times the number of + // quadrature points our quadrature + // formula has on each cell. + // + // For good measure, we also set all user + // pointers of all cells, whether ours of + // not, to the null pointer. This way, if + // we ever access the user pointer of a + // cell which we should not have + // accessed, a segmentation fault will + // let us know that this should not have + // happened: unsigned int our_cells = 0; for (typename Triangulation::active_cell_iterator - cell = triangulation.begin_active(); - cell != triangulation.end(); ++cell) + cell = triangulation.begin_active(); + cell != triangulation.end(); ++cell) if (cell->subdomain_id() == this_mpi_process) - ++our_cells; + ++our_cells; triangulation.clear_user_data(); - // Next, allocate as many quadrature - // objects as we need. Since the - // resize function does not actually - // shrink the amount of allocated memory - // if the requested new size is smaller - // than the old size, we resort to a - // trick to first free all memory, and - // then reallocate it: we declare an - // empty vector as a temporary variable - // and then swap the contents of the old - // vector and this temporary - // variable. This makes sure that the - // quadrature_point_history is now - // really empty, and we can let the - // temporary variable that now holds the - // previous contents of the vector go out - // of scope and be destroyed. In the next - // step. we can then re-allocate as many - // elements as we need, with the vector - // default-initializing the - // PointHistory objects, which - // includes setting the stress variables - // to zero. + // Next, allocate as many quadrature + // objects as we need. Since the + // resize function does not actually + // shrink the amount of allocated memory + // if the requested new size is smaller + // than the old size, we resort to a + // trick to first free all memory, and + // then reallocate it: we declare an + // empty vector as a temporary variable + // and then swap the contents of the old + // vector and this temporary + // variable. This makes sure that the + // quadrature_point_history is now + // really empty, and we can let the + // temporary variable that now holds the + // previous contents of the vector go out + // of scope and be destroyed. In the next + // step. we can then re-allocate as many + // elements as we need, with the vector + // default-initializing the + // PointHistory objects, which + // includes setting the stress variables + // to zero. { std::vector > tmp; tmp.swap (quadrature_point_history); } quadrature_point_history.resize (our_cells * - quadrature_formula.size()); - - // Finally loop over all cells again and - // set the user pointers from the cells - // that belong to the present processor - // to point to the first quadrature point - // objects corresponding to this cell in - // the vector of such objects: + quadrature_formula.size()); + + // Finally loop over all cells again and + // set the user pointers from the cells + // that belong to the present processor + // to point to the first quadrature point + // objects corresponding to this cell in + // the vector of such objects: unsigned int history_index = 0; for (typename Triangulation::active_cell_iterator - cell = triangulation.begin_active(); - cell != triangulation.end(); ++cell) + cell = triangulation.begin_active(); + cell != triangulation.end(); ++cell) if (cell->subdomain_id() == this_mpi_process) - { - cell->set_user_pointer (&quadrature_point_history[history_index]); - history_index += quadrature_formula.size(); - } - - // At the end, for good measure make sure - // that our count of elements was correct - // and that we have both used up all - // objects we allocated previously, and - // not point to any objects beyond the - // end of the vector. Such defensive - // programming strategies are always good - // checks to avoid accidental errors and - // to guard against future changes to - // this function that forget to update - // all uses of a variable at the same - // time. Recall that constructs using the - // Assert macro are optimized away in - // optimized mode, so do not affect the - // run time of optimized runs: + { + cell->set_user_pointer (&quadrature_point_history[history_index]); + history_index += quadrature_formula.size(); + } + + // At the end, for good measure make sure + // that our count of elements was correct + // and that we have both used up all + // objects we allocated previously, and + // not point to any objects beyond the + // end of the vector. Such defensive + // programming strategies are always good + // checks to avoid accidental errors and + // to guard against future changes to + // this function that forget to update + // all uses of a variable at the same + // time. Recall that constructs using the + // Assert macro are optimized away in + // optimized mode, so do not affect the + // run time of optimized runs: Assert (history_index == quadrature_point_history.size(), - ExcInternalError()); + ExcInternalError()); } - // @sect4{TopLevel::update_quadrature_point_history} - - // At the end of each time step, we - // should have computed an - // incremental displacement update - // so that the material in its new - // configuration accommodates for - // the difference between the - // external body and boundary - // forces applied during this time - // step minus the forces exerted - // through pre-existing internal - // stresses. In order to have the - // pre-existing stresses available - // at the next time step, we - // therefore have to update the - // pre-existing stresses with the - // stresses due to the incremental - // displacement computed during the - // present time step. Ideally, the - // resulting sum of internal - // stresses would exactly counter - // all external forces. Indeed, a - // simple experiment can make sure - // that this is so: if we choose - // boundary conditions and body - // forces to be time independent, - // then the forcing terms (the sum - // of external forces and internal - // stresses) should be exactly - // zero. If you make this - // experiment, you will realize - // from the output of the norm of - // the right hand side in each time - // step that this is almost the - // case: it is not exactly zero, - // since in the first time step the - // incremental displacement and - // stress updates were computed - // relative to the undeformed mesh, - // which was then deformed. In the - // second time step, we again - // compute displacement and stress - // updates, but this time in the - // deformed mesh -- there, the - // resulting updates are very small - // but not quite zero. This can be - // iterated, and in each such - // iteration the residual, i.e. the - // norm of the right hand side - // vector, is reduced; if one makes - // this little experiment, one - // realizes that the norm of this - // residual decays exponentially - // with the number of iterations, - // and after an initial very rapid - // decline is reduced by roughly a - // factor of about 3.5 in each - // iteration (for one testcase I - // looked at, other testcases, and - // other numbers of unknowns change - // the factor, but not the - // exponential decay). - - // In a sense, this can then be considered - // as a quasi-timestepping scheme to - // resolve the nonlinear problem of solving - // large-deformation elasticity on a mesh - // that is moved along in a Lagrangian - // manner. - // - // Another complication is that the - // existing (old) stresses are defined on - // the old mesh, which we will move around - // after updating the stresses. If this - // mesh update involves rotations of the - // cell, then we need to also rotate the - // updated stress, since it was computed - // relative to the coordinate system of the - // old cell. - // - // Thus, what we need is the following: on - // each cell which the present processor - // owns, we need to extract the old stress - // from the data stored with each - // quadrature point, compute the stress - // update, add the two together, and then - // rotate the result together with the - // incremental rotation computed from the - // incremental displacement at the present - // quadrature point. We will detail these - // steps below: + // @sect4{TopLevel::update_quadrature_point_history} + + // At the end of each time step, we + // should have computed an + // incremental displacement update + // so that the material in its new + // configuration accommodates for + // the difference between the + // external body and boundary + // forces applied during this time + // step minus the forces exerted + // through pre-existing internal + // stresses. In order to have the + // pre-existing stresses available + // at the next time step, we + // therefore have to update the + // pre-existing stresses with the + // stresses due to the incremental + // displacement computed during the + // present time step. Ideally, the + // resulting sum of internal + // stresses would exactly counter + // all external forces. Indeed, a + // simple experiment can make sure + // that this is so: if we choose + // boundary conditions and body + // forces to be time independent, + // then the forcing terms (the sum + // of external forces and internal + // stresses) should be exactly + // zero. If you make this + // experiment, you will realize + // from the output of the norm of + // the right hand side in each time + // step that this is almost the + // case: it is not exactly zero, + // since in the first time step the + // incremental displacement and + // stress updates were computed + // relative to the undeformed mesh, + // which was then deformed. In the + // second time step, we again + // compute displacement and stress + // updates, but this time in the + // deformed mesh -- there, the + // resulting updates are very small + // but not quite zero. This can be + // iterated, and in each such + // iteration the residual, i.e. the + // norm of the right hand side + // vector, is reduced; if one makes + // this little experiment, one + // realizes that the norm of this + // residual decays exponentially + // with the number of iterations, + // and after an initial very rapid + // decline is reduced by roughly a + // factor of about 3.5 in each + // iteration (for one testcase I + // looked at, other testcases, and + // other numbers of unknowns change + // the factor, but not the + // exponential decay). + + // In a sense, this can then be considered + // as a quasi-timestepping scheme to + // resolve the nonlinear problem of solving + // large-deformation elasticity on a mesh + // that is moved along in a Lagrangian + // manner. + // + // Another complication is that the + // existing (old) stresses are defined on + // the old mesh, which we will move around + // after updating the stresses. If this + // mesh update involves rotations of the + // cell, then we need to also rotate the + // updated stress, since it was computed + // relative to the coordinate system of the + // old cell. + // + // Thus, what we need is the following: on + // each cell which the present processor + // owns, we need to extract the old stress + // from the data stored with each + // quadrature point, compute the stress + // update, add the two together, and then + // rotate the result together with the + // incremental rotation computed from the + // incremental displacement at the present + // quadrature point. We will detail these + // steps below: template void TopLevel::update_quadrature_point_history () { - // First, set up an FEValues object - // by which we will evaluate the - // incremental displacements and the - // gradients thereof at the quadrature - // points, together with a vector that - // will hold this information: + // First, set up an FEValues object + // by which we will evaluate the + // incremental displacements and the + // gradients thereof at the quadrature + // points, together with a vector that + // will hold this information: FEValues fe_values (fe, quadrature_formula, - update_values | update_gradients); + update_values | update_gradients); std::vector > > - displacement_increment_grads (quadrature_formula.size(), - std::vector >(dim)); + displacement_increment_grads (quadrature_formula.size(), + std::vector >(dim)); - // Then loop over all cells and do the - // job in the cells that belong to our - // subdomain: + // Then loop over all cells and do the + // job in the cells that belong to our + // subdomain: for (typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(); - cell != dof_handler.end(); ++cell) + cell = dof_handler.begin_active(); + cell != dof_handler.end(); ++cell) if (cell->subdomain_id() == this_mpi_process) - { - // Next, get a pointer to the - // quadrature point history data - // local to the present cell, and, - // as a defensive measure, make - // sure that this pointer is within - // the bounds of the global array: - PointHistory *local_quadrature_points_history - = reinterpret_cast *>(cell->user_pointer()); - Assert (local_quadrature_points_history >= - &quadrature_point_history.front(), - ExcInternalError()); - Assert (local_quadrature_points_history < - &quadrature_point_history.back(), - ExcInternalError()); - - // Then initialize the FEValues - // object on the present cell, and - // extract the gradients of the - // displacement at the quadrature - // points for later computation of - // the strains - fe_values.reinit (cell); - fe_values.get_function_grads (incremental_displacement, - displacement_increment_grads); - - // Then loop over the quadrature - // points of this cell: - for (unsigned int q=0; q new_stress - = (local_quadrature_points_history[q].old_stress - + - (stress_strain_tensor * - get_strain (displacement_increment_grads[q]))); - - // Finally, we have to rotate - // the result. For this, we - // first have to compute a - // rotation matrix at the - // present quadrature point - // from the incremental - // displacements. In fact, it - // can be computed from the - // gradients, and we already - // have a function for that - // purpose: - const Tensor<2,dim> rotation - = get_rotation_matrix (displacement_increment_grads[q]); - // Note that the result, a - // rotation matrix, is in - // general an antisymmetric - // tensor of rank 2, so we must - // store it as a full tensor. - - // With this rotation matrix, - // we can compute the rotated - // tensor by contraction from - // the left and right, after we - // expand the symmetric tensor - // new_stress into a full - // tensor: - const SymmetricTensor<2,dim> rotated_new_stress - = symmetrize(transpose(rotation) * - static_cast >(new_stress) * - rotation); - // Note that while the - // result of the - // multiplication of - // these three matrices - // should be symmetric, - // it is not due to - // floating point round - // off: we get an - // asymmetry on the - // order of 1e-16 of - // the off-diagonal - // elements of the - // result. When - // assigning the result - // to a - // SymmetricTensor, - // the constuctor of - // that class checks - // the symmetry and - // realizes that it - // isn't exactly - // symmetric; it will - // then raise an - // exception. To avoid - // that, we explicitly - // symmetrize the - // result to make it - // exactly symmetric. - - // The result of all these - // operations is then written - // back into the original - // place: - local_quadrature_points_history[q].old_stress - = rotated_new_stress; - } - } + { + // Next, get a pointer to the + // quadrature point history data + // local to the present cell, and, + // as a defensive measure, make + // sure that this pointer is within + // the bounds of the global array: + PointHistory *local_quadrature_points_history + = reinterpret_cast *>(cell->user_pointer()); + Assert (local_quadrature_points_history >= + &quadrature_point_history.front(), + ExcInternalError()); + Assert (local_quadrature_points_history < + &quadrature_point_history.back(), + ExcInternalError()); + + // Then initialize the FEValues + // object on the present cell, and + // extract the gradients of the + // displacement at the quadrature + // points for later computation of + // the strains + fe_values.reinit (cell); + fe_values.get_function_grads (incremental_displacement, + displacement_increment_grads); + + // Then loop over the quadrature + // points of this cell: + for (unsigned int q=0; q new_stress + = (local_quadrature_points_history[q].old_stress + + + (stress_strain_tensor * + get_strain (displacement_increment_grads[q]))); + + // Finally, we have to rotate + // the result. For this, we + // first have to compute a + // rotation matrix at the + // present quadrature point + // from the incremental + // displacements. In fact, it + // can be computed from the + // gradients, and we already + // have a function for that + // purpose: + const Tensor<2,dim> rotation + = get_rotation_matrix (displacement_increment_grads[q]); + // Note that the result, a + // rotation matrix, is in + // general an antisymmetric + // tensor of rank 2, so we must + // store it as a full tensor. + + // With this rotation matrix, + // we can compute the rotated + // tensor by contraction from + // the left and right, after we + // expand the symmetric tensor + // new_stress into a full + // tensor: + const SymmetricTensor<2,dim> rotated_new_stress + = symmetrize(transpose(rotation) * + static_cast >(new_stress) * + rotation); + // Note that while the + // result of the + // multiplication of + // these three matrices + // should be symmetric, + // it is not due to + // floating point round + // off: we get an + // asymmetry on the + // order of 1e-16 of + // the off-diagonal + // elements of the + // result. When + // assigning the result + // to a + // SymmetricTensor, + // the constuctor of + // that class checks + // the symmetry and + // realizes that it + // isn't exactly + // symmetric; it will + // then raise an + // exception. To avoid + // that, we explicitly + // symmetrize the + // result to make it + // exactly symmetric. + + // The result of all these + // operations is then written + // back into the original + // place: + local_quadrature_points_history[q].old_stress + = rotated_new_stress; + } + } } - // This ends the project specific - // namespace - // Step18. The - // rest is as usual and as already - // shown in step-17: A main() - // function that initializes and - // terminates PETSc, calls the - // classes that do the actual work, - // and makes sure that we catch all - // exceptions that propagate up to - // this point: + // This ends the project specific + // namespace + // Step18. The + // rest is as usual and as already + // shown in step-17: A main() + // function that initializes and + // terminates PETSc, calls the + // classes that do the actual work, + // and makes sure that we catch all + // exceptions that propagate up to + // this point: } @@ -2832,25 +2840,25 @@ int main (int argc, char **argv) catch (std::exception &exc) { std::cerr << std::endl << std::endl - << "----------------------------------------------------" - << std::endl; + << "----------------------------------------------------" + << std::endl; std::cerr << "Exception on processing: " << std::endl - << exc.what() << std::endl - << "Aborting!" << std::endl - << "----------------------------------------------------" - << std::endl; + << exc.what() << std::endl + << "Aborting!" << std::endl + << "----------------------------------------------------" + << std::endl; return 1; } catch (...) { std::cerr << std::endl << std::endl - << "----------------------------------------------------" - << std::endl; + << "----------------------------------------------------" + << std::endl; std::cerr << "Unknown exception!" << std::endl - << "Aborting!" << std::endl - << "----------------------------------------------------" - << std::endl; + << "Aborting!" << std::endl + << "----------------------------------------------------" + << std::endl; return 1; } diff --git a/deal.II/examples/step-19/step-19.cc b/deal.II/examples/step-19/step-19.cc index e805b63b0f..c0a2f21da0 100644 --- a/deal.II/examples/step-19/step-19.cc +++ b/deal.II/examples/step-19/step-19.cc @@ -10,14 +10,14 @@ /* further information on this license. */ - // @sect4{Preliminaries} - - // As usual, we start with include - // files. This program is content with really - // few of these -- we only need two files - // from the library (one for input and output - // of graphical data, one for parameter - // handling), and a few C++ standard headers: +// @sect4{Preliminaries} + +// As usual, we start with include +// files. This program is content with really +// few of these -- we only need two files +// from the library (one for input and output +// of graphical data, one for parameter +// handling), and a few C++ standard headers: #include #include @@ -25,206 +25,206 @@ #include #include - // As mentioned in the first few tutorial - // programs, all names in deal.II are - // declared in a namespace - // dealii. To make using these - // function and class names simpler, we - // import the entire content of that - // namespace into the global scope. As done - // for all previous programs already, we'll - // also place everything we do here into a - // namespace of its own: +// As mentioned in the first few tutorial +// programs, all names in deal.II are +// declared in a namespace +// dealii. To make using these +// function and class names simpler, we +// import the entire content of that +// namespace into the global scope. As done +// for all previous programs already, we'll +// also place everything we do here into a +// namespace of its own: namespace Step19 { using namespace dealii; - // Before we start with the actual program, - // let us declare a few global variables that - // will be used to hold the parameters this - // program is going to use. Usually, global - // variables are frowned upon for a good - // reason, but since we have such a short - // program here that does only a single - // thing, we may stray from our usual line - // and make these variables global, rather - // than passing them around to all functions - // or encapsulating them into a class. - // - // The variables we have are: first, an - // object that will hold parameters of - // operation, such as output format (unless - // given on the command line); second, the - // names of input and output files; and third, - // the format in which the output is to be - // written: + // Before we start with the actual program, + // let us declare a few global variables that + // will be used to hold the parameters this + // program is going to use. Usually, global + // variables are frowned upon for a good + // reason, but since we have such a short + // program here that does only a single + // thing, we may stray from our usual line + // and make these variables global, rather + // than passing them around to all functions + // or encapsulating them into a class. + // + // The variables we have are: first, an + // object that will hold parameters of + // operation, such as output format (unless + // given on the command line); second, the + // names of input and output files; and third, + // the format in which the output is to be + // written: ParameterHandler prm; std::vector input_file_names; std::string output_file; std::string output_format; - // All the stuff this program does can be - // done from here on. As described in the - // introduction, what we have to do is - // declare what values the parameter file can - // have, parse the command line, read the - // input files, then write the output. We - // will do this in this order of operation, - // but before that let us declare a function - // that prints a message about how this - // program is to be used; the function first - // prints a general message, and then goes on - // to list the parameters that are allowed in - // the parameter file (the - // ParameterHandler class has a function - // to do exactly this; see the results - // section for what it prints): + // All the stuff this program does can be + // done from here on. As described in the + // introduction, what we have to do is + // declare what values the parameter file can + // have, parse the command line, read the + // input files, then write the output. We + // will do this in this order of operation, + // but before that let us declare a function + // that prints a message about how this + // program is to be used; the function first + // prints a general message, and then goes on + // to list the parameters that are allowed in + // the parameter file (the + // ParameterHandler class has a function + // to do exactly this; see the results + // section for what it prints): void print_usage_message () { - static const char* message + static const char *message = - "\n" - "Converter from deal.II intermediate format to other graphics formats.\n" - "\n" - "Usage:\n" - " ./step-19 [-p parameter_file] list_of_input_files \n" - " [-x output_format] [-o output_file]\n" - "\n" - "Parameter sequences in brackets can be omitted if a parameter file is\n" - "specified on the command line and if it provides values for these\n" - "missing parameters.\n" - "\n" - "The parameter file has the following format and allows the following\n" - "values (you can cut and paste this and use it for your own parameter\n" - "file):\n" - "\n"; + "\n" + "Converter from deal.II intermediate format to other graphics formats.\n" + "\n" + "Usage:\n" + " ./step-19 [-p parameter_file] list_of_input_files \n" + " [-x output_format] [-o output_file]\n" + "\n" + "Parameter sequences in brackets can be omitted if a parameter file is\n" + "specified on the command line and if it provides values for these\n" + "missing parameters.\n" + "\n" + "The parameter file has the following format and allows the following\n" + "values (you can cut and paste this and use it for your own parameter\n" + "file):\n" + "\n"; std::cout << message; prm.print_parameters (std::cout, ParameterHandler::Text); } - // @sect4{Declaring parameters for the input file} - - // The second function is used to declare the - // parameters this program accepts from the - // input file. While we don't actually take - // many parameters from the input file except - // for, possibly, the output file name and - // format, we nevertheless want to show how - // to work with parameter files. - // - // In short, the ParameterHandler class - // works as follows: one declares the entries - // of parameters that can be given in input - // files together, and later on one can read - // an input file in which these parameters - // are set to their values. If a parameter is - // not listed in the input file, the default - // value specified in the declaration of that - // parameter is used. After that, the program - // can query the values assigned to certain - // parameters from the ParameterHandler - // object. - // - // Declaring parameters can be done using the - // ParameterHandler::declare_entry - // function. It's arguments are the name of a - // parameter, a default value (given as a - // string, even if the parameter is numeric - // in nature, and thirdly an object that - // describes constraints on values that may - // be passed to this parameter. In the - // example below, we use an object of type - // Patterns::Anything to denote that - // there are no constraints on file names - // (this is, of course, not true -- the - // operating system does have constraints, - // but from an application standpoint, almost - // all names are valid). In other cases, one - // may, for example, use - // Patterns::Integer to make sure that - // only parameters are accepted that can be - // interpreted as integer values (it is also - // possible to specify bounds for integer - // values, and all values outside this range - // are rejected), Patterns::Double for - // floating point values, classes that make - // sure that the given parameter value is a - // comma separated list of things, etc. Take - // a look at the Patterns namespace to - // see what is possible. - // - // The fourth argument to declare_entry - // is a help string that can be printed to - // document what this parameter is meant to - // be used for and other information you may - // consider important when declaring this - // parameter. The default value of this - // fourth argument is the empty string. - // - // I always wanted to have an example program - // describing the ParameterHandler class, - // because it is so particularly useful. It - // would have been useful in a number of - // previous example programs (for example, in - // order to let the tolerance for linear - // solvers, or the number of refinement steps - // be determined by a run-time parameter, - // rather than hard-coding them into the - // program), but it turned out that trying to - // explain this class there would have - // overloaded them with things that would - // have distracted from the main - // purpose. However, while writing this - // program, I realized that there aren't all - // that many parameters this program can - // usefully ask for, or better, it turned - // out: declaring and querying these - // parameters was already done centralized in - // one place of the libray, namely the - // DataOutInterface class that handles - // exactly this -- managing parameters for - // input and output. - // - // So the second function call in this - // function is to let the - // DataOutInterface declare a good number - // of parameters that control everything from - // the output format to what kind of output - // should be generated if output is written - // in a specific graphical format. For - // example, when writing data in encapsulated - // postscript (EPS) format, the result is - // just a 2d projection, not data that can be - // viewed and rotated with a - // viewer. Therefore, one has to choose the - // viewing angle and a number of other - // options up front, when output is - // generated, rather than playing around with - // them later on. The call to - // DataOutInterface::declare_parameters - // declares entries that allow to specify - // them in the parameter input file during - // run-time. If the parameter file does not - // contain entries for them, defaults are - // taken. - // - // As a final note: DataOutInterface is a - // template, because it is usually used to - // write output for a specific space - // dimension. However, this program is - // supposed to be used for all dimensions at - // the same time, so we don't know at compile - // time what the right dimension is when - // specifying the template - // parameter. Fortunately, declaring - // parameters is something that is space - // dimension independent, so we can just pick - // one arbitrarily. We pick 1, but it - // could have been any other number as well. + // @sect4{Declaring parameters for the input file} + + // The second function is used to declare the + // parameters this program accepts from the + // input file. While we don't actually take + // many parameters from the input file except + // for, possibly, the output file name and + // format, we nevertheless want to show how + // to work with parameter files. + // + // In short, the ParameterHandler class + // works as follows: one declares the entries + // of parameters that can be given in input + // files together, and later on one can read + // an input file in which these parameters + // are set to their values. If a parameter is + // not listed in the input file, the default + // value specified in the declaration of that + // parameter is used. After that, the program + // can query the values assigned to certain + // parameters from the ParameterHandler + // object. + // + // Declaring parameters can be done using the + // ParameterHandler::declare_entry + // function. It's arguments are the name of a + // parameter, a default value (given as a + // string, even if the parameter is numeric + // in nature, and thirdly an object that + // describes constraints on values that may + // be passed to this parameter. In the + // example below, we use an object of type + // Patterns::Anything to denote that + // there are no constraints on file names + // (this is, of course, not true -- the + // operating system does have constraints, + // but from an application standpoint, almost + // all names are valid). In other cases, one + // may, for example, use + // Patterns::Integer to make sure that + // only parameters are accepted that can be + // interpreted as integer values (it is also + // possible to specify bounds for integer + // values, and all values outside this range + // are rejected), Patterns::Double for + // floating point values, classes that make + // sure that the given parameter value is a + // comma separated list of things, etc. Take + // a look at the Patterns namespace to + // see what is possible. + // + // The fourth argument to declare_entry + // is a help string that can be printed to + // document what this parameter is meant to + // be used for and other information you may + // consider important when declaring this + // parameter. The default value of this + // fourth argument is the empty string. + // + // I always wanted to have an example program + // describing the ParameterHandler class, + // because it is so particularly useful. It + // would have been useful in a number of + // previous example programs (for example, in + // order to let the tolerance for linear + // solvers, or the number of refinement steps + // be determined by a run-time parameter, + // rather than hard-coding them into the + // program), but it turned out that trying to + // explain this class there would have + // overloaded them with things that would + // have distracted from the main + // purpose. However, while writing this + // program, I realized that there aren't all + // that many parameters this program can + // usefully ask for, or better, it turned + // out: declaring and querying these + // parameters was already done centralized in + // one place of the libray, namely the + // DataOutInterface class that handles + // exactly this -- managing parameters for + // input and output. + // + // So the second function call in this + // function is to let the + // DataOutInterface declare a good number + // of parameters that control everything from + // the output format to what kind of output + // should be generated if output is written + // in a specific graphical format. For + // example, when writing data in encapsulated + // postscript (EPS) format, the result is + // just a 2d projection, not data that can be + // viewed and rotated with a + // viewer. Therefore, one has to choose the + // viewing angle and a number of other + // options up front, when output is + // generated, rather than playing around with + // them later on. The call to + // DataOutInterface::declare_parameters + // declares entries that allow to specify + // them in the parameter input file during + // run-time. If the parameter file does not + // contain entries for them, defaults are + // taken. + // + // As a final note: DataOutInterface is a + // template, because it is usually used to + // write output for a specific space + // dimension. However, this program is + // supposed to be used for all dimensions at + // the same time, so we don't know at compile + // time what the right dimension is when + // specifying the template + // parameter. Fortunately, declaring + // parameters is something that is space + // dimension independent, so we can just pick + // one arbitrarily. We pick 1, but it + // could have been any other number as well. void declare_parameters () { prm.declare_entry ("Output file", "", @@ -233,44 +233,44 @@ namespace Step19 DataOutInterface<1>::declare_parameters (prm); - // Since everything that this program can - // usefully request in terms of input - // parameters is already handled by now, - // let us nevertheless show how to use - // input parameters in other - // circumstances. First, parameters are - // like files in a directory tree: they can - // be in the top-level directory, but you - // can also group them into subdirectories - // to make it easier to find them or to be - // able to use the same parameter name in - // different contexts. - // - // Let us first declare a dummy parameter - // in the top-level section; we assume that - // it will denote the number of iterations, - // and that useful numbers of iterations - // that a user should be able to specify - // are in the range 1...1000, with a - // default value of 42: + // Since everything that this program can + // usefully request in terms of input + // parameters is already handled by now, + // let us nevertheless show how to use + // input parameters in other + // circumstances. First, parameters are + // like files in a directory tree: they can + // be in the top-level directory, but you + // can also group them into subdirectories + // to make it easier to find them or to be + // able to use the same parameter name in + // different contexts. + // + // Let us first declare a dummy parameter + // in the top-level section; we assume that + // it will denote the number of iterations, + // and that useful numbers of iterations + // that a user should be able to specify + // are in the range 1...1000, with a + // default value of 42: prm.declare_entry ("Dummy iterations", "42", Patterns::Integer (1,1000), "A dummy parameter asking for an integer"); - // Next, let us declare a sub-section (the - // equivalent to a subdirectory). When - // entered, all following parameter - // declarations will be within this - // subsection. To also visually group these - // declarations with the subsection name, I - // like to use curly braces to force my - // editor to indent everything that goes - // into this sub-section by one level of - // indentation. In this sub-section, we - // shall have two entries, one that takes a - // boolean parameter and one that takes a - // selection list of values, separated by - // the '|' character: + // Next, let us declare a sub-section (the + // equivalent to a subdirectory). When + // entered, all following parameter + // declarations will be within this + // subsection. To also visually group these + // declarations with the subsection name, I + // like to use curly braces to force my + // editor to indent everything that goes + // into this sub-section by one level of + // indentation. In this sub-section, we + // shall have two entries, one that takes a + // boolean parameter and one that takes a + // selection list of values, separated by + // the '|' character: prm.enter_subsection ("Dummy subsection"); { prm.declare_entry ("Dummy generate output", "true", @@ -284,40 +284,40 @@ namespace Step19 "set of values"); } prm.leave_subsection (); - // After this, we have left the subsection - // again. You should have gotten the idea - // by now how one can nest subsections to - // separate parameters. There are a number - // of other possible patterns describing - // possible values of parameters; in all - // cases, if you try to pass a parameter to - // the program that does not match the - // expectations of the pattern, it will - // reject the parameter file and ask you to - // fix it. After all, it does not make much - // sense if you had an entry that contained - // the entry "red" for the parameter - // "Generate output". + // After this, we have left the subsection + // again. You should have gotten the idea + // by now how one can nest subsections to + // separate parameters. There are a number + // of other possible patterns describing + // possible values of parameters; in all + // cases, if you try to pass a parameter to + // the program that does not match the + // expectations of the pattern, it will + // reject the parameter file and ask you to + // fix it. After all, it does not make much + // sense if you had an entry that contained + // the entry "red" for the parameter + // "Generate output". } - // @sect4{Parsing the command line} - - // Our next task is to see what information - // has been provided on the command - // line. First, we need to be sure that there - // is at least one parameter: an input - // file. The format and the output file can - // be specified in the parameter file, but - // the list of input files can't, so at least - // one parameter needs to be there. Together - // with the name of the program (the zeroth - // parameter), argc must therefore be at - // least 2. If this is not the case, we print - // an error message and exit: + // @sect4{Parsing the command line} + + // Our next task is to see what information + // has been provided on the command + // line. First, we need to be sure that there + // is at least one parameter: an input + // file. The format and the output file can + // be specified in the parameter file, but + // the list of input files can't, so at least + // one parameter needs to be there. Together + // with the name of the program (the zeroth + // parameter), argc must therefore be at + // least 2. If this is not the case, we print + // an error message and exit: void parse_command_line (const int argc, - char *const * argv) + char *const *argv) { if (argc < 2) { @@ -325,27 +325,27 @@ namespace Step19 exit (1); } - // Next, collect all parameters in a list - // that will be somewhat simpler to handle - // than the argc/argv mechanism. We - // omit the name of the executable at the - // zeroth index: + // Next, collect all parameters in a list + // that will be somewhat simpler to handle + // than the argc/argv mechanism. We + // omit the name of the executable at the + // zeroth index: std::list args; for (int i=1; i-p, then there must be a - // parameter file following (which - // we should then read), in case of - // -x it is the name of an - // output format. Finally, for - // -o it is the name of the - // output file. In all cases, once - // we've treated a parameter, we - // remove it from the list of - // parameters: + // Then process all these + // parameters. If the parameter is + // -p, then there must be a + // parameter file following (which + // we should then read), in case of + // -x it is the name of an + // output format. Finally, for + // -o it is the name of the + // output file. In all cases, once + // we've treated a parameter, we + // remove it from the list of + // parameters: while (args.size()) { if (args.front() == std::string("-p")) @@ -362,52 +362,52 @@ namespace Step19 const std::string parameter_file = args.front (); args.pop_front (); - // Now read the input file: + // Now read the input file: prm.read_input (parameter_file); - // Both the output file name as - // well as the format can be - // specified on the command - // line. We have therefore given - // them global variables that hold - // their values, but they can also - // be set in the parameter file. We - // therefore need to extract them - // from the parameter file here, - // because they may be overridden - // by later command line - // parameters: + // Both the output file name as + // well as the format can be + // specified on the command + // line. We have therefore given + // them global variables that hold + // their values, but they can also + // be set in the parameter file. We + // therefore need to extract them + // from the parameter file here, + // because they may be overridden + // by later command line + // parameters: if (output_file == "") output_file = prm.get ("Output file"); if (output_format == "") output_format = prm.get ("Output format"); - // Finally, let us note that if we - // were interested in the values of - // the parameters declared above in - // the dummy subsection, we would - // write something like this to - // extract the value of the boolean - // flag (the prm.get function - // returns the value of a parameter - // as a string, whereas the - // prm.get_X functions return a - // value already converted to a - // different type): + // Finally, let us note that if we + // were interested in the values of + // the parameters declared above in + // the dummy subsection, we would + // write something like this to + // extract the value of the boolean + // flag (the prm.get function + // returns the value of a parameter + // as a string, whereas the + // prm.get_X functions return a + // value already converted to a + // different type): prm.enter_subsection ("Dummy subsection"); { prm.get_bool ("Dummy generate output"); } prm.leave_subsection (); - // We would assign the result to a - // variable, or course, but don't - // here in order not to generate an - // unused variable that the - // compiler might warn about. - // - // Alas, let's move on to handling - // of output formats: + // We would assign the result to a + // variable, or course, but don't + // here in order not to generate an + // unused variable that the + // compiler might warn about. + // + // Alas, let's move on to handling + // of output formats: } else if (args.front() == std::string("-x")) { @@ -438,12 +438,12 @@ namespace Step19 args.pop_front (); } - // Otherwise, this is not a parameter - // that starts with a known minus - // sequence, and we should consider it - // to be the name of an input file. Let - // us therefore add this file to the - // list of input files: + // Otherwise, this is not a parameter + // that starts with a known minus + // sequence, and we should consider it + // to be the name of an input file. Let + // us therefore add this file to the + // list of input files: else { input_file_names.push_back (args.front()); @@ -451,9 +451,9 @@ namespace Step19 } } - // Next check a few things and create - // errors if the checks fail. Firstly, - // there must be at least one input file + // Next check a few things and create + // errors if the checks fail. Firstly, + // there must be at least one input file if (input_file_names.size() == 0) { std::cerr << "Error: No input file specified." << std::endl; @@ -463,26 +463,26 @@ namespace Step19 } - // @sect4{Generating output} - - // Now that we have all the information, we - // need to read all the input files, merge - // them, and generate a single output - // file. This, after all, was the motivation, - // borne from the necessity encountered in - // the step-18 tutorial program, to write - // this program in the first place. - // - // So what we do first is to declare an - // object into which we will merge the data - // from all the input file, and read in the - // first file through a stream. Note that - // every time we open a file, we use the - // AssertThrow macro to check whether the - // file is really readable -- if it isn't - // then this will trigger an exception and - // corresponding output will be generated - // from the exception handler in main(): + // @sect4{Generating output} + + // Now that we have all the information, we + // need to read all the input files, merge + // them, and generate a single output + // file. This, after all, was the motivation, + // borne from the necessity encountered in + // the step-18 tutorial program, to write + // this program in the first place. + // + // So what we do first is to declare an + // object into which we will merge the data + // from all the input file, and read in the + // first file through a stream. Note that + // every time we open a file, we use the + // AssertThrow macro to check whether the + // file is really readable -- if it isn't + // then this will trigger an exception and + // corresponding output will be generated + // from the exception handler in main(): template void do_convert () { @@ -495,10 +495,10 @@ namespace Step19 merged_data.read (input); } - // For all the other input files, we read - // their data into an intermediate object, - // and then merge that into the first - // object declared above: + // For all the other input files, we read + // their data into an intermediate object, + // and then merge that into the first + // object declared above: for (unsigned int i=1; iDataOutBase class has a function - // that does this parsing for us, i.e. it - // knows about all the presently supported - // output formats and makes sure that they - // can be specified in the parameter file - // or on the command line. Note that this - // ensures that if the library acquires the - // ability to output in other output - // formats, this program will be able to - // make use of this ability without having - // to be changed! + // Once we have this, let us open an output + // stream, and parse what we got as the + // name of the output format into an + // identifier. Fortunately, the + // DataOutBase class has a function + // that does this parsing for us, i.e. it + // knows about all the presently supported + // output formats and makes sure that they + // can be specified in the parameter file + // or on the command line. Note that this + // ensures that if the library acquires the + // ability to output in other output + // formats, this program will be able to + // make use of this ability without having + // to be changed! std::ofstream output_stream (output_file.c_str()); AssertThrow (output_stream, ExcIO()); const DataOutBase::OutputFormat format = DataOutBase::parse_output_format (output_format); - // Finally, write the merged data to the - // output: + // Finally, write the merged data to the + // output: merged_data.write(output_stream, format); } - // @sect4{Dispatching output generation} - - // The function above takes template - // parameters relating to the space dimension - // of the output, and the dimension of the - // objects to be output. (For example, when - // outputting whole cells, these two - // dimensions are the same, but the - // intermediate files may contain only data - // pertaining to the faces of cells, in which - // case the first parameter will be one less - // than the space dimension.) - // - // The problem is: at compile time, we of - // course don't know the dimensions used in - // the input files. We have to plan for all - // cases, therefore. This is a little clumsy, - // since we need to specify the dimensions - // statically at compile time, even though we - // will only know about them at run time. - // - // So here is what we do: from the first - // input file, we determine (using a function - // in DataOutBase that exists for this - // purpose) these dimensions. We then have a - // series of switches that dispatch, - // statically, to the do_convert - // functions with different template - // arguments. Not pretty, but works. Apart - // from this, the function does nothing -- - // except making sure that it covered the - // dimensions for which it was called, using - // the AssertThrow macro at places in the - // code that shouldn't be reached: + // @sect4{Dispatching output generation} + + // The function above takes template + // parameters relating to the space dimension + // of the output, and the dimension of the + // objects to be output. (For example, when + // outputting whole cells, these two + // dimensions are the same, but the + // intermediate files may contain only data + // pertaining to the faces of cells, in which + // case the first parameter will be one less + // than the space dimension.) + // + // The problem is: at compile time, we of + // course don't know the dimensions used in + // the input files. We have to plan for all + // cases, therefore. This is a little clumsy, + // since we need to specify the dimensions + // statically at compile time, even though we + // will only know about them at run time. + // + // So here is what we do: from the first + // input file, we determine (using a function + // in DataOutBase that exists for this + // purpose) these dimensions. We then have a + // series of switches that dispatch, + // statically, to the do_convert + // functions with different template + // arguments. Not pretty, but works. Apart + // from this, the function does nothing -- + // except making sure that it covered the + // dimensions for which it was called, using + // the AssertThrow macro at places in the + // code that shouldn't be reached: void convert () { AssertThrow (input_file_names.size() > 0, @@ -579,44 +579,44 @@ namespace Step19 AssertThrow (input, ExcIO()); const std::pair - dimensions = DataOutBase::determine_intermediate_format_dimensions (input); + dimensions = DataOutBase::determine_intermediate_format_dimensions (input); switch (dimensions.first) { - case 1: - switch (dimensions.second) - { - case 1: - do_convert <1,1> (); - return; - - case 2: - do_convert <1,2> (); - return; - } - AssertThrow (false, ExcNotImplemented()); - - case 2: - switch (dimensions.second) - { - case 2: - do_convert <2,2> (); - return; - - case 3: - do_convert <2,3> (); - return; - } - AssertThrow (false, ExcNotImplemented()); - - case 3: - switch (dimensions.second) - { - case 3: - do_convert <3,3> (); - return; - } - AssertThrow (false, ExcNotImplemented()); + case 1: + switch (dimensions.second) + { + case 1: + do_convert <1,1> (); + return; + + case 2: + do_convert <1,2> (); + return; + } + AssertThrow (false, ExcNotImplemented()); + + case 2: + switch (dimensions.second) + { + case 2: + do_convert <2,2> (); + return; + + case 3: + do_convert <2,3> (); + return; + } + AssertThrow (false, ExcNotImplemented()); + + case 3: + switch (dimensions.second) + { + case 3: + do_convert <3,3> (); + return; + } + AssertThrow (false, ExcNotImplemented()); } AssertThrow (false, ExcNotImplemented()); @@ -625,19 +625,19 @@ namespace Step19 - // @sect4{main()} +// @sect4{main()} - // Finally, the main program. There is not - // much more to do than to make sure - // parameters are declared, the command line - // is parsed (which includes reading - // parameter files), and finally making sure - // the input files are read and output is - // generated. Everything else just has to do - // with handling exceptions and making sure - // that appropriate output is generated if - // one is thrown. -int main (int argc, char ** argv) +// Finally, the main program. There is not +// much more to do than to make sure +// parameters are declared, the command line +// is parsed (which includes reading +// parameter files), and finally making sure +// the input files are read and output is +// generated. Everything else just has to do +// with handling exceptions and making sure +// that appropriate output is generated if +// one is thrown. +int main (int argc, char **argv) { try { diff --git a/deal.II/examples/step-2/step-2.cc b/deal.II/examples/step-2/step-2.cc index fc9110a46f..d018ba2c78 100644 --- a/deal.II/examples/step-2/step-2.cc +++ b/deal.II/examples/step-2/step-2.cc @@ -9,91 +9,91 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // The first few includes are just - // like in the previous program, so - // do not require additional comments: +// The first few includes are just +// like in the previous program, so +// do not require additional comments: #include #include #include #include #include - // However, the next file is new. We need - // this include file for the association of - // degrees of freedom ("DoF"s) to vertices, - // lines, and cells: +// However, the next file is new. We need +// this include file for the association of +// degrees of freedom ("DoF"s) to vertices, +// lines, and cells: #include - // The following include contains the - // description of the bilinear finite - // element, including the facts that - // it has one degree of freedom on - // each vertex of the triangulation, - // but none on faces and none in the - // interior of the cells. - // - // (In fact, the file contains the - // description of Lagrange elements in - // general, i.e. also the quadratic, cubic, - // etc versions, and not only for 2d but also - // 1d and 3d.) +// The following include contains the +// description of the bilinear finite +// element, including the facts that +// it has one degree of freedom on +// each vertex of the triangulation, +// but none on faces and none in the +// interior of the cells. +// +// (In fact, the file contains the +// description of Lagrange elements in +// general, i.e. also the quadratic, cubic, +// etc versions, and not only for 2d but also +// 1d and 3d.) #include - // In the following file, several - // tools for manipulating degrees of - // freedom can be found: +// In the following file, several +// tools for manipulating degrees of +// freedom can be found: #include - // We will use a sparse matrix to - // visualize the pattern of nonzero - // entries resulting from the - // distribution of degrees of freedom - // on the grid. That class can be - // found here: +// We will use a sparse matrix to +// visualize the pattern of nonzero +// entries resulting from the +// distribution of degrees of freedom +// on the grid. That class can be +// found here: #include - // We will also need to use an - // intermediate sparsity patter - // structure, which is found in this - // file: +// We will also need to use an +// intermediate sparsity patter +// structure, which is found in this +// file: #include - // We will want to use a special - // algorithm to renumber degrees of - // freedom. It is declared here: +// We will want to use a special +// algorithm to renumber degrees of +// freedom. It is declared here: #include - // And this is again needed for C++ output: +// And this is again needed for C++ output: #include - // Finally, as in step-1, we import - // the deal.II namespace into the - // global scope: +// Finally, as in step-1, we import +// the deal.II namespace into the +// global scope: using namespace dealii; - // @sect3{Mesh generation} - - // This is the function that produced the - // circular grid in the previous step-1 - // example program. The sole difference is - // that it returns the grid it produces via - // its argument. - // - // The details of what the function does are - // explained in step-1. The only thing we - // would like to comment on is this: - // - // Since we want to export the triangulation - // through this function's parameter, we need - // to make sure that the boundary object - // lives at least as long as the - // triangulation does. However, in step-1, - // the boundary object is a local variable, - // and it would be deleted at the end of the - // function, which is too early. We avoid the - // problem by declaring it 'static' which - // makes sure that the object is initialized - // the first time control the program passes - // this point, but at the same time assures - // that it lives until the end of the - // program. +// @sect3{Mesh generation} + +// This is the function that produced the +// circular grid in the previous step-1 +// example program. The sole difference is +// that it returns the grid it produces via +// its argument. +// +// The details of what the function does are +// explained in step-1. The only thing we +// would like to comment on is this: +// +// Since we want to export the triangulation +// through this function's parameter, we need +// to make sure that the boundary object +// lives at least as long as the +// triangulation does. However, in step-1, +// the boundary object is a local variable, +// and it would be deleted at the end of the +// function, which is too early. We avoid the +// problem by declaring it 'static' which +// makes sure that the object is initialized +// the first time control the program passes +// this point, but at the same time assures +// that it lives until the end of the +// program. void make_grid (Triangulation<2> &triangulation) { const Point<2> center (1,0); @@ -109,8 +109,8 @@ void make_grid (Triangulation<2> &triangulation) for (unsigned int step=0; step<5; ++step) { Triangulation<2>::active_cell_iterator - cell = triangulation.begin_active(), - endc = triangulation.end(); + cell = triangulation.begin_active(), + endc = triangulation.end(); for (; cell!=endc; ++cell) for (unsigned int v=0; @@ -131,259 +131,259 @@ void make_grid (Triangulation<2> &triangulation) } } - // @sect3{Creation of a DoFHandler} - - // Up to now, we only have a grid, i.e. some - // geometrical (the position of the vertices) - // and some topological information (how - // vertices are connected to lines, and lines - // to cells, as well as which cells neighbor - // which other cells). To use numerical - // algorithms, one needs some logic - // information in addition to that: we would - // like to associate degree of freedom - // numbers to each vertex (or line, or cell, - // in case we were using higher order - // elements) to later generate matrices and - // vectors which describe a finite element - // field on the triangulation. - // - // This function shows how to do this. The - // object to consider is the DoFHandler - // class template. Before we do so, however, - // we first need something that describes how - // many degrees of freedom are to be - // associated to each of these objects. Since - // this is one aspect of the definition of a - // finite element space, the finite element - // base class stores this information. In the - // present context, we therefore create an - // object of the derived class FE_Q that - // describes Lagrange elements. Its - // constructor takes one argument that states - // the polynomial degree of the element, - // which here is one (indicating a bi-linear - // element); this then corresponds to one - // degree of freedom for each vertex, while - // there are none on lines and inside the - // quadrilateral. A value of, say, three - // given to the constructor would instead - // give us a bi-cubic element with one degree - // of freedom per vertex, two per line, and - // four inside the cell. In general, FE_Q - // denotes the family of continuous elements - // with complete polynomials - // (i.e. tensor-product polynomials) up to - // the specified order. - // - // We first need to create an object of this - // class and then pass it on to the - // DoFHandler object to allocate storage - // for the degrees of freedom (in deal.II - // lingo: we distribute degrees of - // freedom). Note that the DoFHandler - // object will store a reference to this - // finite element object, so we have to - // make sure its lifetime is at least as long - // as that of the DoFHandler; one way to - // make sure this is so is to make it static - // as well, in order to prevent its - // preemptive destruction. (However, the - // library would warn us if we forgot about - // this and abort the program if that - // occured. You can check this, if you want, - // by removing the 'static' declaration.) +// @sect3{Creation of a DoFHandler} + +// Up to now, we only have a grid, i.e. some +// geometrical (the position of the vertices) +// and some topological information (how +// vertices are connected to lines, and lines +// to cells, as well as which cells neighbor +// which other cells). To use numerical +// algorithms, one needs some logic +// information in addition to that: we would +// like to associate degree of freedom +// numbers to each vertex (or line, or cell, +// in case we were using higher order +// elements) to later generate matrices and +// vectors which describe a finite element +// field on the triangulation. +// +// This function shows how to do this. The +// object to consider is the DoFHandler +// class template. Before we do so, however, +// we first need something that describes how +// many degrees of freedom are to be +// associated to each of these objects. Since +// this is one aspect of the definition of a +// finite element space, the finite element +// base class stores this information. In the +// present context, we therefore create an +// object of the derived class FE_Q that +// describes Lagrange elements. Its +// constructor takes one argument that states +// the polynomial degree of the element, +// which here is one (indicating a bi-linear +// element); this then corresponds to one +// degree of freedom for each vertex, while +// there are none on lines and inside the +// quadrilateral. A value of, say, three +// given to the constructor would instead +// give us a bi-cubic element with one degree +// of freedom per vertex, two per line, and +// four inside the cell. In general, FE_Q +// denotes the family of continuous elements +// with complete polynomials +// (i.e. tensor-product polynomials) up to +// the specified order. +// +// We first need to create an object of this +// class and then pass it on to the +// DoFHandler object to allocate storage +// for the degrees of freedom (in deal.II +// lingo: we distribute degrees of +// freedom). Note that the DoFHandler +// object will store a reference to this +// finite element object, so we have to +// make sure its lifetime is at least as long +// as that of the DoFHandler; one way to +// make sure this is so is to make it static +// as well, in order to prevent its +// preemptive destruction. (However, the +// library would warn us if we forgot about +// this and abort the program if that +// occured. You can check this, if you want, +// by removing the 'static' declaration.) void distribute_dofs (DoFHandler<2> &dof_handler) { - // As described above, let us first create - // a finite element object, and then use it - // to allocate degrees of freedom on the - // triangulation with which the dof_handler - // object is associated: + // As described above, let us first create + // a finite element object, and then use it + // to allocate degrees of freedom on the + // triangulation with which the dof_handler + // object is associated: static const FE_Q<2> finite_element(1); dof_handler.distribute_dofs (finite_element); - // Now that we have associated a degree of - // freedom with a global number to each - // vertex, we wonder how to visualize this? - // There is no simple way to directly - // visualize the DoF number associated with - // each vertex. However, such information - // would hardly ever be truly important, - // since the numbering itself is more or - // less arbitrary. There are more important - // factors, of which we will demonstrate - // one in the following. - // - // Associated with each vertex of the - // triangulation is a shape - // function. Assume we want to solve - // something like Laplace's equation, then - // the different matrix entries will be the - // integrals over the gradient of each pair - // of such shape functions. Obviously, - // since the shape functions are nonzero - // only on the cells adjacent to the vertex - // they are associated with, matrix entries - // will be nonzero only if the supports of - // the shape functions associated to that - // column and row %numbers intersect. This - // is only the case for adjacent shape - // functions, and therefore only for - // adjacent vertices. Now, since the - // vertices are numbered more or less - // randomly by the above function - // (DoFHandler::distribute_dofs), the - // pattern of nonzero entries in the matrix - // will be somewhat ragged, and we will - // take a look at it now. - // - // First we have to create a - // structure which we use to store - // the places of nonzero - // elements. This can then later be - // used by one or more sparse - // matrix objects that store the - // values of the entries in the - // locations stored by this - // sparsity pattern. The class that - // stores the locations is the - // SparsityPattern class. As it - // turns out, however, this class - // has some drawbacks when we try - // to fill it right away: its data - // structures are set up in such a - // way that we need to have an - // estimate for the maximal number - // of entries we may wish to have - // in each row. In two space - // dimensions, reasonable values - // for this estimate are available - // through the - // DoFHandler::max_couplings_between_dofs() - // function, but in three - // dimensions the function almost - // always severely overestimates - // the true number, leading to a - // lot of wasted memory, sometimes - // too much for the machine used, - // even if the unused memory can be - // released immediately after - // computing the sparsity - // pattern. In order to avoid this, - // we use an intermediate object of - // type CompressedSparsityPattern - // that uses a different %internal - // data structure and that we can - // later copy into the - // SparsityPattern object without - // much overhead. (Some more - // information on these data - // structures can be found in the - // @ref Sparsity module.) In order - // to initialize this intermediate - // data structure, we have to give - // it the size of the matrix, which - // in our case will be square with - // as many rows and columns as - // there are degrees of freedom on - // the grid: + // Now that we have associated a degree of + // freedom with a global number to each + // vertex, we wonder how to visualize this? + // There is no simple way to directly + // visualize the DoF number associated with + // each vertex. However, such information + // would hardly ever be truly important, + // since the numbering itself is more or + // less arbitrary. There are more important + // factors, of which we will demonstrate + // one in the following. + // + // Associated with each vertex of the + // triangulation is a shape + // function. Assume we want to solve + // something like Laplace's equation, then + // the different matrix entries will be the + // integrals over the gradient of each pair + // of such shape functions. Obviously, + // since the shape functions are nonzero + // only on the cells adjacent to the vertex + // they are associated with, matrix entries + // will be nonzero only if the supports of + // the shape functions associated to that + // column and row %numbers intersect. This + // is only the case for adjacent shape + // functions, and therefore only for + // adjacent vertices. Now, since the + // vertices are numbered more or less + // randomly by the above function + // (DoFHandler::distribute_dofs), the + // pattern of nonzero entries in the matrix + // will be somewhat ragged, and we will + // take a look at it now. + // + // First we have to create a + // structure which we use to store + // the places of nonzero + // elements. This can then later be + // used by one or more sparse + // matrix objects that store the + // values of the entries in the + // locations stored by this + // sparsity pattern. The class that + // stores the locations is the + // SparsityPattern class. As it + // turns out, however, this class + // has some drawbacks when we try + // to fill it right away: its data + // structures are set up in such a + // way that we need to have an + // estimate for the maximal number + // of entries we may wish to have + // in each row. In two space + // dimensions, reasonable values + // for this estimate are available + // through the + // DoFHandler::max_couplings_between_dofs() + // function, but in three + // dimensions the function almost + // always severely overestimates + // the true number, leading to a + // lot of wasted memory, sometimes + // too much for the machine used, + // even if the unused memory can be + // released immediately after + // computing the sparsity + // pattern. In order to avoid this, + // we use an intermediate object of + // type CompressedSparsityPattern + // that uses a different %internal + // data structure and that we can + // later copy into the + // SparsityPattern object without + // much overhead. (Some more + // information on these data + // structures can be found in the + // @ref Sparsity module.) In order + // to initialize this intermediate + // data structure, we have to give + // it the size of the matrix, which + // in our case will be square with + // as many rows and columns as + // there are degrees of freedom on + // the grid: CompressedSparsityPattern compressed_sparsity_pattern(dof_handler.n_dofs(), dof_handler.n_dofs()); - // We then fill this object with the - // places where nonzero elements will be - // located given the present numbering of - // degrees of freedom: + // We then fill this object with the + // places where nonzero elements will be + // located given the present numbering of + // degrees of freedom: DoFTools::make_sparsity_pattern (dof_handler, compressed_sparsity_pattern); - // Now we are ready to create the actual - // sparsity pattern that we could later use - // for our matrix. It will just contain the - // data already assembled in the - // CompressedSparsityPattern. + // Now we are ready to create the actual + // sparsity pattern that we could later use + // for our matrix. It will just contain the + // data already assembled in the + // CompressedSparsityPattern. SparsityPattern sparsity_pattern; sparsity_pattern.copy_from (compressed_sparsity_pattern); - // With this, we can now write the results - // to a file: + // With this, we can now write the results + // to a file: std::ofstream out ("sparsity_pattern.1"); sparsity_pattern.print_gnuplot (out); - // The result is in GNUPLOT format, - // where in each line of the output - // file, the coordinates of one - // nonzero entry are listed. The - // output will be shown below. - // - // If you look at it, you will note that - // the sparsity pattern is symmetric. This - // should not come as a surprise, since we - // have not given the - // DoFTools::make_sparsity_pattern any - // information that would indicate that our - // bilinear form may couple shape functions - // in a non-symmetric way. You will also - // note that it has several distinct - // region, which stem from the fact that - // the numbering starts from the coarsest - // cells and moves on to the finer ones; - // since they are all distributed - // symmetrically around the origin, this - // shows up again in the sparsity pattern. + // The result is in GNUPLOT format, + // where in each line of the output + // file, the coordinates of one + // nonzero entry are listed. The + // output will be shown below. + // + // If you look at it, you will note that + // the sparsity pattern is symmetric. This + // should not come as a surprise, since we + // have not given the + // DoFTools::make_sparsity_pattern any + // information that would indicate that our + // bilinear form may couple shape functions + // in a non-symmetric way. You will also + // note that it has several distinct + // region, which stem from the fact that + // the numbering starts from the coarsest + // cells and moves on to the finer ones; + // since they are all distributed + // symmetrically around the origin, this + // shows up again in the sparsity pattern. } - // @sect3{Renumbering of DoFs} - - // In the sparsity pattern produced above, - // the nonzero entries extended quite far off - // from the diagonal. For some algorithms, - // for example for incomplete LU - // decompositions or Gauss-Seidel - // preconditioners, this is unfavorable, and - // we will show a simple way how to improve - // this situation. - // - // Remember that for an entry $(i,j)$ - // in the matrix to be nonzero, the - // supports of the shape functions i - // and j needed to intersect - // (otherwise in the integral, the - // integrand would be zero everywhere - // since either the one or the other - // shape function is zero at some - // point). However, the supports of - // shape functions intersected only - // if they were adjacent to each - // other, so in order to have the - // nonzero entries clustered around - // the diagonal (where $i$ equals $j$), - // we would like to have adjacent - // shape functions to be numbered - // with indices (DoF numbers) that - // differ not too much. - // - // This can be accomplished by a - // simple front marching algorithm, - // where one starts at a given vertex - // and gives it the index zero. Then, - // its neighbors are numbered - // successively, making their indices - // close to the original one. Then, - // their neighbors, if not yet - // numbered, are numbered, and so - // on. - // - // One algorithm that adds a little bit of - // sophistication along these lines is the - // one by Cuthill and McKee. We will use it - // in the following function to renumber the - // degrees of freedom such that the resulting - // sparsity pattern is more localized around - // the diagonal. The only interesting part of - // the function is the first call to - // DoFRenumbering::Cuthill_McKee, the - // rest is essentially as before: +// @sect3{Renumbering of DoFs} + +// In the sparsity pattern produced above, +// the nonzero entries extended quite far off +// from the diagonal. For some algorithms, +// for example for incomplete LU +// decompositions or Gauss-Seidel +// preconditioners, this is unfavorable, and +// we will show a simple way how to improve +// this situation. +// +// Remember that for an entry $(i,j)$ +// in the matrix to be nonzero, the +// supports of the shape functions i +// and j needed to intersect +// (otherwise in the integral, the +// integrand would be zero everywhere +// since either the one or the other +// shape function is zero at some +// point). However, the supports of +// shape functions intersected only +// if they were adjacent to each +// other, so in order to have the +// nonzero entries clustered around +// the diagonal (where $i$ equals $j$), +// we would like to have adjacent +// shape functions to be numbered +// with indices (DoF numbers) that +// differ not too much. +// +// This can be accomplished by a +// simple front marching algorithm, +// where one starts at a given vertex +// and gives it the index zero. Then, +// its neighbors are numbered +// successively, making their indices +// close to the original one. Then, +// their neighbors, if not yet +// numbered, are numbered, and so +// on. +// +// One algorithm that adds a little bit of +// sophistication along these lines is the +// one by Cuthill and McKee. We will use it +// in the following function to renumber the +// degrees of freedom such that the resulting +// sparsity pattern is more localized around +// the diagonal. The only interesting part of +// the function is the first call to +// DoFRenumbering::Cuthill_McKee, the +// rest is essentially as before: void renumber_dofs (DoFHandler<2> &dof_handler) { DoFRenumbering::Cuthill_McKee (dof_handler); @@ -399,44 +399,44 @@ void renumber_dofs (DoFHandler<2> &dof_handler) sparsity_pattern.print_gnuplot (out); } - // Again, the output is shown - // below. Note that the nonzero - // entries are clustered far better - // around the diagonal than - // before. This effect is even more - // distinguished for larger - // matrices (the present one has - // 1260 rows and columns, but large - // matrices often have several - // 100,000s). - - // It is worth noting that the - // DoFRenumbering class offers a number - // of other algorithms as well to renumber - // degrees of freedom. For example, it would - // of course be ideal if all couplings were - // in the lower or upper triangular part of a - // matrix, since then solving the linear - // system would among to only forward or - // backward substitution. This is of course - // unachievable for symmetric sparsity - // patterns, but in some special situations - // involving transport equations, this is - // possible by enumerating degrees of freedom - // from the inflow boundary along streamlines - // to the outflow boundary. Not surprisingly, - // DoFRenumbering also has algorithms for - // this. - - - // @sect3{The main function} - - // Finally, this is the main program. The - // only thing it does is to allocate and - // create the triangulation, then create a - // DoFHandler object and associate it to - // the triangulation, and finally call above - // two functions on it: +// Again, the output is shown +// below. Note that the nonzero +// entries are clustered far better +// around the diagonal than +// before. This effect is even more +// distinguished for larger +// matrices (the present one has +// 1260 rows and columns, but large +// matrices often have several +// 100,000s). + +// It is worth noting that the +// DoFRenumbering class offers a number +// of other algorithms as well to renumber +// degrees of freedom. For example, it would +// of course be ideal if all couplings were +// in the lower or upper triangular part of a +// matrix, since then solving the linear +// system would among to only forward or +// backward substitution. This is of course +// unachievable for symmetric sparsity +// patterns, but in some special situations +// involving transport equations, this is +// possible by enumerating degrees of freedom +// from the inflow boundary along streamlines +// to the outflow boundary. Not surprisingly, +// DoFRenumbering also has algorithms for +// this. + + +// @sect3{The main function} + +// Finally, this is the main program. The +// only thing it does is to allocate and +// create the triangulation, then create a +// DoFHandler object and associate it to +// the triangulation, and finally call above +// two functions on it: int main () { Triangulation<2> triangulation; diff --git a/deal.II/examples/step-20/step-20.cc b/deal.II/examples/step-20/step-20.cc index 8a3e1093f3..fd816b8be1 100644 --- a/deal.II/examples/step-20/step-20.cc +++ b/deal.II/examples/step-20/step-20.cc @@ -9,16 +9,16 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} - - // Since this program is only an - // adaptation of step-4, there is not - // much new stuff in terms of header - // files. In deal.II, we usually list - // include files in the order - // base-lac-grid-dofs-fe-numerics, - // followed by C++ standard include - // files: +// @sect3{Include files} + +// Since this program is only an +// adaptation of step-4, there is not +// much new stuff in terms of header +// files. In deal.II, we usually list +// include files in the order +// base-lac-grid-dofs-fe-numerics, +// followed by C++ standard include +// files: #include #include #include @@ -27,11 +27,11 @@ #include #include #include - // For our Schur complement solver, - // we need two new objects. One is a - // matrix object which acts as the - // inverse of a matrix by calling an - // iterative solver. +// For our Schur complement solver, +// we need two new objects. One is a +// matrix object which acts as the +// inverse of a matrix by calling an +// iterative solver. #include #include @@ -52,119 +52,119 @@ #include #include - // This is the only significant new - // header, namely the one in which - // the Raviart-Thomas finite element - // is declared: +// This is the only significant new +// header, namely the one in which +// the Raviart-Thomas finite element +// is declared: #include - // Finally, as a bonus in this - // program, we will use a tensorial - // coefficient. Since it may have a - // spatial dependence, we consider it - // a tensor-valued function. The - // following include file provides - // the TensorFunction class that - // offers such functionality: +// Finally, as a bonus in this +// program, we will use a tensorial +// coefficient. Since it may have a +// spatial dependence, we consider it +// a tensor-valued function. The +// following include file provides +// the TensorFunction class that +// offers such functionality: #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step20 { using namespace dealii; - // @sect3{The MixedLaplaceProblem class template} - - // Again, since this is an adaptation - // of step-6, the main class is - // almost the same as the one in that - // tutorial program. In terms of - // member functions, the main - // differences are that the - // constructor takes the degree of - // the Raviart-Thomas element as an - // argument (and that there is a - // corresponding member variable to - // store this value) and the addition - // of the compute_error function - // in which, no surprise, we will - // compute the difference between the - // exact and the numerical solution - // to determine convergence of our - // computations: + // @sect3{The MixedLaplaceProblem class template} + + // Again, since this is an adaptation + // of step-6, the main class is + // almost the same as the one in that + // tutorial program. In terms of + // member functions, the main + // differences are that the + // constructor takes the degree of + // the Raviart-Thomas element as an + // argument (and that there is a + // corresponding member variable to + // store this value) and the addition + // of the compute_error function + // in which, no surprise, we will + // compute the difference between the + // exact and the numerical solution + // to determine convergence of our + // computations: template class MixedLaplaceProblem { - public: - MixedLaplaceProblem (const unsigned int degree); - void run (); - - private: - void make_grid_and_dofs (); - void assemble_system (); - void solve (); - void compute_errors () const; - void output_results () const; - - const unsigned int degree; - - Triangulation triangulation; - FESystem fe; - DoFHandler dof_handler; - - // The second difference is that - // the sparsity pattern, the - // system matrix, and solution - // and right hand side vectors - // are now blocked. What this - // means and what one can do with - // such objects is explained in - // the introduction to this - // program as well as further - // down below when we explain the - // linear solvers and - // preconditioners for this - // problem: - BlockSparsityPattern sparsity_pattern; - BlockSparseMatrix system_matrix; - - BlockVector solution; - BlockVector system_rhs; + public: + MixedLaplaceProblem (const unsigned int degree); + void run (); + + private: + void make_grid_and_dofs (); + void assemble_system (); + void solve (); + void compute_errors () const; + void output_results () const; + + const unsigned int degree; + + Triangulation triangulation; + FESystem fe; + DoFHandler dof_handler; + + // The second difference is that + // the sparsity pattern, the + // system matrix, and solution + // and right hand side vectors + // are now blocked. What this + // means and what one can do with + // such objects is explained in + // the introduction to this + // program as well as further + // down below when we explain the + // linear solvers and + // preconditioners for this + // problem: + BlockSparsityPattern sparsity_pattern; + BlockSparseMatrix system_matrix; + + BlockVector solution; + BlockVector system_rhs; }; - // @sect3{Right hand side, boundary values, and exact solution} - - // Our next task is to define the - // right hand side of our problem - // (i.e., the scalar right hand side - // for the pressure in the original - // Laplace equation), boundary values - // for the pressure, as well as a - // function that describes both the - // pressure and the velocity of the - // exact solution for later - // computations of the error. Note - // that these functions have one, - // one, and dim+1 components, - // respectively, and that we pass the - // number of components down to the - // Function@ base class. For - // the exact solution, we only - // declare the function that actually - // returns the entire solution vector - // (i.e. all components of it) at - // once. Here are the respective - // declarations: + // @sect3{Right hand side, boundary values, and exact solution} + + // Our next task is to define the + // right hand side of our problem + // (i.e., the scalar right hand side + // for the pressure in the original + // Laplace equation), boundary values + // for the pressure, as well as a + // function that describes both the + // pressure and the velocity of the + // exact solution for later + // computations of the error. Note + // that these functions have one, + // one, and dim+1 components, + // respectively, and that we pass the + // number of components down to the + // Function@ base class. For + // the exact solution, we only + // declare the function that actually + // returns the entire solution vector + // (i.e. all components of it) at + // once. Here are the respective + // declarations: template class RightHandSide : public Function { - public: - RightHandSide () : Function(1) {} + public: + RightHandSide () : Function(1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -172,32 +172,32 @@ namespace Step20 template class PressureBoundaryValues : public Function { - public: - PressureBoundaryValues () : Function(1) {} + public: + PressureBoundaryValues () : Function(1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; template class ExactSolution : public Function { - public: - ExactSolution () : Function(dim+1) {} + public: + ExactSolution () : Function(dim+1) {} - virtual void vector_value (const Point &p, - Vector &value) const; + virtual void vector_value (const Point &p, + Vector &value) const; }; - // And then we also have to define - // these respective functions, of - // course. Given our discussion in - // the introduction of how the - // solution should look like, the - // following computations should be - // straightforward: + // And then we also have to define + // these respective functions, of + // course. Given our discussion in + // the introduction of how the + // solution should look like, the + // following computations should be + // straightforward: template double RightHandSide::value (const Point &/*p*/, const unsigned int /*component*/) const @@ -236,79 +236,79 @@ namespace Step20 - // @sect3{The inverse permeability tensor} - - // In addition to the other equation - // data, we also want to use a - // permeability tensor, or better -- - // because this is all that appears - // in the weak form -- the inverse of - // the permeability tensor, - // KInverse. For the purpose of - // verifying the exactness of the - // solution and determining - // convergence orders, this tensor is - // more in the way than helpful. We - // will therefore simply set it to - // the identity matrix. - // - // However, a spatially varying - // permeability tensor is - // indispensable in real-life porous - // media flow simulations, and we - // would like to use the opportunity - // to demonstrate the technique to - // use tensor valued functions. - // - // Possibly unsurprising, deal.II - // also has a base class not only for - // scalar and generally vector-valued - // functions (the Function base - // class) but also for functions that - // return tensors of fixed dimension - // and rank, the TensorFunction - // template. Here, the function under - // consideration returns a dim-by-dim - // matrix, i.e. a tensor of rank 2 - // and dimension dim. We then - // choose the template arguments of - // the base class appropriately. - // - // The interface that the - // TensorFunction class provides - // is essentially equivalent to the - // Function class. In particular, - // there exists a value_list - // function that takes a list of - // points at which to evaluate the - // function, and returns the values - // of the function in the second - // argument, a list of tensors: + // @sect3{The inverse permeability tensor} + + // In addition to the other equation + // data, we also want to use a + // permeability tensor, or better -- + // because this is all that appears + // in the weak form -- the inverse of + // the permeability tensor, + // KInverse. For the purpose of + // verifying the exactness of the + // solution and determining + // convergence orders, this tensor is + // more in the way than helpful. We + // will therefore simply set it to + // the identity matrix. + // + // However, a spatially varying + // permeability tensor is + // indispensable in real-life porous + // media flow simulations, and we + // would like to use the opportunity + // to demonstrate the technique to + // use tensor valued functions. + // + // Possibly unsurprising, deal.II + // also has a base class not only for + // scalar and generally vector-valued + // functions (the Function base + // class) but also for functions that + // return tensors of fixed dimension + // and rank, the TensorFunction + // template. Here, the function under + // consideration returns a dim-by-dim + // matrix, i.e. a tensor of rank 2 + // and dimension dim. We then + // choose the template arguments of + // the base class appropriately. + // + // The interface that the + // TensorFunction class provides + // is essentially equivalent to the + // Function class. In particular, + // there exists a value_list + // function that takes a list of + // points at which to evaluate the + // function, and returns the values + // of the function in the second + // argument, a list of tensors: template class KInverse : public TensorFunction<2,dim> { - public: - KInverse () : TensorFunction<2,dim>() {} + public: + KInverse () : TensorFunction<2,dim>() {} - virtual void value_list (const std::vector > &points, - std::vector > &values) const; + virtual void value_list (const std::vector > &points, + std::vector > &values) const; }; - // The implementation is less - // interesting. As in previous - // examples, we add a check to the - // beginning of the class to make - // sure that the sizes of input and - // output parameters are the same - // (see step-5 for a discussion of - // this technique). Then we loop over - // all evaluation points, and for - // each one first clear the output - // tensor and then set all its - // diagonal elements to one - // (i.e. fill the tensor with the - // identity matrix): + // The implementation is less + // interesting. As in previous + // examples, we add a check to the + // beginning of the class to make + // sure that the sizes of input and + // output parameters are the same + // (see step-5 for a discussion of + // this technique). Then we loop over + // all evaluation points, and for + // each one first clear the output + // tensor and then set all its + // diagonal elements to one + // (i.e. fill the tensor with the + // identity matrix): template void KInverse::value_list (const std::vector > &points, @@ -328,80 +328,80 @@ namespace Step20 - // @sect3{MixedLaplaceProblem class implementation} - - // @sect4{MixedLaplaceProblem::MixedLaplaceProblem} - - // In the constructor of this class, - // we first store the value that was - // passed in concerning the degree of - // the finite elements we shall use - // (a degree of zero, for example, - // means to use RT(0) and DG(0)), and - // then construct the vector valued - // element belonging to the space X_h - // described in the introduction. The - // rest of the constructor is as in - // the early tutorial programs. - // - // The only thing worth describing - // here is the constructor call of - // the fe variable. The - // FESystem class to which this - // variable belongs has a number of - // different constructors that all - // refer to binding simpler elements - // together into one larger - // element. In the present case, we - // want to couple a single RT(degree) - // element with a single DQ(degree) - // element. The constructor to - // FESystem that does this - // requires us to specity first the - // first base element (the - // FE_RaviartThomas object of - // given degree) and then the number - // of copies for this base element, - // and then similarly the kind and - // number of FE_DGQ - // elements. Note that the Raviart - // Thomas element already has dim - // vector components, so that the - // coupled element will have - // dim+1 vector components, the - // first dim of which correspond - // to the velocity variable whereas the - // last one corresponds to the - // pressure. - // - // It is also worth comparing the way - // we constructed this element from - // its base elements, with the way we - // have done so in step-8: there, we - // have built it as fe - // (FE_Q@(1), dim), i.e. we - // have simply used dim copies of - // the FE_Q(1) element, one copy - // for the displacement in each - // coordinate direction. + // @sect3{MixedLaplaceProblem class implementation} + + // @sect4{MixedLaplaceProblem::MixedLaplaceProblem} + + // In the constructor of this class, + // we first store the value that was + // passed in concerning the degree of + // the finite elements we shall use + // (a degree of zero, for example, + // means to use RT(0) and DG(0)), and + // then construct the vector valued + // element belonging to the space X_h + // described in the introduction. The + // rest of the constructor is as in + // the early tutorial programs. + // + // The only thing worth describing + // here is the constructor call of + // the fe variable. The + // FESystem class to which this + // variable belongs has a number of + // different constructors that all + // refer to binding simpler elements + // together into one larger + // element. In the present case, we + // want to couple a single RT(degree) + // element with a single DQ(degree) + // element. The constructor to + // FESystem that does this + // requires us to specity first the + // first base element (the + // FE_RaviartThomas object of + // given degree) and then the number + // of copies for this base element, + // and then similarly the kind and + // number of FE_DGQ + // elements. Note that the Raviart + // Thomas element already has dim + // vector components, so that the + // coupled element will have + // dim+1 vector components, the + // first dim of which correspond + // to the velocity variable whereas the + // last one corresponds to the + // pressure. + // + // It is also worth comparing the way + // we constructed this element from + // its base elements, with the way we + // have done so in step-8: there, we + // have built it as fe + // (FE_Q@(1), dim), i.e. we + // have simply used dim copies of + // the FE_Q(1) element, one copy + // for the displacement in each + // coordinate direction. template MixedLaplaceProblem::MixedLaplaceProblem (const unsigned int degree) - : - degree (degree), - fe (FE_RaviartThomas(degree), 1, - FE_DGQ(degree), 1), - dof_handler (triangulation) + : + degree (degree), + fe (FE_RaviartThomas(degree), 1, + FE_DGQ(degree), 1), + dof_handler (triangulation) {} - // @sect4{MixedLaplaceProblem::make_grid_and_dofs} + // @sect4{MixedLaplaceProblem::make_grid_and_dofs} - // This next function starts out with - // well-known functions calls that - // create and refine a mesh, and then - // associate degrees of freedom with - // it: + // This next function starts out with + // well-known functions calls that + // create and refine a mesh, and then + // associate degrees of freedom with + // it: template void MixedLaplaceProblem::make_grid_and_dofs () { @@ -410,55 +410,55 @@ namespace Step20 dof_handler.distribute_dofs (fe); - // However, then things become - // different. As mentioned in the - // introduction, we want to - // subdivide the matrix into blocks - // corresponding to the two - // different kinds of variables, - // velocity and pressure. To this end, - // we first have to make sure that - // the indices corresponding to - // velocities and pressures are not - // intermingled: First all velocity - // degrees of freedom, then all - // pressure DoFs. This way, the - // global matrix separates nicely - // into a 2x2 system. To achieve - // this, we have to renumber - // degrees of freedom base on their - // vector component, an operation - // that conveniently is already - // implemented: + // However, then things become + // different. As mentioned in the + // introduction, we want to + // subdivide the matrix into blocks + // corresponding to the two + // different kinds of variables, + // velocity and pressure. To this end, + // we first have to make sure that + // the indices corresponding to + // velocities and pressures are not + // intermingled: First all velocity + // degrees of freedom, then all + // pressure DoFs. This way, the + // global matrix separates nicely + // into a 2x2 system. To achieve + // this, we have to renumber + // degrees of freedom base on their + // vector component, an operation + // that conveniently is already + // implemented: DoFRenumbering::component_wise (dof_handler); - // The next thing is that we want - // to figure out the sizes of these - // blocks, so that we can allocate - // an appropriate amount of - // space. To this end, we call the - // DoFTools::count_dofs_per_component - // function that counts how many - // shape functions are non-zero for - // a particular vector - // component. We have dim+1 - // vector components, and we have - // to use the knowledge that for - // Raviart-Thomas elements all - // shape functions are nonzero in - // all components. In other words, - // the number of velocity shape - // functions equals the number of - // overall shape functions that are - // nonzero in the zeroth vector - // component. On the other hand, - // the number of pressure variables - // equals the number of shape - // functions that are nonzero in - // the dim-th component. Let us - // compute these numbers and then - // create some nice output with - // that: + // The next thing is that we want + // to figure out the sizes of these + // blocks, so that we can allocate + // an appropriate amount of + // space. To this end, we call the + // DoFTools::count_dofs_per_component + // function that counts how many + // shape functions are non-zero for + // a particular vector + // component. We have dim+1 + // vector components, and we have + // to use the knowledge that for + // Raviart-Thomas elements all + // shape functions are nonzero in + // all components. In other words, + // the number of velocity shape + // functions equals the number of + // overall shape functions that are + // nonzero in the zeroth vector + // component. On the other hand, + // the number of pressure variables + // equals the number of shape + // functions that are nonzero in + // the dim-th component. Let us + // compute these numbers and then + // create some nice output with + // that: std::vector dofs_per_component (dim+1); DoFTools::count_dofs_per_component (dof_handler, dofs_per_component); const unsigned int n_u = dofs_per_component[0], @@ -475,37 +475,37 @@ namespace Step20 << " (" << n_u << '+' << n_p << ')' << std::endl; - // The next task is to allocate a - // sparsity pattern for the matrix - // that we will create. The way - // this works is that we first - // obtain a guess for the maximal - // number of nonzero entries per - // row (this could be done more - // efficiently in this case, but we - // only want to solve relatively - // small problems for which this is - // not so important). In the second - // step, we allocate a 2x2 block - // pattern and then reinitialize - // each of the blocks to its - // correct size using the n_u - // and n_p variables defined - // above that hold the number of - // velocity and pressure - // variables. In this second step, - // we only operate on the - // individual blocks of the - // system. In the third step, we - // therefore have to instruct the - // overlying block system to update - // its knowledge about the sizes of - // the blocks it manages; this - // happens with the - // sparsity_pattern.collect_sizes() - // call: + // The next task is to allocate a + // sparsity pattern for the matrix + // that we will create. The way + // this works is that we first + // obtain a guess for the maximal + // number of nonzero entries per + // row (this could be done more + // efficiently in this case, but we + // only want to solve relatively + // small problems for which this is + // not so important). In the second + // step, we allocate a 2x2 block + // pattern and then reinitialize + // each of the blocks to its + // correct size using the n_u + // and n_p variables defined + // above that hold the number of + // velocity and pressure + // variables. In this second step, + // we only operate on the + // individual blocks of the + // system. In the third step, we + // therefore have to instruct the + // overlying block system to update + // its knowledge about the sizes of + // the blocks it manages; this + // happens with the + // sparsity_pattern.collect_sizes() + // call: const unsigned int - n_couplings = dof_handler.max_couplings_between_dofs(); + n_couplings = dof_handler.max_couplings_between_dofs(); sparsity_pattern.reinit (2,2); sparsity_pattern.block(0,0).reinit (n_u, n_u, n_couplings); @@ -514,22 +514,22 @@ namespace Step20 sparsity_pattern.block(1,1).reinit (n_p, n_p, n_couplings); sparsity_pattern.collect_sizes(); - // Now that the sparsity pattern - // and its blocks have the correct - // sizes, we actually need to - // construct the content of this - // pattern, and as usual compress - // it, before we also initialize a - // block matrix with this block - // sparsity pattern: + // Now that the sparsity pattern + // and its blocks have the correct + // sizes, we actually need to + // construct the content of this + // pattern, and as usual compress + // it, before we also initialize a + // block matrix with this block + // sparsity pattern: DoFTools::make_sparsity_pattern (dof_handler, sparsity_pattern); sparsity_pattern.compress(); system_matrix.reinit (sparsity_pattern); - // Then we have to resize the - // solution and right hand side - // vectors in exactly the same way: + // Then we have to resize the + // solution and right hand side + // vectors in exactly the same way: solution.reinit (2); solution.block(0).reinit (n_u); solution.block(1).reinit (n_p); @@ -542,25 +542,25 @@ namespace Step20 } - // @sect4{MixedLaplaceProblem::assemble_system} - // Similarly, the function that - // assembles the linear system has - // mostly been discussed already in - // the introduction to this - // example. At its top, what happens - // are all the usual steps, with the - // addition that we do not only - // allocate quadrature and - // FEValues objects for the cell - // terms, but also for face - // terms. After that, we define the - // usual abbreviations for variables, - // and the allocate space for the - // local matrix and right hand side - // contributions, and the array that - // holds the global numbers of the - // degrees of freedom local to the - // present cell. + // @sect4{MixedLaplaceProblem::assemble_system} + // Similarly, the function that + // assembles the linear system has + // mostly been discussed already in + // the introduction to this + // example. At its top, what happens + // are all the usual steps, with the + // addition that we do not only + // allocate quadrature and + // FEValues objects for the cell + // terms, but also for face + // terms. After that, we define the + // usual abbreviations for variables, + // and the allocate space for the + // local matrix and right hand side + // contributions, and the array that + // holds the global numbers of the + // degrees of freedom local to the + // present cell. template void MixedLaplaceProblem::assemble_system () { @@ -583,20 +583,20 @@ namespace Step20 std::vector local_dof_indices (dofs_per_cell); - // The next step is to declare - // objects that represent the - // source term, pressure boundary - // value, and coefficient in the - // equation. In addition to these - // objects that represent - // continuous functions, we also - // need arrays to hold their values - // at the quadrature points of - // individual cells (or faces, for - // the boundary values). Note that - // in the case of the coefficient, - // the array has to be one of - // matrices. + // The next step is to declare + // objects that represent the + // source term, pressure boundary + // value, and coefficient in the + // equation. In addition to these + // objects that represent + // continuous functions, we also + // need arrays to hold their values + // at the quadrature points of + // individual cells (or faces, for + // the boundary values). Note that + // in the case of the coefficient, + // the array has to be one of + // matrices. const RightHandSide right_hand_side; const PressureBoundaryValues pressure_boundary_values; const KInverse k_inverse; @@ -605,33 +605,33 @@ namespace Step20 std::vector boundary_values (n_face_q_points); std::vector > k_inverse_values (n_q_points); - // Finally, we need a couple of extractors - // that we will use to get at the velocity - // and pressure components of vector-valued - // shape functions. Their function and use - // is described in detail in the @ref - // vector_valued report. Essentially, we - // will use them as subscripts on the - // FEValues objects below: the FEValues - // object describes all vector components - // of shape functions, while after - // subscription, it will only refer to the - // velocities (a set of dim - // components starting at component zero) - // or the pressure (a scalar component - // located at position dim): + // Finally, we need a couple of extractors + // that we will use to get at the velocity + // and pressure components of vector-valued + // shape functions. Their function and use + // is described in detail in the @ref + // vector_valued report. Essentially, we + // will use them as subscripts on the + // FEValues objects below: the FEValues + // object describes all vector components + // of shape functions, while after + // subscription, it will only refer to the + // velocities (a set of dim + // components starting at component zero) + // or the pressure (a scalar component + // located at position dim): const FEValuesExtractors::Vector velocities (0); const FEValuesExtractors::Scalar pressure (dim); - // With all this in place, we can - // go on with the loop over all - // cells. The body of this loop has - // been discussed in the - // introduction, and will not be - // commented any further here: + // With all this in place, we can + // go on with the loop over all + // cells. The body of this loop has + // been discussed in the + // introduction, and will not be + // commented any further here: typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { fe_values.reinit (cell); @@ -675,8 +675,8 @@ namespace Step20 fe_face_values.reinit (cell, face_no); pressure_boundary_values - .value_list (fe_face_values.get_quadrature_points(), - boundary_values); + .value_list (fe_face_values.get_quadrature_points(), + boundary_values); for (unsigned int q=0; qget_dof_indices (local_dof_indices); for (unsigned int i=0; iSchurComplement class template} - - // The next class is the Schur - // complement class. Its rationale - // has also been discussed in length - // in the introduction. The only - // things we would like to note is - // that the class, too, is derived - // from the Subscriptor class and - // that as mentioned above it stores - // pointers to the entire block - // matrix and the inverse of the mass - // matrix block using - // SmartPointer objects. - // - // The vmult function requires - // two temporary vectors that we do - // not want to re-allocate and free - // every time we call this - // function. Since here, we have full - // control over the use of these - // vectors (unlike above, where a - // class called by the vmult - // function required these vectors, - // not the vmult function - // itself), we allocate them - // directly, rather than going - // through the VectorMemory - // mechanism. However, again, these - // member variables do not carry any - // state between successive calls to - // the member functions of this class - // (i.e., we never care what values - // they were set to the last time a - // member function was called), we - // mark these vectors as mutable. - // - // The rest of the (short) - // implementation of this class is - // straightforward if you know the - // order of matrix-vector - // multiplications performed by the - // vmult function: + // @sect3{Linear solvers and preconditioners} + + // The linear solvers and + // preconditioners we use in this + // example have been discussed in + // significant detail already in the + // introduction. We will therefore + // not discuss the rationale for + // these classes here any more, but + // rather only comment on + // implementational aspects. + + + // @sect4{The SchurComplement class template} + + // The next class is the Schur + // complement class. Its rationale + // has also been discussed in length + // in the introduction. The only + // things we would like to note is + // that the class, too, is derived + // from the Subscriptor class and + // that as mentioned above it stores + // pointers to the entire block + // matrix and the inverse of the mass + // matrix block using + // SmartPointer objects. + // + // The vmult function requires + // two temporary vectors that we do + // not want to re-allocate and free + // every time we call this + // function. Since here, we have full + // control over the use of these + // vectors (unlike above, where a + // class called by the vmult + // function required these vectors, + // not the vmult function + // itself), we allocate them + // directly, rather than going + // through the VectorMemory + // mechanism. However, again, these + // member variables do not carry any + // state between successive calls to + // the member functions of this class + // (i.e., we never care what values + // they were set to the last time a + // member function was called), we + // mark these vectors as mutable. + // + // The rest of the (short) + // implementation of this class is + // straightforward if you know the + // order of matrix-vector + // multiplications performed by the + // vmult function: class SchurComplement : public Subscriptor { - public: - SchurComplement (const BlockSparseMatrix &A, - const IterativeInverse > &Minv); + public: + SchurComplement (const BlockSparseMatrix &A, + const IterativeInverse > &Minv); - void vmult (Vector &dst, - const Vector &src) const; + void vmult (Vector &dst, + const Vector &src) const; - private: - const SmartPointer > system_matrix; - const SmartPointer > > m_inverse; + private: + const SmartPointer > system_matrix; + const SmartPointer > > m_inverse; - mutable Vector tmp1, tmp2; + mutable Vector tmp1, tmp2; }; SchurComplement::SchurComplement (const BlockSparseMatrix &A, const IterativeInverse > &Minv) - : - system_matrix (&A), - m_inverse (&Minv), - tmp1 (A.block(0,0).m()), - tmp2 (A.block(0,0).m()) + : + system_matrix (&A), + m_inverse (&Minv), + tmp1 (A.block(0,0).m()), + tmp2 (A.block(0,0).m()) {} @@ -807,52 +807,52 @@ namespace Step20 } - // @sect4{The ApproximateSchurComplement class template} - - // The third component of our solver - // and preconditioner system is the - // class that approximates the Schur - // complement so we can form a - // an InverseIterate - // object that approximates the - // inverse of the Schur - // complement. It follows the same - // pattern as the Schur complement - // class, with the only exception - // that we do not multiply with the - // inverse mass matrix in vmult, - // but rather just do a single Jacobi - // step. Consequently, the class also - // does not have to store a pointer - // to an inverse mass matrix object. - // - // Since InverseIterate follows the - // standard convention for matrices, - // we need to provide a - // Tvmult function here as - // well. + // @sect4{The ApproximateSchurComplement class template} + + // The third component of our solver + // and preconditioner system is the + // class that approximates the Schur + // complement so we can form a + // an InverseIterate + // object that approximates the + // inverse of the Schur + // complement. It follows the same + // pattern as the Schur complement + // class, with the only exception + // that we do not multiply with the + // inverse mass matrix in vmult, + // but rather just do a single Jacobi + // step. Consequently, the class also + // does not have to store a pointer + // to an inverse mass matrix object. + // + // Since InverseIterate follows the + // standard convention for matrices, + // we need to provide a + // Tvmult function here as + // well. class ApproximateSchurComplement : public Subscriptor { - public: - ApproximateSchurComplement (const BlockSparseMatrix &A); + public: + ApproximateSchurComplement (const BlockSparseMatrix &A); - void vmult (Vector &dst, - const Vector &src) const; - void Tvmult (Vector &dst, - const Vector &src) const; + void vmult (Vector &dst, + const Vector &src) const; + void Tvmult (Vector &dst, + const Vector &src) const; - private: - const SmartPointer > system_matrix; + private: + const SmartPointer > system_matrix; - mutable Vector tmp1, tmp2; + mutable Vector tmp1, tmp2; }; ApproximateSchurComplement::ApproximateSchurComplement (const BlockSparseMatrix &A) - : - system_matrix (&A), - tmp1 (A.block(0,0).m()), - tmp2 (A.block(0,0).m()) + : + system_matrix (&A), + tmp1 (A.block(0,0).m()), + tmp2 (A.block(0,0).m()) {} @@ -875,29 +875,29 @@ namespace Step20 - // @sect4{MixedLaplace::solve} - - // After all these preparations, we - // can finally write the function - // that actually solves the linear - // problem. We will go through the - // two parts it has that each solve - // one of the two equations, the - // first one for the pressure - // (component 1 of the solution), - // then the velocities (component 0 - // of the solution). Both parts need - // an object representing the inverse - // mass matrix and an auxiliary - // vector, and we therefore declare - // these objects at the beginning of - // this function. + // @sect4{MixedLaplace::solve} + + // After all these preparations, we + // can finally write the function + // that actually solves the linear + // problem. We will go through the + // two parts it has that each solve + // one of the two equations, the + // first one for the pressure + // (component 1 of the solution), + // then the velocities (component 0 + // of the solution). Both parts need + // an object representing the inverse + // mass matrix and an auxiliary + // vector, and we therefore declare + // these objects at the beginning of + // this function. template void MixedLaplaceProblem::solve () { PreconditionIdentity identity; IterativeInverse > - m_inverse; + m_inverse; m_inverse.initialize(system_matrix.block(0,0), identity); m_inverse.solver.select("cg"); static ReductionControl inner_control(1000, 0., 1.e-13); @@ -905,19 +905,19 @@ namespace Step20 Vector tmp (solution.block(0).size()); - // Now on to the first - // equation. The right hand side of - // it is BM^{-1}F-G, which is what - // we compute in the first few - // lines. We then declare the - // objects representing the Schur - // complement, its approximation, - // and the inverse of the - // approximation. Finally, we - // declare a solver object and hand - // off all these matrices and - // vectors to it to compute block 1 - // (the pressure) of the solution: + // Now on to the first + // equation. The right hand side of + // it is BM^{-1}F-G, which is what + // we compute in the first few + // lines. We then declare the + // objects representing the Schur + // complement, its approximation, + // and the inverse of the + // approximation. Finally, we + // declare a solver object and hand + // off all these matrices and + // vectors to it to compute block 1 + // (the pressure) of the solution: { Vector schur_rhs (solution.block(1).size()); @@ -927,13 +927,13 @@ namespace Step20 SchurComplement - schur_complement (system_matrix, m_inverse); + schur_complement (system_matrix, m_inverse); ApproximateSchurComplement - approximate_schur_complement (system_matrix); + approximate_schur_complement (system_matrix); IterativeInverse > - preconditioner; + preconditioner; preconditioner.initialize(approximate_schur_complement, identity); preconditioner.solver.select("cg"); preconditioner.solver.set_control(inner_control); @@ -951,14 +951,14 @@ namespace Step20 << std::endl; } - // After we have the pressure, we - // can compute the velocity. The - // equation reads MU=-B^TP+F, and - // we solve it by first computing - // the right hand side, and then - // multiplying it with the object - // that represents the inverse of - // the mass matrix: + // After we have the pressure, we + // can compute the velocity. The + // equation reads MU=-B^TP+F, and + // we solve it by first computing + // the right hand side, and then + // multiplying it with the object + // that represents the inverse of + // the mass matrix: { system_matrix.block(0,1).vmult (tmp, solution.block(1)); tmp *= -1; @@ -969,125 +969,125 @@ namespace Step20 } - // @sect3{MixedLaplaceProblem class implementation (continued)} - - // @sect4{MixedLaplace::compute_errors} - - // After we have dealt with the - // linear solver and preconditioners, - // we continue with the - // implementation of our main - // class. In particular, the next - // task is to compute the errors in - // our numerical solution, in both - // the pressures as well as - // velocities. - // - // To compute errors in the solution, - // we have already introduced the - // VectorTools::integrate_difference - // function in step-7 and - // step-11. However, there we only - // dealt with scalar solutions, - // whereas here we have a - // vector-valued solution with - // components that even denote - // different quantities and may have - // different orders of convergence - // (this isn't the case here, by - // choice of the used finite - // elements, but is frequently the - // case in mixed finite element - // applications). What we therefore - // have to do is to `mask' the - // components that we are interested - // in. This is easily done: the - // VectorTools::integrate_difference - // function takes as its last - // argument a pointer to a weight - // function (the parameter defaults - // to the null pointer, meaning unit - // weights). What we simply have to - // do is to pass a function object - // that equals one in the components - // we are interested in, and zero in - // the other ones. For example, to - // compute the pressure error, we - // should pass a function that - // represents the constant vector - // with a unit value in component - // dim, whereas for the velocity - // the constant vector should be one - // in the first dim components, - // and zero in the location of the - // pressure. - // - // In deal.II, the - // ComponentSelectFunction does - // exactly this: it wants to know how - // many vector components the - // function it is to represent should - // have (in our case this would be - // dim+1, for the joint - // velocity-pressure space) and which - // individual or range of components - // should be equal to one. We - // therefore define two such masks at - // the beginning of the function, - // following by an object - // representing the exact solution - // and a vector in which we will - // store the cellwise errors as - // computed by - // integrate_difference: + // @sect3{MixedLaplaceProblem class implementation (continued)} + + // @sect4{MixedLaplace::compute_errors} + + // After we have dealt with the + // linear solver and preconditioners, + // we continue with the + // implementation of our main + // class. In particular, the next + // task is to compute the errors in + // our numerical solution, in both + // the pressures as well as + // velocities. + // + // To compute errors in the solution, + // we have already introduced the + // VectorTools::integrate_difference + // function in step-7 and + // step-11. However, there we only + // dealt with scalar solutions, + // whereas here we have a + // vector-valued solution with + // components that even denote + // different quantities and may have + // different orders of convergence + // (this isn't the case here, by + // choice of the used finite + // elements, but is frequently the + // case in mixed finite element + // applications). What we therefore + // have to do is to `mask' the + // components that we are interested + // in. This is easily done: the + // VectorTools::integrate_difference + // function takes as its last + // argument a pointer to a weight + // function (the parameter defaults + // to the null pointer, meaning unit + // weights). What we simply have to + // do is to pass a function object + // that equals one in the components + // we are interested in, and zero in + // the other ones. For example, to + // compute the pressure error, we + // should pass a function that + // represents the constant vector + // with a unit value in component + // dim, whereas for the velocity + // the constant vector should be one + // in the first dim components, + // and zero in the location of the + // pressure. + // + // In deal.II, the + // ComponentSelectFunction does + // exactly this: it wants to know how + // many vector components the + // function it is to represent should + // have (in our case this would be + // dim+1, for the joint + // velocity-pressure space) and which + // individual or range of components + // should be equal to one. We + // therefore define two such masks at + // the beginning of the function, + // following by an object + // representing the exact solution + // and a vector in which we will + // store the cellwise errors as + // computed by + // integrate_difference: template void MixedLaplaceProblem::compute_errors () const { const ComponentSelectFunction - pressure_mask (dim, dim+1); + pressure_mask (dim, dim+1); const ComponentSelectFunction - velocity_mask(std::make_pair(0, dim), dim+1); + velocity_mask(std::make_pair(0, dim), dim+1); ExactSolution exact_solution; Vector cellwise_errors (triangulation.n_active_cells()); - // As already discussed in step-7, - // we have to realize that it is - // impossible to integrate the - // errors exactly. All we can do is - // approximate this integral using - // quadrature. This actually - // presents a slight twist here: if - // we naively chose an object of - // type QGauss@(degree+1) - // as one may be inclined to do - // (this is what we used for - // integrating the linear system), - // one realizes that the error is - // very small and does not follow - // the expected convergence curves - // at all. What is happening is - // that for the mixed finite - // elements used here, the Gauss - // points happen to be - // superconvergence points in which - // the pointwise error is much - // smaller (and converges with - // higher order) than anywhere - // else. These are therefore not - // particularly good points for - // ingration. To avoid this - // problem, we simply use a - // trapezoidal rule and iterate it - // degree+2 times in each - // coordinate direction (again as - // explained in step-7): + // As already discussed in step-7, + // we have to realize that it is + // impossible to integrate the + // errors exactly. All we can do is + // approximate this integral using + // quadrature. This actually + // presents a slight twist here: if + // we naively chose an object of + // type QGauss@(degree+1) + // as one may be inclined to do + // (this is what we used for + // integrating the linear system), + // one realizes that the error is + // very small and does not follow + // the expected convergence curves + // at all. What is happening is + // that for the mixed finite + // elements used here, the Gauss + // points happen to be + // superconvergence points in which + // the pointwise error is much + // smaller (and converges with + // higher order) than anywhere + // else. These are therefore not + // particularly good points for + // ingration. To avoid this + // problem, we simply use a + // trapezoidal rule and iterate it + // degree+2 times in each + // coordinate direction (again as + // explained in step-7): QTrapez<1> q_trapez; QIterated quadrature (q_trapez, degree+2); - // With this, we can then let the - // library compute the errors and - // output them to the screen: + // With this, we can then let the + // library compute the errors and + // output them to the screen: VectorTools::integrate_difference (dof_handler, solution, exact_solution, cellwise_errors, quadrature, VectorTools::L2_norm, @@ -1106,68 +1106,68 @@ namespace Step20 } - // @sect4{MixedLaplace::output_results} - - // The last interesting function is - // the one in which we generate - // graphical output. Everything here - // looks obvious and familiar. Note - // how we construct unique names for - // all the solution variables at the - // beginning, like we did in step-8 - // and other programs later on. The - // only thing worth mentioning is - // that for higher order elements, in - // seems inappropriate to only show a - // single bilinear quadrilateral per - // cell in the graphical output. We - // therefore generate patches of size - // (degree+1)x(degree+1) to capture - // the full information content of - // the solution. See the step-7 - // tutorial program for more - // information on this. - // - // Note that we output the dim+1 - // components of the solution vector as a - // collection of individual scalars - // here. Most visualization programs will - // then only offer to visualize them - // individually, rather than allowing us to - // plot the flow field as a vector - // field. However, as explained in the - // corresponding function of step-22 or the - // @ref VVOutput "Generating graphical output" - // section of the @ref vector_valued module, - // instructing the DataOut class to identify - // components of the FESystem object as - // elements of a dim-dimensional - // vector is not actually very difficult and - // will then allow us to show results as - // vector plots. We skip this here for - // simplicity and refer to the links above - // for more information. + // @sect4{MixedLaplace::output_results} + + // The last interesting function is + // the one in which we generate + // graphical output. Everything here + // looks obvious and familiar. Note + // how we construct unique names for + // all the solution variables at the + // beginning, like we did in step-8 + // and other programs later on. The + // only thing worth mentioning is + // that for higher order elements, in + // seems inappropriate to only show a + // single bilinear quadrilateral per + // cell in the graphical output. We + // therefore generate patches of size + // (degree+1)x(degree+1) to capture + // the full information content of + // the solution. See the step-7 + // tutorial program for more + // information on this. + // + // Note that we output the dim+1 + // components of the solution vector as a + // collection of individual scalars + // here. Most visualization programs will + // then only offer to visualize them + // individually, rather than allowing us to + // plot the flow field as a vector + // field. However, as explained in the + // corresponding function of step-22 or the + // @ref VVOutput "Generating graphical output" + // section of the @ref vector_valued module, + // instructing the DataOut class to identify + // components of the FESystem object as + // elements of a dim-dimensional + // vector is not actually very difficult and + // will then allow us to show results as + // vector plots. We skip this here for + // simplicity and refer to the links above + // for more information. template void MixedLaplaceProblem::output_results () const { std::vector solution_names; switch (dim) { - case 2: - solution_names.push_back ("u"); - solution_names.push_back ("v"); - solution_names.push_back ("p"); - break; - - case 3: - solution_names.push_back ("u"); - solution_names.push_back ("v"); - solution_names.push_back ("w"); - solution_names.push_back ("p"); - break; - - default: - Assert (false, ExcNotImplemented()); + case 2: + solution_names.push_back ("u"); + solution_names.push_back ("v"); + solution_names.push_back ("p"); + break; + + case 3: + solution_names.push_back ("u"); + solution_names.push_back ("v"); + solution_names.push_back ("w"); + solution_names.push_back ("p"); + break; + + default: + Assert (false, ExcNotImplemented()); } @@ -1184,12 +1184,12 @@ namespace Step20 - // @sect4{MixedLaplace::run} + // @sect4{MixedLaplace::run} - // This is the final function of our - // main class. It's only job is to - // call the other functions in their - // natural order: + // This is the final function of our + // main class. It's only job is to + // call the other functions in their + // natural order: template void MixedLaplaceProblem::run () { @@ -1202,18 +1202,18 @@ namespace Step20 } - // @sect3{The main function} +// @sect3{The main function} - // The main function we stole from - // step-6 instead of step-4. It is - // almost equal to the one in step-6 - // (apart from the changed class - // names, of course), the only - // exception is that we pass the - // degree of the finite element space - // to the constructor of the mixed - // laplace problem (here, we use - // zero-th order elements). +// The main function we stole from +// step-6 instead of step-4. It is +// almost equal to the one in step-6 +// (apart from the changed class +// names, of course), the only +// exception is that we pass the +// degree of the finite element space +// to the constructor of the mixed +// laplace problem (here, we use +// zero-th order elements). int main () { try diff --git a/deal.II/examples/step-21/step-21.cc b/deal.II/examples/step-21/step-21.cc index a4d9e6148a..8d8cc00ee7 100644 --- a/deal.II/examples/step-21/step-21.cc +++ b/deal.II/examples/step-21/step-21.cc @@ -9,18 +9,18 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // This program is an adaptation of step-20 - // and includes some technique of DG methods - // from step-12. A good part of the program - // is therefore very similar to step-20 and - // we will not comment again on these - // parts. Only the new stuff will be - // discussed in more detail. - - // @sect3{Include files} - - // All of these include files have been used - // before: +// This program is an adaptation of step-20 +// and includes some technique of DG methods +// from step-12. A good part of the program +// is therefore very similar to step-20 and +// we will not comment again on these +// parts. Only the new stuff will be +// discussed in more detail. + +// @sect3{Include files} + +// All of these include files have been used +// before: #include #include #include @@ -55,105 +55,105 @@ #include #include - // In this program, we use a tensor-valued - // coefficient. Since it may have a spatial - // dependence, we consider it a tensor-valued - // function. The following include file - // provides the TensorFunction - // class that offers such functionality: +// In this program, we use a tensor-valued +// coefficient. Since it may have a spatial +// dependence, we consider it a tensor-valued +// function. The following include file +// provides the TensorFunction +// class that offers such functionality: #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step21 { using namespace dealii; - // @sect3{The TwoPhaseFlowProblem class} - - // This is the main class of the program. It - // is close to the one of step-20, but with a - // few additional functions: - // - //
    - //
  • assemble_rhs_S assembles the - // right hand side of the saturation - // equation. As explained in the - // introduction, this can't be integrated - // into assemble_rhs since it depends - // on the velocity that is computed in the - // first part of the time step. - // - //
  • get_maximal_velocity does as its - // name suggests. This function is used in - // the computation of the time step size. - // - //
  • project_back_saturation resets - // all saturation degrees of freedom with - // values less than zero to zero, and all - // those with saturations greater than one - // to one. - //
- // - // The rest of the class should be pretty - // much obvious. The viscosity variable - // stores the viscosity $\mu$ that enters - // several of the formulas in the nonlinear - // equations. + // @sect3{The TwoPhaseFlowProblem class} + + // This is the main class of the program. It + // is close to the one of step-20, but with a + // few additional functions: + // + //
    + //
  • assemble_rhs_S assembles the + // right hand side of the saturation + // equation. As explained in the + // introduction, this can't be integrated + // into assemble_rhs since it depends + // on the velocity that is computed in the + // first part of the time step. + // + //
  • get_maximal_velocity does as its + // name suggests. This function is used in + // the computation of the time step size. + // + //
  • project_back_saturation resets + // all saturation degrees of freedom with + // values less than zero to zero, and all + // those with saturations greater than one + // to one. + //
+ // + // The rest of the class should be pretty + // much obvious. The viscosity variable + // stores the viscosity $\mu$ that enters + // several of the formulas in the nonlinear + // equations. template class TwoPhaseFlowProblem { - public: - TwoPhaseFlowProblem (const unsigned int degree); - void run (); + public: + TwoPhaseFlowProblem (const unsigned int degree); + void run (); - private: - void make_grid_and_dofs (); - void assemble_system (); - void assemble_rhs_S (); - double get_maximal_velocity () const; - void solve (); - void project_back_saturation (); - void output_results () const; + private: + void make_grid_and_dofs (); + void assemble_system (); + void assemble_rhs_S (); + double get_maximal_velocity () const; + void solve (); + void project_back_saturation (); + void output_results () const; - const unsigned int degree; + const unsigned int degree; - Triangulation triangulation; - FESystem fe; - DoFHandler dof_handler; + Triangulation triangulation; + FESystem fe; + DoFHandler dof_handler; - BlockSparsityPattern sparsity_pattern; - BlockSparseMatrix system_matrix; + BlockSparsityPattern sparsity_pattern; + BlockSparseMatrix system_matrix; - const unsigned int n_refinement_steps; + const unsigned int n_refinement_steps; - double time_step; - unsigned int timestep_number; - double viscosity; + double time_step; + unsigned int timestep_number; + double viscosity; - BlockVector solution; - BlockVector old_solution; - BlockVector system_rhs; + BlockVector solution; + BlockVector old_solution; + BlockVector system_rhs; }; - // @sect3{Equation data} + // @sect3{Equation data} - // @sect4{Pressure right hand side} - // At present, the right hand side of the - // pressure equation is simply the zero - // function. However, the rest of the program - // is fully equipped to deal with anything - // else, if this is desired: + // @sect4{Pressure right hand side} + // At present, the right hand side of the + // pressure equation is simply the zero + // function. However, the rest of the program + // is fully equipped to deal with anything + // else, if this is desired: template class PressureRightHandSide : public Function { - public: - PressureRightHandSide () : Function(1) {} + public: + PressureRightHandSide () : Function(1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -167,18 +167,18 @@ namespace Step21 } - // @sect4{Pressure boundary values} - // The next are pressure boundary values. As - // mentioned in the introduction, we choose a - // linear pressure field: + // @sect4{Pressure boundary values} + // The next are pressure boundary values. As + // mentioned in the introduction, we choose a + // linear pressure field: template class PressureBoundaryValues : public Function { - public: - PressureBoundaryValues () : Function(1) {} + public: + PressureBoundaryValues () : Function(1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -191,24 +191,24 @@ namespace Step21 } - // @sect4{Saturation boundary values} + // @sect4{Saturation boundary values} - // Then we also need boundary values on the - // inflow portions of the boundary. The - // question whether something is an inflow - // part is decided when assembling the right - // hand side, we only have to provide a - // functional description of the boundary - // values. This is as explained in the - // introduction: + // Then we also need boundary values on the + // inflow portions of the boundary. The + // question whether something is an inflow + // part is decided when assembling the right + // hand side, we only have to provide a + // functional description of the boundary + // values. This is as explained in the + // introduction: template class SaturationBoundaryValues : public Function { - public: - SaturationBoundaryValues () : Function(1) {} + public: + SaturationBoundaryValues () : Function(1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -226,36 +226,36 @@ namespace Step21 - // @sect4{Initial data} - - // Finally, we need initial data. In reality, - // we only need initial data for the - // saturation, but we are lazy, so we will - // later, before the first time step, simply - // interpolate the entire solution for the - // previous time step from a function that - // contains all vector components. - // - // We therefore simply create a function that - // returns zero in all components. We do that - // by simply forward every function to the - // ZeroFunction class. Why not use that right - // away in the places of this program where - // we presently use the InitialValues - // class? Because this way it is simpler to - // later go back and choose a different - // function for initial values. + // @sect4{Initial data} + + // Finally, we need initial data. In reality, + // we only need initial data for the + // saturation, but we are lazy, so we will + // later, before the first time step, simply + // interpolate the entire solution for the + // previous time step from a function that + // contains all vector components. + // + // We therefore simply create a function that + // returns zero in all components. We do that + // by simply forward every function to the + // ZeroFunction class. Why not use that right + // away in the places of this program where + // we presently use the InitialValues + // class? Because this way it is simpler to + // later go back and choose a different + // function for initial values. template class InitialValues : public Function { - public: - InitialValues () : Function(dim+2) {} + public: + InitialValues () : Function(dim+2) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void vector_value (const Point &p, - Vector &value) const; + virtual void vector_value (const Point &p, + Vector &value) const; }; @@ -280,42 +280,42 @@ namespace Step21 - // @sect3{The inverse permeability tensor} + // @sect3{The inverse permeability tensor} - // As announced in the introduction, we - // implement two different permeability - // tensor fields. Each of them we put into a - // namespace of its own, so that it will be - // easy later to replace use of one by the - // other in the code. + // As announced in the introduction, we + // implement two different permeability + // tensor fields. Each of them we put into a + // namespace of its own, so that it will be + // easy later to replace use of one by the + // other in the code. - // @sect4{Single curving crack permeability} + // @sect4{Single curving crack permeability} - // The first function for the - // permeability was the one that - // models a single curving crack. It - // was already used at the end of - // step-20, and its functional form - // is given in the introduction of - // the present tutorial program. As - // in some previous programs, we have - // to declare a (seemingly - // unnecessary) default constructor - // of the KInverse class to avoid - // warnings from some compilers: + // The first function for the + // permeability was the one that + // models a single curving crack. It + // was already used at the end of + // step-20, and its functional form + // is given in the introduction of + // the present tutorial program. As + // in some previous programs, we have + // to declare a (seemingly + // unnecessary) default constructor + // of the KInverse class to avoid + // warnings from some compilers: namespace SingleCurvingCrack { template class KInverse : public TensorFunction<2,dim> { - public: - KInverse () - : - TensorFunction<2,dim> () - {} - - virtual void value_list (const std::vector > &points, - std::vector > &values) const; + public: + KInverse () + : + TensorFunction<2,dim> () + {} + + virtual void value_list (const std::vector > &points, + std::vector > &values) const; }; @@ -346,75 +346,75 @@ namespace Step21 } - // @sect4{Random medium permeability} - - // This function does as announced in the - // introduction, i.e. it creates an overlay - // of exponentials at random places. There is - // one thing worth considering for this - // class. The issue centers around the - // problem that the class creates the centers - // of the exponentials using a random - // function. If we therefore created the - // centers each time we create an object of - // the present type, we would get a different - // list of centers each time. That's not what - // we expect from classes of this type: they - // should reliably represent the same - // function. - // - // The solution to this problem is to make - // the list of centers a static member - // variable of this class, i.e. there exists - // exactly one such variable for the entire - // program, rather than for each object of - // this type. That's exactly what we are - // going to do. - // - // The next problem, however, is that we need - // a way to initialize this variable. Since - // this variable is initialized at the - // beginning of the program, we can't use a - // regular member function for that since - // there may not be an object of this type - // around at the time. The C++ standard - // therefore says that only non-member and - // static member functions can be used to - // initialize a static variable. We use the - // latter possibility by defining a function - // get_centers that computes the list of - // center points when called. - // - // Note that this class works just fine in - // both 2d and 3d, with the only difference - // being that we use more points in 3d: by - // experimenting we find that we need more - // exponentials in 3d than in 2d (we have - // more ground to cover, after all, if we - // want to keep the distance between centers - // roughly equal), so we choose 40 in 2d and - // 100 in 3d. For any other dimension, the - // function does presently not know what to - // do so simply throws an exception - // indicating exactly this. + // @sect4{Random medium permeability} + + // This function does as announced in the + // introduction, i.e. it creates an overlay + // of exponentials at random places. There is + // one thing worth considering for this + // class. The issue centers around the + // problem that the class creates the centers + // of the exponentials using a random + // function. If we therefore created the + // centers each time we create an object of + // the present type, we would get a different + // list of centers each time. That's not what + // we expect from classes of this type: they + // should reliably represent the same + // function. + // + // The solution to this problem is to make + // the list of centers a static member + // variable of this class, i.e. there exists + // exactly one such variable for the entire + // program, rather than for each object of + // this type. That's exactly what we are + // going to do. + // + // The next problem, however, is that we need + // a way to initialize this variable. Since + // this variable is initialized at the + // beginning of the program, we can't use a + // regular member function for that since + // there may not be an object of this type + // around at the time. The C++ standard + // therefore says that only non-member and + // static member functions can be used to + // initialize a static variable. We use the + // latter possibility by defining a function + // get_centers that computes the list of + // center points when called. + // + // Note that this class works just fine in + // both 2d and 3d, with the only difference + // being that we use more points in 3d: by + // experimenting we find that we need more + // exponentials in 3d than in 2d (we have + // more ground to cover, after all, if we + // want to keep the distance between centers + // roughly equal), so we choose 40 in 2d and + // 100 in 3d. For any other dimension, the + // function does presently not know what to + // do so simply throws an exception + // indicating exactly this. namespace RandomMedium { template class KInverse : public TensorFunction<2,dim> { - public: - KInverse () - : - TensorFunction<2,dim> () - {} + public: + KInverse () + : + TensorFunction<2,dim> () + {} - virtual void value_list (const std::vector > &points, - std::vector > &values) const; + virtual void value_list (const std::vector > &points, + std::vector > &values) const; - private: - static std::vector > centers; + private: + static std::vector > centers; - static std::vector > get_centers (); + static std::vector > get_centers (); }; @@ -472,13 +472,13 @@ namespace Step21 - // @sect3{The inverse mobility and saturation functions} + // @sect3{The inverse mobility and saturation functions} - // There are two more pieces of data that we - // need to describe, namely the inverse - // mobility function and the saturation - // curve. Their form is also given in the - // introduction: + // There are two more pieces of data that we + // need to describe, namely the inverse + // mobility function and the saturation + // curve. Their form is also given in the + // introduction: double mobility_inverse (const double S, const double viscosity) { @@ -495,48 +495,48 @@ namespace Step21 - // @sect3{Linear solvers and preconditioners} - - // The linear solvers we use are also - // completely analogous to the ones - // used in step-20. The following - // classes are therefore copied - // verbatim from there. There is a - // single change: if the size of a - // linear system is small, i.e. when - // the mesh is very coarse, then it - // is sometimes not sufficient to set - // a maximum of - // src.size() CG - // iterations before the solver in - // the vmult() function - // converges. (This is, of course, a - // result of numerical round-off, - // since we know that on paper, the - // CG method converges in at most - // src.size() steps.) As - // a consequence, we set the maximum - // number of iterations equal to the - // maximum of the size of the linear - // system and 200. + // @sect3{Linear solvers and preconditioners} + + // The linear solvers we use are also + // completely analogous to the ones + // used in step-20. The following + // classes are therefore copied + // verbatim from there. There is a + // single change: if the size of a + // linear system is small, i.e. when + // the mesh is very coarse, then it + // is sometimes not sufficient to set + // a maximum of + // src.size() CG + // iterations before the solver in + // the vmult() function + // converges. (This is, of course, a + // result of numerical round-off, + // since we know that on paper, the + // CG method converges in at most + // src.size() steps.) As + // a consequence, we set the maximum + // number of iterations equal to the + // maximum of the size of the linear + // system and 200. template class InverseMatrix : public Subscriptor { - public: - InverseMatrix (const Matrix &m); + public: + InverseMatrix (const Matrix &m); - void vmult (Vector &dst, - const Vector &src) const; + void vmult (Vector &dst, + const Vector &src) const; - private: - const SmartPointer matrix; + private: + const SmartPointer matrix; }; template InverseMatrix::InverseMatrix (const Matrix &m) - : - matrix (&m) + : + matrix (&m) {} @@ -558,18 +558,18 @@ namespace Step21 class SchurComplement : public Subscriptor { - public: - SchurComplement (const BlockSparseMatrix &A, - const InverseMatrix > &Minv); + public: + SchurComplement (const BlockSparseMatrix &A, + const InverseMatrix > &Minv); - void vmult (Vector &dst, - const Vector &src) const; + void vmult (Vector &dst, + const Vector &src) const; - private: - const SmartPointer > system_matrix; - const SmartPointer > > m_inverse; + private: + const SmartPointer > system_matrix; + const SmartPointer > > m_inverse; - mutable Vector tmp1, tmp2; + mutable Vector tmp1, tmp2; }; @@ -577,11 +577,11 @@ namespace Step21 SchurComplement:: SchurComplement (const BlockSparseMatrix &A, const InverseMatrix > &Minv) - : - system_matrix (&A), - m_inverse (&Minv), - tmp1 (A.block(0,0).m()), - tmp2 (A.block(0,0).m()) + : + system_matrix (&A), + m_inverse (&Minv), + tmp1 (A.block(0,0).m()), + tmp2 (A.block(0,0).m()) {} @@ -597,25 +597,25 @@ namespace Step21 class ApproximateSchurComplement : public Subscriptor { - public: - ApproximateSchurComplement (const BlockSparseMatrix &A); + public: + ApproximateSchurComplement (const BlockSparseMatrix &A); - void vmult (Vector &dst, - const Vector &src) const; + void vmult (Vector &dst, + const Vector &src) const; - private: - const SmartPointer > system_matrix; + private: + const SmartPointer > system_matrix; - mutable Vector tmp1, tmp2; + mutable Vector tmp1, tmp2; }; ApproximateSchurComplement:: ApproximateSchurComplement (const BlockSparseMatrix &A) - : - system_matrix (&A), - tmp1 (A.block(0,0).m()), - tmp2 (A.block(0,0).m()) + : + system_matrix (&A), + tmp1 (A.block(0,0).m()), + tmp2 (A.block(0,0).m()) {} @@ -631,45 +631,45 @@ namespace Step21 - // @sect3{TwoPhaseFlowProblem class implementation} + // @sect3{TwoPhaseFlowProblem class implementation} - // Here now the implementation of the main - // class. Much of it is actually copied from - // step-20, so we won't comment on it in much - // detail. You should try to get familiar - // with that program first, then most of what - // is happening here should be mostly clear. + // Here now the implementation of the main + // class. Much of it is actually copied from + // step-20, so we won't comment on it in much + // detail. You should try to get familiar + // with that program first, then most of what + // is happening here should be mostly clear. - // @sect4{TwoPhaseFlowProblem::TwoPhaseFlowProblem} - // First for the constructor. We use $RT_k - // \times DQ_k \times DQ_k$ spaces. The time - // step is set to zero initially, but will be - // computed before it is needed first, as - // described in a subsection of the - // introduction. + // @sect4{TwoPhaseFlowProblem::TwoPhaseFlowProblem} + // First for the constructor. We use $RT_k + // \times DQ_k \times DQ_k$ spaces. The time + // step is set to zero initially, but will be + // computed before it is needed first, as + // described in a subsection of the + // introduction. template TwoPhaseFlowProblem::TwoPhaseFlowProblem (const unsigned int degree) - : - degree (degree), - fe (FE_RaviartThomas(degree), 1, - FE_DGQ(degree), 1, - FE_DGQ(degree), 1), - dof_handler (triangulation), - n_refinement_steps (5), - time_step (0), - viscosity (0.2) + : + degree (degree), + fe (FE_RaviartThomas(degree), 1, + FE_DGQ(degree), 1, + FE_DGQ(degree), 1), + dof_handler (triangulation), + n_refinement_steps (5), + time_step (0), + viscosity (0.2) {} - // @sect4{TwoPhaseFlowProblem::make_grid_and_dofs} + // @sect4{TwoPhaseFlowProblem::make_grid_and_dofs} - // This next function starts out with - // well-known functions calls that create and - // refine a mesh, and then associate degrees - // of freedom with it. It does all the same - // things as in step-20, just now for three - // components instead of two. + // This next function starts out with + // well-known functions calls that create and + // refine a mesh, and then associate degrees + // of freedom with it. It does all the same + // things as in step-20, just now for three + // components instead of two. template void TwoPhaseFlowProblem::make_grid_and_dofs () { @@ -695,7 +695,7 @@ namespace Step21 << std::endl; const unsigned int - n_couplings = dof_handler.max_couplings_between_dofs(); + n_couplings = dof_handler.max_couplings_between_dofs(); sparsity_pattern.reinit (3,3); sparsity_pattern.block(0,0).reinit (n_u, n_u, n_couplings); @@ -737,30 +737,30 @@ namespace Step21 } - // @sect4{TwoPhaseFlowProblem::assemble_system} - - // This is the function that assembles the - // linear system, or at least everything - // except the (1,3) block that depends on the - // still-unknown velocity computed during - // this time step (we deal with this in - // assemble_rhs_S). Much of it - // is again as in step-20, but we have to - // deal with some nonlinearity this time. - // However, the top of the function is pretty - // much as usual (note that we set matrix and - // right hand side to zero at the beginning - // — something we didn't have to do for - // stationary problems since there we use - // each matrix object only once and it is - // empty at the beginning anyway). - // - // Note that in its present form, the - // function uses the permeability implemented - // in the RandomMedium::KInverse - // class. Switching to the single curved - // crack permeability function is as simple - // as just changing the namespace name. + // @sect4{TwoPhaseFlowProblem::assemble_system} + + // This is the function that assembles the + // linear system, or at least everything + // except the (1,3) block that depends on the + // still-unknown velocity computed during + // this time step (we deal with this in + // assemble_rhs_S). Much of it + // is again as in step-20, but we have to + // deal with some nonlinearity this time. + // However, the top of the function is pretty + // much as usual (note that we set matrix and + // right hand side to zero at the beginning + // — something we didn't have to do for + // stationary problems since there we use + // each matrix object only once and it is + // empty at the beginning anyway). + // + // Note that in its present form, the + // function uses the permeability implemented + // in the RandomMedium::KInverse + // class. Switching to the single curved + // crack permeability function is as simple + // as just changing the namespace name. template void TwoPhaseFlowProblem::assemble_system () { @@ -797,59 +797,59 @@ namespace Step21 std::vector > old_solution_values(n_q_points, Vector(dim+2)); std::vector > > old_solution_grads(n_q_points, - std::vector > (dim+2)); + std::vector > (dim+2)); const FEValuesExtractors::Vector velocities (0); const FEValuesExtractors::Scalar pressure (dim); const FEValuesExtractors::Scalar saturation (dim+1); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { fe_values.reinit (cell); local_matrix = 0; local_rhs = 0; - // Here's the first significant - // difference: We have to get the - // values of the saturation function of - // the previous time step at the - // quadrature points. To this end, we - // can use the - // FEValues::get_function_values - // (previously already used in step-9, - // step-14 and step-15), a function - // that takes a solution vector and - // returns a list of function values at - // the quadrature points of the present - // cell. In fact, it returns the - // complete vector-valued solution at - // each quadrature point, i.e. not only - // the saturation but also the - // velocities and pressure: + // Here's the first significant + // difference: We have to get the + // values of the saturation function of + // the previous time step at the + // quadrature points. To this end, we + // can use the + // FEValues::get_function_values + // (previously already used in step-9, + // step-14 and step-15), a function + // that takes a solution vector and + // returns a list of function values at + // the quadrature points of the present + // cell. In fact, it returns the + // complete vector-valued solution at + // each quadrature point, i.e. not only + // the saturation but also the + // velocities and pressure: fe_values.get_function_values (old_solution, old_solution_values); - // Then we also have to get the values - // of the pressure right hand side and - // of the inverse permeability tensor - // at the quadrature points: + // Then we also have to get the values + // of the pressure right hand side and + // of the inverse permeability tensor + // at the quadrature points: pressure_right_hand_side.value_list (fe_values.get_quadrature_points(), pressure_rhs_values); k_inverse.value_list (fe_values.get_quadrature_points(), k_inverse_values); - // With all this, we can now loop over - // all the quadrature points and shape - // functions on this cell and assemble - // those parts of the matrix and right - // hand side that we deal with in this - // function. The individual terms in - // the contributions should be - // self-explanatory given the explicit - // form of the bilinear form stated in - // the introduction: + // With all this, we can now loop over + // all the quadrature points and shape + // functions on this cell and assemble + // those parts of the matrix and right + // hand side that we deal with in this + // function. The individual terms in + // the contributions should be + // self-explanatory given the explicit + // form of the bilinear form stated in + // the introduction: for (unsigned int q=0; q::faces_per_cell; ++face_no) @@ -891,14 +891,14 @@ namespace Step21 fe_face_values.reinit (cell, face_no); pressure_boundary_values - .value_list (fe_face_values.get_quadrature_points(), - boundary_values); + .value_list (fe_face_values.get_quadrature_points(), + boundary_values); for (unsigned int q=0; q - phi_i_u = fe_face_values[velocities].value (i, q); + phi_i_u = fe_face_values[velocities].value (i, q); local_rhs(i) += -(phi_i_u * fe_face_values.normal_vector(q) * @@ -907,11 +907,11 @@ namespace Step21 } } - // The final step in the loop - // over all cells is to - // transfer local contributions - // into the global matrix and - // right hand side vector: + // The final step in the loop + // over all cells is to + // transfer local contributions + // into the global matrix and + // right hand side vector: cell->get_dof_indices (local_dof_indices); for (unsigned int i=0; i void TwoPhaseFlowProblem::assemble_rhs_S () { @@ -973,8 +973,8 @@ namespace Step21 const FEValuesExtractors::Scalar saturation (dim+1); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { local_rhs = 0; @@ -983,12 +983,12 @@ namespace Step21 fe_values.get_function_values (old_solution, old_solution_values); fe_values.get_function_values (solution, present_solution_values); - // First for the cell terms. These are, - // following the formulas in the - // introduction, $(S^n,\sigma)-(F(S^n) - // \mathbf{v}^{n+1},\nabla \sigma)$, - // where $\sigma$ is the saturation - // component of the test function: + // First for the cell terms. These are, + // following the formulas in the + // introduction, $(S^n,\sigma)-(F(S^n) + // \mathbf{v}^{n+1},\nabla \sigma)$, + // where $\sigma$ is the saturation + // component of the test function: for (unsigned int q=0; q::faces_per_cell; ++face_no) { @@ -1036,20 +1036,20 @@ namespace Step21 if (cell->at_boundary(face_no)) saturation_boundary_values - .value_list (fe_face_values.get_quadrature_points(), - neighbor_saturation); + .value_list (fe_face_values.get_quadrature_points(), + neighbor_saturation); else { const typename DoFHandler::active_cell_iterator - neighbor = cell->neighbor(face_no); + neighbor = cell->neighbor(face_no); const unsigned int - neighbor_face = cell->neighbor_of_neighbor(face_no); + neighbor_face = cell->neighbor_of_neighbor(face_no); fe_face_values_neighbor.reinit (neighbor, neighbor_face); fe_face_values_neighbor - .get_function_values (old_solution, - old_solution_values_face_neighbor); + .get_function_values (old_solution, + old_solution_values_face_neighbor); for (unsigned int q=0; q void TwoPhaseFlowProblem::solve () { const InverseMatrix > - m_inverse (system_matrix.block(0,0)); + m_inverse (system_matrix.block(0,0)); Vector tmp (solution.block(0).size()); Vector schur_rhs (solution.block(1).size()); Vector tmp2 (solution.block(2).size()); - // First the pressure, using the pressure - // Schur complement of the first two - // equations: + // First the pressure, using the pressure + // Schur complement of the first two + // equations: { m_inverse.vmult (tmp, system_rhs.block(0)); system_matrix.block(1,0).vmult (schur_rhs, tmp); @@ -1116,13 +1116,13 @@ namespace Step21 SchurComplement - schur_complement (system_matrix, m_inverse); + schur_complement (system_matrix, m_inverse); ApproximateSchurComplement - approximate_schur_complement (system_matrix); + approximate_schur_complement (system_matrix); InverseMatrix - preconditioner (approximate_schur_complement); + preconditioner (approximate_schur_complement); SolverControl solver_control (solution.block(1).size(), @@ -1138,7 +1138,7 @@ namespace Step21 << std::endl; } - // Now the velocity: + // Now the velocity: { system_matrix.block(0,1).vmult (tmp, solution.block(1)); tmp *= -1; @@ -1147,36 +1147,36 @@ namespace Step21 m_inverse.vmult (solution.block(0), tmp); } - // Finally, we have to take care of the - // saturation equation. The first business - // we have here is to determine the time - // step using the formula in the - // introduction. Knowing the shape of our - // domain and that we created the mesh by - // regular subdivision of cells, we can - // compute the diameter of each of our - // cells quite easily (in fact we use the - // linear extensions in coordinate - // directions of the cells, not the - // diameter). Note that we will learn a - // more general way to do this in step-24, - // where we use the - // GridTools::minimal_cell_diameter - // function. - // - // The maximal velocity we compute using a - // helper function to compute the maximal - // velocity defined below, and with all - // this we can evaluate our new time step - // length: + // Finally, we have to take care of the + // saturation equation. The first business + // we have here is to determine the time + // step using the formula in the + // introduction. Knowing the shape of our + // domain and that we created the mesh by + // regular subdivision of cells, we can + // compute the diameter of each of our + // cells quite easily (in fact we use the + // linear extensions in coordinate + // directions of the cells, not the + // diameter). Note that we will learn a + // more general way to do this in step-24, + // where we use the + // GridTools::minimal_cell_diameter + // function. + // + // The maximal velocity we compute using a + // helper function to compute the maximal + // velocity defined below, and with all + // this we can evaluate our new time step + // length: time_step = std::pow(0.5, double(n_refinement_steps)) / get_maximal_velocity(); - // The next step is to assemble the right - // hand side, and then to pass everything - // on for solution. At the end, we project - // back saturations onto the physically - // reasonable range: + // The next step is to assemble the right + // hand side, and then to pass everything + // on for solution. At the end, we project + // back saturations onto the physically + // reasonable range: assemble_rhs_S (); { @@ -1199,12 +1199,12 @@ namespace Step21 } - // @sect4{TwoPhaseFlowProblem::output_results} + // @sect4{TwoPhaseFlowProblem::output_results} - // There is nothing surprising here. Since - // the program will do a lot of time steps, - // we create an output file only every fifth - // time step. + // There is nothing surprising here. Since + // the program will do a lot of time steps, + // we create an output file only every fifth + // time step. template void TwoPhaseFlowProblem::output_results () const { @@ -1214,23 +1214,23 @@ namespace Step21 std::vector solution_names; switch (dim) { - case 2: - solution_names.push_back ("u"); - solution_names.push_back ("v"); - solution_names.push_back ("p"); - solution_names.push_back ("S"); - break; - - case 3: - solution_names.push_back ("u"); - solution_names.push_back ("v"); - solution_names.push_back ("w"); - solution_names.push_back ("p"); - solution_names.push_back ("S"); - break; - - default: - Assert (false, ExcNotImplemented()); + case 2: + solution_names.push_back ("u"); + solution_names.push_back ("v"); + solution_names.push_back ("p"); + solution_names.push_back ("S"); + break; + + case 3: + solution_names.push_back ("u"); + solution_names.push_back ("v"); + solution_names.push_back ("w"); + solution_names.push_back ("p"); + solution_names.push_back ("S"); + break; + + default: + Assert (false, ExcNotImplemented()); } DataOut data_out; @@ -1249,32 +1249,32 @@ namespace Step21 - // @sect4{TwoPhaseFlowProblem::project_back_saturation} - - // In this function, we simply run over all - // saturation degrees of freedom and make - // sure that if they should have left the - // physically reasonable range, that they be - // reset to the interval $[0,1]$. To do this, - // we only have to loop over all saturation - // components of the solution vector; these - // are stored in the block 2 (block 0 are the - // velocities, block 1 are the pressures). - // - // It may be instructive to note that this - // function almost never triggers when the - // time step is chosen as mentioned in the - // introduction. However, if we choose the - // timestep only slightly larger, we get - // plenty of values outside the proper - // range. Strictly speaking, the function is - // therefore unnecessary if we choose the - // time step small enough. In a sense, the - // function is therefore only a safety device - // to avoid situations where our entire - // solution becomes unphysical because - // individual degrees of freedom have become - // unphysical a few time steps earlier. + // @sect4{TwoPhaseFlowProblem::project_back_saturation} + + // In this function, we simply run over all + // saturation degrees of freedom and make + // sure that if they should have left the + // physically reasonable range, that they be + // reset to the interval $[0,1]$. To do this, + // we only have to loop over all saturation + // components of the solution vector; these + // are stored in the block 2 (block 0 are the + // velocities, block 1 are the pressures). + // + // It may be instructive to note that this + // function almost never triggers when the + // time step is chosen as mentioned in the + // introduction. However, if we choose the + // timestep only slightly larger, we get + // plenty of values outside the proper + // range. Strictly speaking, the function is + // therefore unnecessary if we choose the + // time step small enough. In a sense, the + // function is therefore only a safety device + // to avoid situations where our entire + // solution becomes unphysical because + // individual degrees of freedom have become + // unphysical a few time steps earlier. template void TwoPhaseFlowProblem::project_back_saturation () @@ -1282,20 +1282,19 @@ namespace Step21 for (unsigned int i=0; i 1) - solution.block(2)(i) = 1; + else if (solution.block(2)(i) > 1) + solution.block(2)(i) = 1; } - // @sect4{TwoPhaseFlowProblem::get_maximal_velocity} + // @sect4{TwoPhaseFlowProblem::get_maximal_velocity} - // The following function is used in - // determining the maximal allowable time - // step. What it does is to loop over all - // quadrature points in the domain and find - // what the maximal magnitude of the velocity - // is. + // The following function is used in + // determining the maximal allowable time + // step. What it does is to loop over all + // quadrature points in the domain and find + // what the maximal magnitude of the velocity + // is. template double TwoPhaseFlowProblem::get_maximal_velocity () const @@ -1311,8 +1310,8 @@ namespace Step21 double max_velocity = 0; typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { fe_values.reinit (cell); @@ -1333,38 +1332,38 @@ namespace Step21 } - // @sect4{TwoPhaseFlowProblem::run} - - // This is the final function of our main - // class. Its brevity speaks for - // itself. There are only two points worth - // noting: First, the function projects the - // initial values onto the finite element - // space at the beginning; the - // VectorTools::project function doing this - // requires an argument indicating the - // hanging node constraints. We have none in - // this program (we compute on a uniformly - // refined mesh), but the function requires - // the argument anyway, of course. So we have - // to create a constraint object. In its - // original state, constraint objects are - // unsorted, and have to be sorted (using the - // ConstraintMatrix::close function) before - // they can be used. This is what we do here, - // and which is why we can't simply call the - // VectorTools::project function with an - // anonymous temporary object - // ConstraintMatrix() as the - // second argument. - // - // The second point worth mentioning is that - // we only compute the length of the present - // time step in the middle of solving the - // linear system corresponding to each time - // step. We can therefore output the present - // end time of a time step only at the end of - // the time step. + // @sect4{TwoPhaseFlowProblem::run} + + // This is the final function of our main + // class. Its brevity speaks for + // itself. There are only two points worth + // noting: First, the function projects the + // initial values onto the finite element + // space at the beginning; the + // VectorTools::project function doing this + // requires an argument indicating the + // hanging node constraints. We have none in + // this program (we compute on a uniformly + // refined mesh), but the function requires + // the argument anyway, of course. So we have + // to create a constraint object. In its + // original state, constraint objects are + // unsorted, and have to be sorted (using the + // ConstraintMatrix::close function) before + // they can be used. This is what we do here, + // and which is why we can't simply call the + // VectorTools::project function with an + // anonymous temporary object + // ConstraintMatrix() as the + // second argument. + // + // The second point worth mentioning is that + // we only compute the length of the present + // time step in the middle of solving the + // linear system corresponding to each time + // step. We can therefore output the present + // end time of a time step only at the end of + // the time step. template void TwoPhaseFlowProblem::run () { @@ -1407,15 +1406,15 @@ namespace Step21 } - // @sect3{The main function} +// @sect3{The main function} - // That's it. In the main function, we pass - // the degree of the finite element space to - // the constructor of the TwoPhaseFlowProblem - // object. Here, we use zero-th degree - // elements, i.e. $RT_0\times DQ_0 \times - // DQ_0$. The rest is as in all the other - // programs. +// That's it. In the main function, we pass +// the degree of the finite element space to +// the constructor of the TwoPhaseFlowProblem +// object. Here, we use zero-th degree +// elements, i.e. $RT_0\times DQ_0 \times +// DQ_0$. The rest is as in all the other +// programs. int main () { try diff --git a/deal.II/examples/step-22/step-22.cc b/deal.II/examples/step-22/step-22.cc index 20bf102930..71fd586f90 100644 --- a/deal.II/examples/step-22/step-22.cc +++ b/deal.II/examples/step-22/step-22.cc @@ -10,10 +10,10 @@ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // As usual, we start by including - // some well-known files: +// As usual, we start by including +// some well-known files: #include #include #include @@ -48,174 +48,174 @@ #include #include - // Then we need to include the header file - // for the sparse direct solver UMFPACK: +// Then we need to include the header file +// for the sparse direct solver UMFPACK: #include - // This includes the library for the - // incomplete LU factorization that will - // be used as a preconditioner in 3D: +// This includes the library for the +// incomplete LU factorization that will +// be used as a preconditioner in 3D: #include - // This is C++: +// This is C++: #include #include - // As in all programs, the namespace dealii - // is included: +// As in all programs, the namespace dealii +// is included: namespace Step22 { using namespace dealii; - // @sect3{Defining the inner preconditioner type} - - // As explained in the introduction, we are - // going to use different preconditioners for - // two and three space dimensions, - // respectively. We distinguish between - // them by the use of the spatial dimension - // as a template parameter. See step-4 for - // details on templates. We are not going to - // create any preconditioner object here, all - // we do is to create class that holds a - // local typedef determining the - // preconditioner class so we can write our - // program in a dimension-independent way. + // @sect3{Defining the inner preconditioner type} + + // As explained in the introduction, we are + // going to use different preconditioners for + // two and three space dimensions, + // respectively. We distinguish between + // them by the use of the spatial dimension + // as a template parameter. See step-4 for + // details on templates. We are not going to + // create any preconditioner object here, all + // we do is to create class that holds a + // local typedef determining the + // preconditioner class so we can write our + // program in a dimension-independent way. template struct InnerPreconditioner; - // In 2D, we are going to use a sparse direct - // solver as preconditioner: + // In 2D, we are going to use a sparse direct + // solver as preconditioner: template <> struct InnerPreconditioner<2> { - typedef SparseDirectUMFPACK type; + typedef SparseDirectUMFPACK type; }; - // And the ILU preconditioning in 3D, called - // by SparseILU: + // And the ILU preconditioning in 3D, called + // by SparseILU: template <> struct InnerPreconditioner<3> { - typedef SparseILU type; + typedef SparseILU type; }; - // @sect3{The StokesProblem class template} - - // This is an adaptation of step-20, so the - // main class and the data types are the - // same as used there. In this example we - // also use adaptive grid refinement, which - // is handled in analogy to - // step-6. According to the discussion in - // the introduction, we are also going to - // use the ConstraintMatrix for - // implementing Dirichlet boundary - // conditions. Hence, we change the name - // hanging_node_constraints - // into constraints. + // @sect3{The StokesProblem class template} + + // This is an adaptation of step-20, so the + // main class and the data types are the + // same as used there. In this example we + // also use adaptive grid refinement, which + // is handled in analogy to + // step-6. According to the discussion in + // the introduction, we are also going to + // use the ConstraintMatrix for + // implementing Dirichlet boundary + // conditions. Hence, we change the name + // hanging_node_constraints + // into constraints. template class StokesProblem { - public: - StokesProblem (const unsigned int degree); - void run (); - - private: - void setup_dofs (); - void assemble_system (); - void solve (); - void output_results (const unsigned int refinement_cycle) const; - void refine_mesh (); - - const unsigned int degree; - - Triangulation triangulation; - FESystem fe; - DoFHandler dof_handler; - - ConstraintMatrix constraints; - - BlockSparsityPattern sparsity_pattern; - BlockSparseMatrix system_matrix; - - BlockVector solution; - BlockVector system_rhs; - - // This one is new: We shall use a - // so-called shared pointer structure to - // access the preconditioner. Shared - // pointers are essentially just a - // convenient form of pointers. Several - // shared pointers can point to the same - // object (just like regular pointers), - // but when the last shared pointer - // object to point to a preconditioner - // object is deleted (for example if a - // shared pointer object goes out of - // scope, if the class of which it is a - // member is destroyed, or if the pointer - // is assigned a different preconditioner - // object) then the preconditioner object - // pointed to is also destroyed. This - // ensures that we don't have to manually - // track in how many places a - // preconditioner object is still - // referenced, it can never create a - // memory leak, and can never produce a - // dangling pointer to an already - // destroyed object: - std_cxx1x::shared_ptr::type> A_preconditioner; + public: + StokesProblem (const unsigned int degree); + void run (); + + private: + void setup_dofs (); + void assemble_system (); + void solve (); + void output_results (const unsigned int refinement_cycle) const; + void refine_mesh (); + + const unsigned int degree; + + Triangulation triangulation; + FESystem fe; + DoFHandler dof_handler; + + ConstraintMatrix constraints; + + BlockSparsityPattern sparsity_pattern; + BlockSparseMatrix system_matrix; + + BlockVector solution; + BlockVector system_rhs; + + // This one is new: We shall use a + // so-called shared pointer structure to + // access the preconditioner. Shared + // pointers are essentially just a + // convenient form of pointers. Several + // shared pointers can point to the same + // object (just like regular pointers), + // but when the last shared pointer + // object to point to a preconditioner + // object is deleted (for example if a + // shared pointer object goes out of + // scope, if the class of which it is a + // member is destroyed, or if the pointer + // is assigned a different preconditioner + // object) then the preconditioner object + // pointed to is also destroyed. This + // ensures that we don't have to manually + // track in how many places a + // preconditioner object is still + // referenced, it can never create a + // memory leak, and can never produce a + // dangling pointer to an already + // destroyed object: + std_cxx1x::shared_ptr::type> A_preconditioner; }; - // @sect3{Boundary values and right hand side} - - // As in step-20 and most other - // example programs, the next task is - // to define the data for the PDE: - // For the Stokes problem, we are - // going to use natural boundary - // values on parts of the boundary - // (i.e. homogenous Neumann-type) for - // which we won't have to do anything - // special (the homogeneity implies - // that the corresponding terms in - // the weak form are simply zero), - // and boundary conditions on the - // velocity (Dirichlet-type) on the - // rest of the boundary, as described - // in the introduction. - // - // In order to enforce the Dirichlet - // boundary values on the velocity, - // we will use the - // VectorTools::interpolate_boundary_values - // function as usual which requires - // us to write a function object with - // as many components as the finite - // element has. In other words, we - // have to define the function on the - // $(u,p)$-space, but we are going to - // filter out the pressure component - // when interpolating the boundary - // values. - - // The following function object is a - // representation of the boundary - // values described in the - // introduction: + // @sect3{Boundary values and right hand side} + + // As in step-20 and most other + // example programs, the next task is + // to define the data for the PDE: + // For the Stokes problem, we are + // going to use natural boundary + // values on parts of the boundary + // (i.e. homogenous Neumann-type) for + // which we won't have to do anything + // special (the homogeneity implies + // that the corresponding terms in + // the weak form are simply zero), + // and boundary conditions on the + // velocity (Dirichlet-type) on the + // rest of the boundary, as described + // in the introduction. + // + // In order to enforce the Dirichlet + // boundary values on the velocity, + // we will use the + // VectorTools::interpolate_boundary_values + // function as usual which requires + // us to write a function object with + // as many components as the finite + // element has. In other words, we + // have to define the function on the + // $(u,p)$-space, but we are going to + // filter out the pressure component + // when interpolating the boundary + // values. + + // The following function object is a + // representation of the boundary + // values described in the + // introduction: template class BoundaryValues : public Function { - public: - BoundaryValues () : Function(dim+1) {} + public: + BoundaryValues () : Function(dim+1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void vector_value (const Point &p, - Vector &value) const; + virtual void vector_value (const Point &p, + Vector &value) const; }; @@ -244,20 +244,20 @@ namespace Step22 - // We implement similar functions for - // the right hand side which for the - // current example is simply zero: + // We implement similar functions for + // the right hand side which for the + // current example is simply zero: template class RightHandSide : public Function { - public: - RightHandSide () : Function(dim+1) {} + public: + RightHandSide () : Function(dim+1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void vector_value (const Point &p, - Vector &value) const; + virtual void vector_value (const Point &p, + Vector &value) const; }; @@ -281,82 +281,82 @@ namespace Step22 } - // @sect3{Linear solvers and preconditioners} - - // The linear solvers and preconditioners are - // discussed extensively in the - // introduction. Here, we create the - // respective objects that will be used. - - // @sect4{The InverseMatrix class template} - - // The InverseMatrix - // class represents the data - // structure for an inverse - // matrix. It is derived from the one - // in step-20. The only difference is - // that we now do include a - // preconditioner to the matrix since - // we will apply this class to - // different kinds of matrices that - // will require different - // preconditioners (in step-20 we did - // not use a preconditioner in this - // class at all). The types of matrix - // and preconditioner are passed to - // this class via template - // parameters, and matrix and - // preconditioner objects of these - // types will then be passed to the - // constructor when an - // InverseMatrix object - // is created. The member function - // vmult is, as in - // step-20, a multiplication with a - // vector, obtained by solving a - // linear system: + // @sect3{Linear solvers and preconditioners} + + // The linear solvers and preconditioners are + // discussed extensively in the + // introduction. Here, we create the + // respective objects that will be used. + + // @sect4{The InverseMatrix class template} + + // The InverseMatrix + // class represents the data + // structure for an inverse + // matrix. It is derived from the one + // in step-20. The only difference is + // that we now do include a + // preconditioner to the matrix since + // we will apply this class to + // different kinds of matrices that + // will require different + // preconditioners (in step-20 we did + // not use a preconditioner in this + // class at all). The types of matrix + // and preconditioner are passed to + // this class via template + // parameters, and matrix and + // preconditioner objects of these + // types will then be passed to the + // constructor when an + // InverseMatrix object + // is created. The member function + // vmult is, as in + // step-20, a multiplication with a + // vector, obtained by solving a + // linear system: template class InverseMatrix : public Subscriptor { - public: - InverseMatrix (const Matrix &m, - const Preconditioner &preconditioner); + public: + InverseMatrix (const Matrix &m, + const Preconditioner &preconditioner); - void vmult (Vector &dst, - const Vector &src) const; + void vmult (Vector &dst, + const Vector &src) const; - private: - const SmartPointer matrix; - const SmartPointer preconditioner; + private: + const SmartPointer matrix; + const SmartPointer preconditioner; }; template InverseMatrix::InverseMatrix (const Matrix &m, const Preconditioner &preconditioner) - : - matrix (&m), - preconditioner (&preconditioner) + : + matrix (&m), + preconditioner (&preconditioner) {} - // This is the implementation of the - // vmult function. - - // In this class we use a rather large - // tolerance for the solver control. The - // reason for this is that the function is - // used very frequently, and hence, any - // additional effort to make the residual - // in the CG solve smaller makes the - // solution more expensive. Note that we do - // not only use this class as a - // preconditioner for the Schur complement, - // but also when forming the inverse of the - // Laplace matrix – which is hence - // directly responsible for the accuracy of - // the solution itself, so we can't choose - // a too large tolerance, either. + // This is the implementation of the + // vmult function. + + // In this class we use a rather large + // tolerance for the solver control. The + // reason for this is that the function is + // used very frequently, and hence, any + // additional effort to make the residual + // in the CG solve smaller makes the + // solution more expensive. Note that we do + // not only use this class as a + // preconditioner for the Schur complement, + // but also when forming the inverse of the + // Laplace matrix – which is hence + // directly responsible for the accuracy of + // the solution itself, so we can't choose + // a too large tolerance, either. template void InverseMatrix::vmult (Vector &dst, const Vector &src) const @@ -370,36 +370,36 @@ namespace Step22 } - // @sect4{The SchurComplement class template} - - // This class implements the Schur complement - // discussed in the introduction. It is in - // analogy to step-20. Though, we now call - // it with a template parameter - // Preconditioner in order to - // access that when specifying the respective - // type of the inverse matrix class. As a - // consequence of the definition above, the - // declaration InverseMatrix now - // contains the second template parameter - // for a preconditioner class as above, which - // affects the SmartPointer - // object m_inverse as well. + // @sect4{The SchurComplement class template} + + // This class implements the Schur complement + // discussed in the introduction. It is in + // analogy to step-20. Though, we now call + // it with a template parameter + // Preconditioner in order to + // access that when specifying the respective + // type of the inverse matrix class. As a + // consequence of the definition above, the + // declaration InverseMatrix now + // contains the second template parameter + // for a preconditioner class as above, which + // affects the SmartPointer + // object m_inverse as well. template class SchurComplement : public Subscriptor { - public: - SchurComplement (const BlockSparseMatrix &system_matrix, - const InverseMatrix, Preconditioner> &A_inverse); + public: + SchurComplement (const BlockSparseMatrix &system_matrix, + const InverseMatrix, Preconditioner> &A_inverse); - void vmult (Vector &dst, - const Vector &src) const; + void vmult (Vector &dst, + const Vector &src) const; - private: - const SmartPointer > system_matrix; - const SmartPointer, Preconditioner> > A_inverse; + private: + const SmartPointer > system_matrix; + const SmartPointer, Preconditioner> > A_inverse; - mutable Vector tmp1, tmp2; + mutable Vector tmp1, tmp2; }; @@ -408,11 +408,11 @@ namespace Step22 SchurComplement:: SchurComplement (const BlockSparseMatrix &system_matrix, const InverseMatrix,Preconditioner> &A_inverse) - : - system_matrix (&system_matrix), - A_inverse (&A_inverse), - tmp1 (system_matrix.block(0,0).m()), - tmp2 (system_matrix.block(0,0).m()) + : + system_matrix (&system_matrix), + A_inverse (&A_inverse), + tmp1 (system_matrix.block(0,0).m()), + tmp2 (system_matrix.block(0,0).m()) {} @@ -426,137 +426,137 @@ namespace Step22 } - // @sect3{StokesProblem class implementation} - - // @sect4{StokesProblem::StokesProblem} - - // The constructor of this class - // looks very similar to the one of - // step-20. The constructor - // initializes the variables for the - // polynomial degree, triangulation, - // finite element system and the dof - // handler. The underlying polynomial - // functions are of order - // degree+1 for the - // vector-valued velocity components - // and of order degree - // for the pressure. This gives the - // LBB-stable element pair - // $Q_{degree+1}^d\times Q_{degree}$, - // often referred to as the - // Taylor-Hood element. - // - // Note that we initialize the triangulation - // with a MeshSmoothing argument, which - // ensures that the refinement of cells is - // done in a way that the approximation of - // the PDE solution remains well-behaved - // (problems arise if grids are too - // unstructered), see the documentation of - // Triangulation::MeshSmoothing - // for details. + // @sect3{StokesProblem class implementation} + + // @sect4{StokesProblem::StokesProblem} + + // The constructor of this class + // looks very similar to the one of + // step-20. The constructor + // initializes the variables for the + // polynomial degree, triangulation, + // finite element system and the dof + // handler. The underlying polynomial + // functions are of order + // degree+1 for the + // vector-valued velocity components + // and of order degree + // for the pressure. This gives the + // LBB-stable element pair + // $Q_{degree+1}^d\times Q_{degree}$, + // often referred to as the + // Taylor-Hood element. + // + // Note that we initialize the triangulation + // with a MeshSmoothing argument, which + // ensures that the refinement of cells is + // done in a way that the approximation of + // the PDE solution remains well-behaved + // (problems arise if grids are too + // unstructered), see the documentation of + // Triangulation::MeshSmoothing + // for details. template StokesProblem::StokesProblem (const unsigned int degree) - : - degree (degree), - triangulation (Triangulation::maximum_smoothing), - fe (FE_Q(degree+1), dim, - FE_Q(degree), 1), - dof_handler (triangulation) + : + degree (degree), + triangulation (Triangulation::maximum_smoothing), + fe (FE_Q(degree+1), dim, + FE_Q(degree), 1), + dof_handler (triangulation) {} - // @sect4{StokesProblem::setup_dofs} - - // Given a mesh, this function - // associates the degrees of freedom - // with it and creates the - // corresponding matrices and - // vectors. At the beginning it also - // releases the pointer to the - // preconditioner object (if the - // shared pointer pointed at anything - // at all at this point) since it - // will definitely not be needed any - // more after this point and will - // have to be re-computed after - // assembling the matrix, and unties - // the sparse matrix from its - // sparsity pattern object. - // - // We then proceed with distributing - // degrees of freedom and renumbering - // them: In order to make the ILU - // preconditioner (in 3D) work - // efficiently, it is important to - // enumerate the degrees of freedom - // in such a way that it reduces the - // bandwidth of the matrix, or maybe - // more importantly: in such a way - // that the ILU is as close as - // possible to a real LU - // decomposition. On the other hand, - // we need to preserve the block - // structure of velocity and pressure - // already seen in in step-20 and - // step-21. This is done in two - // steps: First, all dofs are - // renumbered to improve the ILU and - // then we renumber once again by - // components. Since - // DoFRenumbering::component_wise - // does not touch the renumbering - // within the individual blocks, the - // basic renumbering from the first - // step remains. As for how the - // renumber degrees of freedom to - // improve the ILU: deal.II has a - // number of algorithms that attempt - // to find orderings to improve ILUs, - // or reduce the bandwidth of - // matrices, or optimize some other - // aspect. The DoFRenumbering - // namespace shows a comparison of - // the results we obtain with several - // of these algorithms based on the - // testcase discussed here in this - // tutorial program. Here, we will - // use the traditional Cuthill-McKee - // algorithm already used in some of - // the previous tutorial programs. - // In the - // section on improved ILU - // we're going to discuss this issue - // in more detail. - - // There is one more change compared - // to previous tutorial programs: - // There is no reason in sorting the - // dim velocity - // components individually. In fact, - // rather than first enumerating all - // $x$-velocities, then all - // $y$-velocities, etc, we would like - // to keep all velocities at the same - // location together and only - // separate between velocities (all - // components) and pressures. By - // default, this is not what the - // DoFRenumbering::component_wise - // function does: it treats each - // vector component separately; what - // we have to do is group several - // components into "blocks" and pass - // this block structure to that - // function. Consequently, we - // allocate a vector - // block_component with - // as many elements as there are - // components and describe all - // velocity components to correspond - // to block 0, while the pressure - // component will form block 1: + // @sect4{StokesProblem::setup_dofs} + + // Given a mesh, this function + // associates the degrees of freedom + // with it and creates the + // corresponding matrices and + // vectors. At the beginning it also + // releases the pointer to the + // preconditioner object (if the + // shared pointer pointed at anything + // at all at this point) since it + // will definitely not be needed any + // more after this point and will + // have to be re-computed after + // assembling the matrix, and unties + // the sparse matrix from its + // sparsity pattern object. + // + // We then proceed with distributing + // degrees of freedom and renumbering + // them: In order to make the ILU + // preconditioner (in 3D) work + // efficiently, it is important to + // enumerate the degrees of freedom + // in such a way that it reduces the + // bandwidth of the matrix, or maybe + // more importantly: in such a way + // that the ILU is as close as + // possible to a real LU + // decomposition. On the other hand, + // we need to preserve the block + // structure of velocity and pressure + // already seen in in step-20 and + // step-21. This is done in two + // steps: First, all dofs are + // renumbered to improve the ILU and + // then we renumber once again by + // components. Since + // DoFRenumbering::component_wise + // does not touch the renumbering + // within the individual blocks, the + // basic renumbering from the first + // step remains. As for how the + // renumber degrees of freedom to + // improve the ILU: deal.II has a + // number of algorithms that attempt + // to find orderings to improve ILUs, + // or reduce the bandwidth of + // matrices, or optimize some other + // aspect. The DoFRenumbering + // namespace shows a comparison of + // the results we obtain with several + // of these algorithms based on the + // testcase discussed here in this + // tutorial program. Here, we will + // use the traditional Cuthill-McKee + // algorithm already used in some of + // the previous tutorial programs. + // In the + // section on improved ILU + // we're going to discuss this issue + // in more detail. + + // There is one more change compared + // to previous tutorial programs: + // There is no reason in sorting the + // dim velocity + // components individually. In fact, + // rather than first enumerating all + // $x$-velocities, then all + // $y$-velocities, etc, we would like + // to keep all velocities at the same + // location together and only + // separate between velocities (all + // components) and pressures. By + // default, this is not what the + // DoFRenumbering::component_wise + // function does: it treats each + // vector component separately; what + // we have to do is group several + // components into "blocks" and pass + // this block structure to that + // function. Consequently, we + // allocate a vector + // block_component with + // as many elements as there are + // components and describe all + // velocity components to correspond + // to block 0, while the pressure + // component will form block 1: template void StokesProblem::setup_dofs () { @@ -570,47 +570,47 @@ namespace Step22 block_component[dim] = 1; DoFRenumbering::component_wise (dof_handler, block_component); - // Now comes the implementation of - // Dirichlet boundary conditions, which - // should be evident after the discussion - // in the introduction. All that changed - // is that the function already appears - // in the setup functions, whereas we - // were used to see it in some assembly - // routine. Further down below where we - // set up the mesh, we will associate the - // top boundary where we impose Dirichlet - // boundary conditions with boundary - // indicator 1. We will have to pass - // this boundary indicator as second - // argument to the function below - // interpolating boundary values. There - // is one more thing, though. The - // function describing the Dirichlet - // conditions was defined for all - // components, both velocity and - // pressure. However, the Dirichlet - // conditions are to be set for the - // velocity only. To this end, we use a - // ComponentMask that only selects the - // velocity components. The component - // mask is obtained from the finite - // element by specifying the particular - // components we want. Since we use - // adaptively refined grids the - // constraint matrix needs to be first - // filled with hanging node constraints - // generated from the DoF handler. Note - // the order of the two functions — - // we first compute the hanging node - // constraints, and then insert the - // boundary values into the constraint - // matrix. This makes sure that we - // respect H1 conformity on - // boundaries with hanging nodes (in - // three space dimensions), where the - // hanging node needs to dominate the - // Dirichlet boundary values. + // Now comes the implementation of + // Dirichlet boundary conditions, which + // should be evident after the discussion + // in the introduction. All that changed + // is that the function already appears + // in the setup functions, whereas we + // were used to see it in some assembly + // routine. Further down below where we + // set up the mesh, we will associate the + // top boundary where we impose Dirichlet + // boundary conditions with boundary + // indicator 1. We will have to pass + // this boundary indicator as second + // argument to the function below + // interpolating boundary values. There + // is one more thing, though. The + // function describing the Dirichlet + // conditions was defined for all + // components, both velocity and + // pressure. However, the Dirichlet + // conditions are to be set for the + // velocity only. To this end, we use a + // ComponentMask that only selects the + // velocity components. The component + // mask is obtained from the finite + // element by specifying the particular + // components we want. Since we use + // adaptively refined grids the + // constraint matrix needs to be first + // filled with hanging node constraints + // generated from the DoF handler. Note + // the order of the two functions — + // we first compute the hanging node + // constraints, and then insert the + // boundary values into the constraint + // matrix. This makes sure that we + // respect H1 conformity on + // boundaries with hanging nodes (in + // three space dimensions), where the + // hanging node needs to dominate the + // Dirichlet boundary values. { constraints.clear (); @@ -626,17 +626,17 @@ namespace Step22 constraints.close (); - // In analogy to step-20, we count the dofs - // in the individual components. We could - // do this in the same way as there, but we - // want to operate on the block structure - // we used already for the renumbering: The - // function - // DoFTools::count_dofs_per_block - // does the same as - // DoFTools::count_dofs_per_component, - // but now grouped as velocity and pressure - // block via block_component. + // In analogy to step-20, we count the dofs + // in the individual components. We could + // do this in the same way as there, but we + // want to operate on the block structure + // we used already for the renumbering: The + // function + // DoFTools::count_dofs_per_block + // does the same as + // DoFTools::count_dofs_per_component, + // but now grouped as velocity and pressure + // block via block_component. std::vector dofs_per_block (2); DoFTools::count_dofs_per_block (dof_handler, dofs_per_block, block_component); const unsigned int n_u = dofs_per_block[0], @@ -650,84 +650,84 @@ namespace Step22 << " (" << n_u << '+' << n_p << ')' << std::endl; - // The next task is to allocate a - // sparsity pattern for the system matrix - // we will create. We could do this in - // the same way as in step-20, - // i.e. directly build an object of type - // SparsityPattern through - // DoFTools::make_sparsity_pattern. However, - // there is a major reason not to do so: - // In 3D, the function - // DoFTools::max_couplings_between_dofs - // yields a conservative but rather large - // number for the coupling between the - // individual dofs, so that the memory - // initially provided for the creation of - // the sparsity pattern of the matrix is - // far too much -- so much actually that - // the initial sparsity pattern won't - // even fit into the physical memory of - // most systems already for - // moderately-sized 3D problems, see also - // the discussion in step-18. Instead, - // we first build a temporary object that - // uses a different data structure that - // doesn't require allocating more memory - // than necessary but isn't suitable for - // use as a basis of SparseMatrix or - // BlockSparseMatrix objects; in a second - // step we then copy this object into an - // object of BlockSparsityPattern. This - // is entirely analgous to what we - // already did in step-11 and step-18. - // - // There is one snag again here, though: - // it turns out that using the - // CompressedSparsityPattern (or the - // block version - // BlockCompressedSparsityPattern we - // would use here) has a bottleneck that - // makes the algorithm to build the - // sparsity pattern be quadratic in the - // number of degrees of freedom. This - // doesn't become noticeable until we get - // well into the range of several 100,000 - // degrees of freedom, but eventually - // dominates the setup of the linear - // system when we get to more than a - // million degrees of freedom. This is - // due to the data structures used in the - // CompressedSparsityPattern class, - // nothing that can easily be - // changed. Fortunately, there is an easy - // solution: the - // CompressedSimpleSparsityPattern class - // (and its block variant - // BlockCompressedSimpleSparsityPattern) - // has exactly the same interface, uses a - // different %internal data structure and - // is linear in the number of degrees of - // freedom and therefore much more - // efficient for large problems. As - // another alternative, we could also - // have chosen the class - // BlockCompressedSetSparsityPattern that - // uses yet another strategy for %internal - // memory management. Though, that class - // turns out to be more memory-demanding - // than - // BlockCompressedSimpleSparsityPattern - // for this example. - // - // Consequently, this is the class that - // we will use for our intermediate - // sparsity representation. All this is - // done inside a new scope, which means - // that the memory of csp - // will be released once the information - // has been copied to - // sparsity_pattern. + // The next task is to allocate a + // sparsity pattern for the system matrix + // we will create. We could do this in + // the same way as in step-20, + // i.e. directly build an object of type + // SparsityPattern through + // DoFTools::make_sparsity_pattern. However, + // there is a major reason not to do so: + // In 3D, the function + // DoFTools::max_couplings_between_dofs + // yields a conservative but rather large + // number for the coupling between the + // individual dofs, so that the memory + // initially provided for the creation of + // the sparsity pattern of the matrix is + // far too much -- so much actually that + // the initial sparsity pattern won't + // even fit into the physical memory of + // most systems already for + // moderately-sized 3D problems, see also + // the discussion in step-18. Instead, + // we first build a temporary object that + // uses a different data structure that + // doesn't require allocating more memory + // than necessary but isn't suitable for + // use as a basis of SparseMatrix or + // BlockSparseMatrix objects; in a second + // step we then copy this object into an + // object of BlockSparsityPattern. This + // is entirely analgous to what we + // already did in step-11 and step-18. + // + // There is one snag again here, though: + // it turns out that using the + // CompressedSparsityPattern (or the + // block version + // BlockCompressedSparsityPattern we + // would use here) has a bottleneck that + // makes the algorithm to build the + // sparsity pattern be quadratic in the + // number of degrees of freedom. This + // doesn't become noticeable until we get + // well into the range of several 100,000 + // degrees of freedom, but eventually + // dominates the setup of the linear + // system when we get to more than a + // million degrees of freedom. This is + // due to the data structures used in the + // CompressedSparsityPattern class, + // nothing that can easily be + // changed. Fortunately, there is an easy + // solution: the + // CompressedSimpleSparsityPattern class + // (and its block variant + // BlockCompressedSimpleSparsityPattern) + // has exactly the same interface, uses a + // different %internal data structure and + // is linear in the number of degrees of + // freedom and therefore much more + // efficient for large problems. As + // another alternative, we could also + // have chosen the class + // BlockCompressedSetSparsityPattern that + // uses yet another strategy for %internal + // memory management. Though, that class + // turns out to be more memory-demanding + // than + // BlockCompressedSimpleSparsityPattern + // for this example. + // + // Consequently, this is the class that + // we will use for our intermediate + // sparsity representation. All this is + // done inside a new scope, which means + // that the memory of csp + // will be released once the information + // has been copied to + // sparsity_pattern. { BlockCompressedSimpleSparsityPattern csp (2,2); @@ -742,10 +742,10 @@ namespace Step22 sparsity_pattern.copy_from (csp); } - // Finally, the system matrix, - // solution and right hand side are - // created from the block - // structure as in step-20: + // Finally, the system matrix, + // solution and right hand side are + // created from the block + // structure as in step-20: system_matrix.reinit (sparsity_pattern); solution.reinit (2); @@ -760,16 +760,16 @@ namespace Step22 } - // @sect4{StokesProblem::assemble_system} + // @sect4{StokesProblem::assemble_system} - // The assembly process follows the - // discussion in step-20 and in the - // introduction. We use the well-known - // abbreviations for the data structures - // that hold the local matrix, right - // hand side, and global - // numbering of the degrees of freedom - // for the present cell. + // The assembly process follows the + // discussion in step-20 and in the + // introduction. We use the well-known + // abbreviations for the data structures + // that hold the local matrix, right + // hand side, and global + // numbering of the degrees of freedom + // for the present cell. template void StokesProblem::assemble_system () { @@ -797,70 +797,70 @@ namespace Step22 std::vector > rhs_values (n_q_points, Vector(dim+1)); - // Next, we need two objects that work as - // extractors for the FEValues - // object. Their use is explained in detail - // in the report on @ref vector_valued : + // Next, we need two objects that work as + // extractors for the FEValues + // object. Their use is explained in detail + // in the report on @ref vector_valued : const FEValuesExtractors::Vector velocities (0); const FEValuesExtractors::Scalar pressure (dim); - // As an extension over step-20 and - // step-21, we include a few - // optimizations that make assembly - // much faster for this particular - // problem. The improvements are - // based on the observation that we - // do a few calculations too many - // times when we do as in step-20: - // The symmetric gradient actually - // has dofs_per_cell - // different values per quadrature - // point, but we extract it - // dofs_per_cell*dofs_per_cell - // times from the FEValues object - - // for both the loop over - // i and the inner - // loop over j. In 3d, - // that means evaluating it - // $89^2=7921$ instead of $89$ - // times, a not insignificant - // difference. - // - // So what we're - // going to do here is to avoid - // such repeated calculations by - // getting a vector of rank-2 - // tensors (and similarly for - // the divergence and the basis - // function value on pressure) - // at the quadrature point prior - // to starting the loop over the - // dofs on the cell. First, we - // create the respective objects - // that will hold these - // values. Then, we start the - // loop over all cells and the loop - // over the quadrature points, - // where we first extract these - // values. There is one more - // optimization we implement here: - // the local matrix (as well as - // the global one) is going to - // be symmetric, since all - // the operations involved are - // symmetric with respect to $i$ - // and $j$. This is implemented by - // simply running the inner loop - // not to dofs_per_cell, - // but only up to i, - // the index of the outer loop. + // As an extension over step-20 and + // step-21, we include a few + // optimizations that make assembly + // much faster for this particular + // problem. The improvements are + // based on the observation that we + // do a few calculations too many + // times when we do as in step-20: + // The symmetric gradient actually + // has dofs_per_cell + // different values per quadrature + // point, but we extract it + // dofs_per_cell*dofs_per_cell + // times from the FEValues object - + // for both the loop over + // i and the inner + // loop over j. In 3d, + // that means evaluating it + // $89^2=7921$ instead of $89$ + // times, a not insignificant + // difference. + // + // So what we're + // going to do here is to avoid + // such repeated calculations by + // getting a vector of rank-2 + // tensors (and similarly for + // the divergence and the basis + // function value on pressure) + // at the quadrature point prior + // to starting the loop over the + // dofs on the cell. First, we + // create the respective objects + // that will hold these + // values. Then, we start the + // loop over all cells and the loop + // over the quadrature points, + // where we first extract these + // values. There is one more + // optimization we implement here: + // the local matrix (as well as + // the global one) is going to + // be symmetric, since all + // the operations involved are + // symmetric with respect to $i$ + // and $j$. This is implemented by + // simply running the inner loop + // not to dofs_per_cell, + // but only up to i, + // the index of the outer loop. std::vector > symgrad_phi_u (dofs_per_cell); std::vector div_phi_u (dofs_per_cell); std::vector phi_p (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { fe_values.reinit (cell); @@ -899,47 +899,47 @@ namespace Step22 } } - // Note that in the above computation - // of the local matrix contribution - // we added the term phi_p[i] * - // phi_p[j] , yielding a - // pressure mass matrix in the - // $(1,1)$ block of the matrix as - // discussed in the - // introduction. That this term only - // ends up in the $(1,1)$ block stems - // from the fact that both of the - // factors in phi_p[i] * - // phi_p[j] are only non-zero - // when all the other terms vanish - // (and the other way around). - // - // Note also that operator* is - // overloaded for symmetric - // tensors, yielding the scalar - // product between the two - // tensors in the first line of - // the local matrix - // contribution. - - // Before we can write the local data - // into the global matrix (and - // simultaneously use the - // ConstraintMatrix object to apply - // Dirichlet boundary conditions and - // eliminate hanging node - // constraints, as we discussed in - // the introduction), we have to be - // careful about one thing, - // though. We have only build up half - // of the local matrix because of - // symmetry, but we're going to save - // the full system matrix in order to - // use the standard functions for - // solution. This is done by flipping - // the indices in case we are - // pointing into the empty part of - // the local matrix. + // Note that in the above computation + // of the local matrix contribution + // we added the term phi_p[i] * + // phi_p[j] , yielding a + // pressure mass matrix in the + // $(1,1)$ block of the matrix as + // discussed in the + // introduction. That this term only + // ends up in the $(1,1)$ block stems + // from the fact that both of the + // factors in phi_p[i] * + // phi_p[j] are only non-zero + // when all the other terms vanish + // (and the other way around). + // + // Note also that operator* is + // overloaded for symmetric + // tensors, yielding the scalar + // product between the two + // tensors in the first line of + // the local matrix + // contribution. + + // Before we can write the local data + // into the global matrix (and + // simultaneously use the + // ConstraintMatrix object to apply + // Dirichlet boundary conditions and + // eliminate hanging node + // constraints, as we discussed in + // the introduction), we have to be + // careful about one thing, + // though. We have only build up half + // of the local matrix because of + // symmetry, but we're going to save + // the full system matrix in order to + // use the standard functions for + // solution. This is done by flipping + // the indices in case we are + // pointing into the empty part of + // the local matrix. for (unsigned int i=0; iblock(0,0) in the - // system matrix. As mentioned - // above, this depends on the - // spatial dimension. Since the two - // classes described by the - // InnerPreconditioner::type - // typedef have the same interface, - // we do not have to do anything - // different whether we want to use - // a sparse direct solver or an - // ILU: + // Before we're going to solve this + // linear system, we generate a + // preconditioner for the + // velocity-velocity matrix, i.e., + // block(0,0) in the + // system matrix. As mentioned + // above, this depends on the + // spatial dimension. Since the two + // classes described by the + // InnerPreconditioner::type + // typedef have the same interface, + // we do not have to do anything + // different whether we want to use + // a sparse direct solver or an + // ILU: std::cout << " Computing preconditioner..." << std::endl << std::flush; A_preconditioner @@ -976,37 +976,37 @@ namespace Step22 - // @sect4{StokesProblem::solve} + // @sect4{StokesProblem::solve} - // After the discussion in the introduction - // and the definition of the respective - // classes above, the implementation of the - // solve function is rather - // straigt-forward and done in a similar way - // as in step-20. To start with, we need an - // object of the InverseMatrix - // class that represents the inverse of the - // matrix A. As described in the - // introduction, the inverse is generated - // with the help of an inner preconditioner - // of type - // InnerPreconditioner::type. + // After the discussion in the introduction + // and the definition of the respective + // classes above, the implementation of the + // solve function is rather + // straigt-forward and done in a similar way + // as in step-20. To start with, we need an + // object of the InverseMatrix + // class that represents the inverse of the + // matrix A. As described in the + // introduction, the inverse is generated + // with the help of an inner preconditioner + // of type + // InnerPreconditioner::type. template void StokesProblem::solve () { const InverseMatrix, - typename InnerPreconditioner::type> - A_inverse (system_matrix.block(0,0), *A_preconditioner); + typename InnerPreconditioner::type> + A_inverse (system_matrix.block(0,0), *A_preconditioner); Vector tmp (solution.block(0).size()); - // This is as in step-20. We generate the - // right hand side $B A^{-1} F - G$ for the - // Schur complement and an object that - // represents the respective linear - // operation $B A^{-1} B^T$, now with a - // template parameter indicating the - // preconditioner - in accordance with the - // definition of the class. + // This is as in step-20. We generate the + // right hand side $B A^{-1} F - G$ for the + // Schur complement and an object that + // represents the respective linear + // operation $B A^{-1} B^T$, now with a + // template parameter indicating the + // preconditioner - in accordance with the + // definition of the class. { Vector schur_rhs (solution.block(1).size()); A_inverse.vmult (tmp, system_rhs.block(0)); @@ -1014,79 +1014,79 @@ namespace Step22 schur_rhs -= system_rhs.block(1); SchurComplement::type> - schur_complement (system_matrix, A_inverse); + schur_complement (system_matrix, A_inverse); - // The usual control structures for - // the solver call are created... + // The usual control structures for + // the solver call are created... SolverControl solver_control (solution.block(1).size(), 1e-6*schur_rhs.l2_norm()); SolverCG<> cg (solver_control); - // Now to the preconditioner to the - // Schur complement. As explained in - // the introduction, the - // preconditioning is done by a mass - // matrix in the pressure variable. It - // is stored in the $(1,1)$ block of - // the system matrix (that is not used - // anywhere else but in - // preconditioning). - // - // Actually, the solver needs to have - // the preconditioner in the form - // $P^{-1}$, so we need to create an - // inverse operation. Once again, we - // use an object of the class - // InverseMatrix, which - // implements the vmult - // operation that is needed by the - // solver. In this case, we have to - // invert the pressure mass matrix. As - // it already turned out in earlier - // tutorial programs, the inversion of - // a mass matrix is a rather cheap and - // straight-forward operation (compared - // to, e.g., a Laplace matrix). The CG - // method with ILU preconditioning - // converges in 5-10 steps, - // independently on the mesh size. - // This is precisely what we do here: - // We choose another ILU preconditioner - // and take it along to the - // InverseMatrix object via the - // corresponding template parameter. A - // CG solver is then called within the - // vmult operation of the inverse - // matrix. - // - // An alternative that is cheaper to - // build, but needs more iterations - // afterwards, would be to choose a - // SSOR preconditioner with factor - // 1.2. It needs about twice the number - // of iterations, but the costs for its - // generation are almost neglible. + // Now to the preconditioner to the + // Schur complement. As explained in + // the introduction, the + // preconditioning is done by a mass + // matrix in the pressure variable. It + // is stored in the $(1,1)$ block of + // the system matrix (that is not used + // anywhere else but in + // preconditioning). + // + // Actually, the solver needs to have + // the preconditioner in the form + // $P^{-1}$, so we need to create an + // inverse operation. Once again, we + // use an object of the class + // InverseMatrix, which + // implements the vmult + // operation that is needed by the + // solver. In this case, we have to + // invert the pressure mass matrix. As + // it already turned out in earlier + // tutorial programs, the inversion of + // a mass matrix is a rather cheap and + // straight-forward operation (compared + // to, e.g., a Laplace matrix). The CG + // method with ILU preconditioning + // converges in 5-10 steps, + // independently on the mesh size. + // This is precisely what we do here: + // We choose another ILU preconditioner + // and take it along to the + // InverseMatrix object via the + // corresponding template parameter. A + // CG solver is then called within the + // vmult operation of the inverse + // matrix. + // + // An alternative that is cheaper to + // build, but needs more iterations + // afterwards, would be to choose a + // SSOR preconditioner with factor + // 1.2. It needs about twice the number + // of iterations, but the costs for its + // generation are almost neglible. SparseILU preconditioner; preconditioner.initialize (system_matrix.block(1,1), SparseILU::AdditionalData()); InverseMatrix,SparseILU > - m_inverse (system_matrix.block(1,1), preconditioner); - - // With the Schur complement and an - // efficient preconditioner at hand, we - // can solve the respective equation - // for the pressure (i.e. block 0 in - // the solution vector) in the usual - // way: + m_inverse (system_matrix.block(1,1), preconditioner); + + // With the Schur complement and an + // efficient preconditioner at hand, we + // can solve the respective equation + // for the pressure (i.e. block 0 in + // the solution vector) in the usual + // way: cg.solve (schur_complement, solution.block(1), schur_rhs, m_inverse); - // After this first solution step, the - // hanging node constraints have to be - // distributed to the solution in order - // to achieve a consistent pressure - // field. + // After this first solution step, the + // hanging node constraints have to be + // distributed to the solution in order + // to achieve a consistent pressure + // field. constraints.distribute (solution); std::cout << " " @@ -1095,17 +1095,17 @@ namespace Step22 << std::endl; } - // As in step-20, we finally need to - // solve for the velocity equation where - // we plug in the solution to the - // pressure equation. This involves only - // objects we already know - so we simply - // multiply $p$ by $B^T$, subtract the - // right hand side and multiply by the - // inverse of $A$. At the end, we need to - // distribute the constraints from - // hanging nodes in order to obtain a - // constistent flow field: + // As in step-20, we finally need to + // solve for the velocity equation where + // we plug in the solution to the + // pressure equation. This involves only + // objects we already know - so we simply + // multiply $p$ by $B^T$, subtract the + // right hand side and multiply by the + // inverse of $A$. At the end, we need to + // distribute the constraints from + // hanging nodes in order to obtain a + // constistent flow field: { system_matrix.block(0,1).vmult (tmp, solution.block(1)); tmp *= -1; @@ -1118,48 +1118,48 @@ namespace Step22 } - // @sect4{StokesProblem::output_results} - - // The next function generates graphical - // output. In this example, we are going to - // use the VTK file format. We attach - // names to the individual variables in the - // problem: velocity to the - // dim components of velocity - // and pressure to the - // pressure. - // - // Not all visualization programs have the - // ability to group individual vector - // components into a vector to provide - // vector plots; in particular, this holds - // for some VTK-based visualization - // programs. In this case, the logical - // grouping of components into vectors - // should already be described in the file - // containing the data. In other words, - // what we need to do is provide our output - // writers with a way to know which of the - // components of the finite element - // logically form a vector (with $d$ - // components in $d$ space dimensions) - // rather than letting them assume that we - // simply have a bunch of scalar fields. - // This is achieved using the members of - // the - // DataComponentInterpretation - // namespace: as with the filename, we - // create a vector in which the first - // dim components refer to the - // velocities and are given the tag - // DataComponentInterpretation::component_is_part_of_vector; - // we finally push one tag - // DataComponentInterpretation::component_is_scalar - // to describe the grouping of the pressure - // variable. - - // The rest of the function is then - // the same as in step-20. + // @sect4{StokesProblem::output_results} + + // The next function generates graphical + // output. In this example, we are going to + // use the VTK file format. We attach + // names to the individual variables in the + // problem: velocity to the + // dim components of velocity + // and pressure to the + // pressure. + // + // Not all visualization programs have the + // ability to group individual vector + // components into a vector to provide + // vector plots; in particular, this holds + // for some VTK-based visualization + // programs. In this case, the logical + // grouping of components into vectors + // should already be described in the file + // containing the data. In other words, + // what we need to do is provide our output + // writers with a way to know which of the + // components of the finite element + // logically form a vector (with $d$ + // components in $d$ space dimensions) + // rather than letting them assume that we + // simply have a bunch of scalar fields. + // This is achieved using the members of + // the + // DataComponentInterpretation + // namespace: as with the filename, we + // create a vector in which the first + // dim components refer to the + // velocities and are given the tag + // DataComponentInterpretation::component_is_part_of_vector; + // we finally push one tag + // DataComponentInterpretation::component_is_scalar + // to describe the grouping of the pressure + // variable. + + // The rest of the function is then + // the same as in step-20. template void StokesProblem::output_results (const unsigned int refinement_cycle) const @@ -1168,10 +1168,10 @@ namespace Step22 solution_names.push_back ("pressure"); std::vector - data_component_interpretation - (dim, DataComponentInterpretation::component_is_part_of_vector); data_component_interpretation - .push_back (DataComponentInterpretation::component_is_scalar); + (dim, DataComponentInterpretation::component_is_part_of_vector); + data_component_interpretation + .push_back (DataComponentInterpretation::component_is_scalar); DataOut data_out; data_out.attach_dof_handler (dof_handler); @@ -1190,25 +1190,25 @@ namespace Step22 } - // @sect4{StokesProblem::refine_mesh} - - // This is the last interesting function of - // the StokesProblem class. - // As indicated by its name, it takes the - // solution to the problem and refines the - // mesh where this is needed. The procedure - // is the same as in the respective step in - // step-6, with the exception that we base - // the refinement only on the change in - // pressure, i.e., we call the Kelly error - // estimator with a mask object of type - // ComponentMask that selects the single - // scalar component for the pressure that - // we are interested in (we get such a mask - // from the finite element class by - // specifying the component we - // want). Additionally, we do not coarsen - // the grid again: + // @sect4{StokesProblem::refine_mesh} + + // This is the last interesting function of + // the StokesProblem class. + // As indicated by its name, it takes the + // solution to the problem and refines the + // mesh where this is needed. The procedure + // is the same as in the respective step in + // step-6, with the exception that we base + // the refinement only on the change in + // pressure, i.e., we call the Kelly error + // estimator with a mask object of type + // ComponentMask that selects the single + // scalar component for the pressure that + // we are interested in (we get such a mask + // from the finite element class by + // specifying the component we + // want). Additionally, we do not coarsen + // the grid again: template void StokesProblem::refine_mesh () @@ -1230,27 +1230,27 @@ namespace Step22 } - // @sect4{StokesProblem::run} - - // The last step in the Stokes class is, as - // usual, the function that generates the - // initial grid and calls the other - // functions in the respective order. - // - // We start off with a rectangle of size $4 - // \times 1$ (in 2d) or $4 \times 1 \times - // 1$ (in 3d), placed in $R^2/R^3$ as - // $(-2,2)\times(-1,0)$ or - // $(-2,2)\times(0,1)\times(-1,0)$, - // respectively. It is natural to start - // with equal mesh size in each direction, - // so we subdivide the initial rectangle - // four times in the first coordinate - // direction. To limit the scope of the - // variables involved in the creation of - // the mesh to the range where we actually - // need them, we put the entire block - // between a pair of braces: + // @sect4{StokesProblem::run} + + // The last step in the Stokes class is, as + // usual, the function that generates the + // initial grid and calls the other + // functions in the respective order. + // + // We start off with a rectangle of size $4 + // \times 1$ (in 2d) or $4 \times 1 \times + // 1$ (in 3d), placed in $R^2/R^3$ as + // $(-2,2)\times(-1,0)$ or + // $(-2,2)\times(0,1)\times(-1,0)$, + // respectively. It is natural to start + // with equal mesh size in each direction, + // so we subdivide the initial rectangle + // four times in the first coordinate + // direction. To limit the scope of the + // variables involved in the creation of + // the mesh to the range where we actually + // need them, we put the entire block + // between a pair of braces: template void StokesProblem::run () { @@ -1271,32 +1271,32 @@ namespace Step22 top_right); } - // A boundary indicator of 1 is set to all - // boundaries that are subject to Dirichlet - // boundary conditions, i.e. to faces that - // are located at 0 in the last coordinate - // direction. See the example description - // above for details. + // A boundary indicator of 1 is set to all + // boundaries that are subject to Dirichlet + // boundary conditions, i.e. to faces that + // are located at 0 in the last coordinate + // direction. See the example description + // above for details. for (typename Triangulation::active_cell_iterator - cell = triangulation.begin_active(); + cell = triangulation.begin_active(); cell != triangulation.end(); ++cell) for (unsigned int f=0; f::faces_per_cell; ++f) if (cell->face(f)->center()[dim-1] == 0) cell->face(f)->set_all_boundary_indicators(1); - // We then apply an initial refinement - // before solving for the first time. In - // 3D, there are going to be more degrees - // of freedom, so we refine less there: + // We then apply an initial refinement + // before solving for the first time. In + // 3D, there are going to be more degrees + // of freedom, so we refine less there: triangulation.refine_global (4-dim); - // As first seen in step-6, we cycle over - // the different refinement levels and - // refine (except for the first cycle), - // setup the degrees of freedom and - // matrices, assemble, solve and create - // output: + // As first seen in step-6, we cycle over + // the different refinement levels and + // refine (except for the first cycle), + // setup the degrees of freedom and + // matrices, assemble, solve and create + // output: for (unsigned int refinement_cycle = 0; refinement_cycle<6; ++refinement_cycle) { @@ -1321,12 +1321,12 @@ namespace Step22 } - // @sect3{The main function} +// @sect3{The main function} - // The main function is the same as in - // step-20. We pass the element degree as a - // parameter and choose the space dimension - // at the well-known template slot. +// The main function is the same as in +// step-20. We pass the element degree as a +// parameter and choose the space dimension +// at the well-known template slot. int main () { try diff --git a/deal.II/examples/step-23/step-23.cc b/deal.II/examples/step-23/step-23.cc index 6b09a2329c..f9b6c34f5c 100644 --- a/deal.II/examples/step-23/step-23.cc +++ b/deal.II/examples/step-23/step-23.cc @@ -11,11 +11,11 @@ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // We start with the usual assortment - // of include files that we've seen - // in so many of the previous tests: +// We start with the usual assortment +// of include files that we've seen +// in so many of the previous tests: #include #include #include @@ -44,188 +44,188 @@ #include #include - // Here are the only three include - // files of some new interest: The - // first one is already used, for - // example, for the - // VectorTools::interpolate_boundary_values - // and - // VectorTools::apply_boundary_values - // functions. However, we here use - // another function in that class, - // VectorTools::project to compute - // our initial values as the $L^2$ - // projection of the continuous - // initial values. Furthermore, we - // use - // VectorTools::create_right_hand_side - // to generate the integrals - // $(f^n,\phi^n_i)$. These were - // previously always generated by - // hand in - // assemble_system or - // similar functions in application - // code. However, we're too lazy to - // do that here, so simply use a - // library function: +// Here are the only three include +// files of some new interest: The +// first one is already used, for +// example, for the +// VectorTools::interpolate_boundary_values +// and +// VectorTools::apply_boundary_values +// functions. However, we here use +// another function in that class, +// VectorTools::project to compute +// our initial values as the $L^2$ +// projection of the continuous +// initial values. Furthermore, we +// use +// VectorTools::create_right_hand_side +// to generate the integrals +// $(f^n,\phi^n_i)$. These were +// previously always generated by +// hand in +// assemble_system or +// similar functions in application +// code. However, we're too lazy to +// do that here, so simply use a +// library function: #include - // In a very similar vein, we are - // also too lazy to write the code to - // assemble mass and Laplace - // matrices, although it would have - // only taken copying the relevant - // code from any number of previous - // tutorial programs. Rather, we want - // to focus on the things that are - // truly new to this program and - // therefore use the - // MatrixTools::create_mass_matrix - // and - // MatrixTools::create_laplace_matrix - // functions. They are declared here: +// In a very similar vein, we are +// also too lazy to write the code to +// assemble mass and Laplace +// matrices, although it would have +// only taken copying the relevant +// code from any number of previous +// tutorial programs. Rather, we want +// to focus on the things that are +// truly new to this program and +// therefore use the +// MatrixTools::create_mass_matrix +// and +// MatrixTools::create_laplace_matrix +// functions. They are declared here: #include - // Finally, here is an include file - // that contains all sorts of tool - // functions that one sometimes - // needs. In particular, we need the - // Utilities::int_to_string class - // that, given an integer argument, - // returns a string representation of - // it. It is particularly useful - // since it allows for a second - // parameter indicating the number of - // digits to which we want the result - // padded with leading zeros. We will - // use this to write output files - // that have the form - // solution-XXX.gnuplot - // where XXX denotes the - // number of the time step and always - // consists of three digits even if - // we are still in the single or - // double digit time steps. +// Finally, here is an include file +// that contains all sorts of tool +// functions that one sometimes +// needs. In particular, we need the +// Utilities::int_to_string class +// that, given an integer argument, +// returns a string representation of +// it. It is particularly useful +// since it allows for a second +// parameter indicating the number of +// digits to which we want the result +// padded with leading zeros. We will +// use this to write output files +// that have the form +// solution-XXX.gnuplot +// where XXX denotes the +// number of the time step and always +// consists of three digits even if +// we are still in the single or +// double digit time steps. #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step23 { using namespace dealii; - // @sect3{The WaveEquation class} - - // Next comes the declaration of the main - // class. It's public interface of functions - // is like in most of the other tutorial - // programs. Worth mentioning is that we now - // have to store four matrices instead of - // one: the mass matrix $M$, the Laplace - // matrix $A$, the matrix $M+k^2\theta^2A$ - // used for solving for $U^n$, and a copy of - // the mass matrix with boundary conditions - // applied used for solving for $V^n$. Note - // that it is a bit wasteful to have an - // additional copy of the mass matrix - // around. We will discuss strategies for how - // to avoid this in the section on possible - // improvements. - // - // Likewise, we need solution vectors for - // $U^n,V^n$ as well as for the corresponding - // vectors at the previous time step, - // $U^{n-1},V^{n-1}$. The - // system_rhs will be used for - // whatever right hand side vector we have - // when solving one of the two linear systems - // in each time step. These will be solved in - // the two functions solve_u and - // solve_v. - // - // Finally, the variable - // theta is used to - // indicate the parameter $\theta$ - // that is used to define which time - // stepping scheme to use, as - // explained in the introduction. The - // rest is self-explanatory. + // @sect3{The WaveEquation class} + + // Next comes the declaration of the main + // class. It's public interface of functions + // is like in most of the other tutorial + // programs. Worth mentioning is that we now + // have to store four matrices instead of + // one: the mass matrix $M$, the Laplace + // matrix $A$, the matrix $M+k^2\theta^2A$ + // used for solving for $U^n$, and a copy of + // the mass matrix with boundary conditions + // applied used for solving for $V^n$. Note + // that it is a bit wasteful to have an + // additional copy of the mass matrix + // around. We will discuss strategies for how + // to avoid this in the section on possible + // improvements. + // + // Likewise, we need solution vectors for + // $U^n,V^n$ as well as for the corresponding + // vectors at the previous time step, + // $U^{n-1},V^{n-1}$. The + // system_rhs will be used for + // whatever right hand side vector we have + // when solving one of the two linear systems + // in each time step. These will be solved in + // the two functions solve_u and + // solve_v. + // + // Finally, the variable + // theta is used to + // indicate the parameter $\theta$ + // that is used to define which time + // stepping scheme to use, as + // explained in the introduction. The + // rest is self-explanatory. template class WaveEquation { - public: - WaveEquation (); - void run (); - - private: - void setup_system (); - void solve_u (); - void solve_v (); - void output_results () const; - - Triangulation triangulation; - FE_Q fe; - DoFHandler dof_handler; - - ConstraintMatrix constraints; - - SparsityPattern sparsity_pattern; - SparseMatrix mass_matrix; - SparseMatrix laplace_matrix; - SparseMatrix matrix_u; - SparseMatrix matrix_v; - - Vector solution_u, solution_v; - Vector old_solution_u, old_solution_v; - Vector system_rhs; - - double time, time_step; - unsigned int timestep_number; - const double theta; + public: + WaveEquation (); + void run (); + + private: + void setup_system (); + void solve_u (); + void solve_v (); + void output_results () const; + + Triangulation triangulation; + FE_Q fe; + DoFHandler dof_handler; + + ConstraintMatrix constraints; + + SparsityPattern sparsity_pattern; + SparseMatrix mass_matrix; + SparseMatrix laplace_matrix; + SparseMatrix matrix_u; + SparseMatrix matrix_v; + + Vector solution_u, solution_v; + Vector old_solution_u, old_solution_v; + Vector system_rhs; + + double time, time_step; + unsigned int timestep_number; + const double theta; }; - // @sect3{Equation data} - - // Before we go on filling in the - // details of the main class, let us - // define the equation data - // corresponding to the problem, - // i.e. initial and boundary values - // for both the solution $u$ and its - // time derivative $v$, as well as a - // right hand side class. We do so - // using classes derived from the - // Function class template that has - // been used many times before, so - // the following should not be a - // surprise. - // - // Let's start with initial values - // and choose zero for both the value - // $u$ as well as its time - // derivative, the velocity $v$: + // @sect3{Equation data} + + // Before we go on filling in the + // details of the main class, let us + // define the equation data + // corresponding to the problem, + // i.e. initial and boundary values + // for both the solution $u$ and its + // time derivative $v$, as well as a + // right hand side class. We do so + // using classes derived from the + // Function class template that has + // been used many times before, so + // the following should not be a + // surprise. + // + // Let's start with initial values + // and choose zero for both the value + // $u$ as well as its time + // derivative, the velocity $v$: template class InitialValuesU : public Function { - public: - InitialValuesU () : Function() {} + public: + InitialValuesU () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; template class InitialValuesV : public Function { - public: - InitialValuesV () : Function() {} + public: + InitialValuesV () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -250,17 +250,17 @@ namespace Step23 - // Secondly, we have the right hand - // side forcing term. Boring as we - // are, we choose zero here as well: + // Secondly, we have the right hand + // side forcing term. Boring as we + // are, we choose zero here as well: template class RightHandSide : public Function { - public: - RightHandSide () : Function() {} + public: + RightHandSide () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -275,18 +275,18 @@ namespace Step23 - // Finally, we have boundary values for $u$ - // and $v$. They are as described in the - // introduction, one being the time - // derivative of the other: + // Finally, we have boundary values for $u$ + // and $v$. They are as described in the + // introduction, one being the time + // derivative of the other: template class BoundaryValuesU : public Function { - public: - BoundaryValuesU () : Function() {} + public: + BoundaryValuesU () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -295,11 +295,11 @@ namespace Step23 template class BoundaryValuesV : public Function { - public: - BoundaryValuesV () : Function() {} + public: + BoundaryValuesV () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -341,44 +341,44 @@ namespace Step23 - // @sect3{Implementation of the WaveEquation class} + // @sect3{Implementation of the WaveEquation class} - // The implementation of the actual logic is - // actually fairly short, since we relegate - // things like assembling the matrices and - // right hand side vectors to the - // library. The rest boils down to not much - // more than 130 lines of actual code, a - // significant fraction of which is - // boilerplate code that can be taken from - // previous example programs (e.g. the - // functions that solve linear systems, or - // that generate output). - // - // Let's start with the constructor (for an - // explanation of the choice of time step, - // see the section on Courant, Friedrichs, - // and Lewy in the introduction): + // The implementation of the actual logic is + // actually fairly short, since we relegate + // things like assembling the matrices and + // right hand side vectors to the + // library. The rest boils down to not much + // more than 130 lines of actual code, a + // significant fraction of which is + // boilerplate code that can be taken from + // previous example programs (e.g. the + // functions that solve linear systems, or + // that generate output). + // + // Let's start with the constructor (for an + // explanation of the choice of time step, + // see the section on Courant, Friedrichs, + // and Lewy in the introduction): template WaveEquation::WaveEquation () : - fe (1), - dof_handler (triangulation), - time_step (1./64), - theta (0.5) + fe (1), + dof_handler (triangulation), + time_step (1./64), + theta (0.5) {} - // @sect4{WaveEquation::setup_system} + // @sect4{WaveEquation::setup_system} - // The next function is the one that - // sets up the mesh, DoFHandler, and - // matrices and vectors at the - // beginning of the program, - // i.e. before the first time - // step. The first few lines are - // pretty much standard if you've - // read through the tutorial programs - // at least up to step-6: + // The next function is the one that + // sets up the mesh, DoFHandler, and + // matrices and vectors at the + // beginning of the program, + // i.e. before the first time + // step. The first few lines are + // pretty much standard if you've + // read through the tutorial programs + // at least up to step-6: template void WaveEquation::setup_system () { @@ -402,44 +402,44 @@ namespace Step23 DoFTools::make_sparsity_pattern (dof_handler, sparsity_pattern); sparsity_pattern.compress(); - // Then comes a block where we have to - // initialize the 3 matrices we need in the - // course of the program: the mass matrix, - // the laplace matrix, and the matrix - // $M+k^2\theta^2A$ used when solving for - // $U^n$ in each time step. - // - // When setting up these matrices, note - // that they all make use of the same - // sparsity pattern object. Finally, the - // reason why matrices and sparsity - // patterns are separate objects in deal.II - // (unlike in many other finite element or - // linear algebra classes) becomes clear: - // in a significant fraction of - // applications, one has to hold several - // matrices that happen to have the same - // sparsity pattern, and there is no reason - // for them not to share this information, - // rather than re-building and wasting - // memory on it several times. - // - // After initializing all of these - // matrices, we call library functions that - // build the Laplace and mass matrices. All - // they need is a DoFHandler object and a - // quadrature formula object that is to be - // used for numerical integration. Note - // that in many respects these functions - // are better than what we would usually do - // in application programs, for example - // because they automatically parallelize - // building the matrices if multiple - // processors are available in a - // machine. The matrices for solving linear - // systems will be filled in the run() - // method because we need to re-apply - // boundary conditions every time step. + // Then comes a block where we have to + // initialize the 3 matrices we need in the + // course of the program: the mass matrix, + // the laplace matrix, and the matrix + // $M+k^2\theta^2A$ used when solving for + // $U^n$ in each time step. + // + // When setting up these matrices, note + // that they all make use of the same + // sparsity pattern object. Finally, the + // reason why matrices and sparsity + // patterns are separate objects in deal.II + // (unlike in many other finite element or + // linear algebra classes) becomes clear: + // in a significant fraction of + // applications, one has to hold several + // matrices that happen to have the same + // sparsity pattern, and there is no reason + // for them not to share this information, + // rather than re-building and wasting + // memory on it several times. + // + // After initializing all of these + // matrices, we call library functions that + // build the Laplace and mass matrices. All + // they need is a DoFHandler object and a + // quadrature formula object that is to be + // used for numerical integration. Note + // that in many respects these functions + // are better than what we would usually do + // in application programs, for example + // because they automatically parallelize + // building the matrices if multiple + // processors are available in a + // machine. The matrices for solving linear + // systems will be filled in the run() + // method because we need to re-apply + // boundary conditions every time step. mass_matrix.reinit (sparsity_pattern); laplace_matrix.reinit (sparsity_pattern); matrix_u.reinit (sparsity_pattern); @@ -450,17 +450,17 @@ namespace Step23 MatrixCreator::create_laplace_matrix (dof_handler, QGauss(3), laplace_matrix); - // The rest of the function is spent on - // setting vector sizes to the correct - // value. The final line closes the hanging - // node constraints object. Since we work - // on a uniformly refined mesh, no - // constraints exist or have been computed - // (i.e. there was no need to call - // DoFTools::make_hanging_node_constraints - // as in other programs), but we need a - // constraints object in one place further - // down below anyway. + // The rest of the function is spent on + // setting vector sizes to the correct + // value. The final line closes the hanging + // node constraints object. Since we work + // on a uniformly refined mesh, no + // constraints exist or have been computed + // (i.e. there was no need to call + // DoFTools::make_hanging_node_constraints + // as in other programs), but we need a + // constraints object in one place further + // down below anyway. solution_u.reinit (dof_handler.n_dofs()); solution_v.reinit (dof_handler.n_dofs()); old_solution_u.reinit (dof_handler.n_dofs()); @@ -471,26 +471,26 @@ namespace Step23 } - // @sect4{WaveEquation::solve_u and WaveEquation::solve_v} - - // The next two functions deal with solving - // the linear systems associated with the - // equations for $U^n$ and $V^n$. Both are - // not particularly interesting as they - // pretty much follow the scheme used in all - // the previous tutorial programs. - // - // One can make little experiments with - // preconditioners for the two matrices we - // have to invert. As it turns out, however, - // for the matrices at hand here, using - // Jacobi or SSOR preconditioners reduces the - // number of iterations necessary to solve - // the linear system slightly, but due to the - // cost of applying the preconditioner it is - // no win in terms of run-time. It is not - // much of a loss either, but let's keep it - // simple and just do without: + // @sect4{WaveEquation::solve_u and WaveEquation::solve_v} + + // The next two functions deal with solving + // the linear systems associated with the + // equations for $U^n$ and $V^n$. Both are + // not particularly interesting as they + // pretty much follow the scheme used in all + // the previous tutorial programs. + // + // One can make little experiments with + // preconditioners for the two matrices we + // have to invert. As it turns out, however, + // for the matrices at hand here, using + // Jacobi or SSOR preconditioners reduces the + // number of iterations necessary to solve + // the linear system slightly, but due to the + // cost of applying the preconditioner it is + // no win in terms of run-time. It is not + // much of a loss either, but let's keep it + // simple and just do without: template void WaveEquation::solve_u () { @@ -522,16 +522,16 @@ namespace Step23 - // @sect4{WaveEquation::output_results} + // @sect4{WaveEquation::output_results} - // Likewise, the following function is pretty - // much what we've done before. The only - // thing worth mentioning is how here we - // generate a string representation of the - // time step number padded with leading zeros - // to 3 character length using the - // Utilities::int_to_string function's second - // argument. + // Likewise, the following function is pretty + // much what we've done before. The only + // thing worth mentioning is how here we + // generate a string representation of the + // time step number padded with leading zeros + // to 3 character length using the + // Utilities::int_to_string function's second + // argument. template void WaveEquation::output_results () const { @@ -553,22 +553,22 @@ namespace Step23 - // @sect4{WaveEquation::run} + // @sect4{WaveEquation::run} - // The following is really the only - // interesting function of the program. It - // contains the loop over all time steps, but - // before we get to that we have to set up - // the grid, DoFHandler, and matrices. In - // addition, we have to somehow get started - // with initial values. To this end, we use - // the VectorTools::project function that - // takes an object that describes a - // continuous function and computes the $L^2$ - // projection of this function onto the - // finite element space described by the - // DoFHandler object. Can't be any simpler - // than that: + // The following is really the only + // interesting function of the program. It + // contains the loop over all time steps, but + // before we get to that we have to set up + // the grid, DoFHandler, and matrices. In + // addition, we have to somehow get started + // with initial values. To this end, we use + // the VectorTools::project function that + // takes an object that describes a + // continuous function and computes the $L^2$ + // projection of this function onto the + // finite element space described by the + // DoFHandler object. Can't be any simpler + // than that: template void WaveEquation::run () { @@ -581,45 +581,45 @@ namespace Step23 InitialValuesV(), old_solution_v); - // The next thing is to loop over all the - // time steps until we reach the end time - // ($T=5$ in this case). In each time step, - // we first have to solve for $U^n$, using - // the equation $(M^n + k^2\theta^2 A^n)U^n - // =$ $(M^{n,n-1} - k^2\theta(1-\theta) - // A^{n,n-1})U^{n-1} + kM^{n,n-1}V^{n-1} +$ - // $k\theta \left[k \theta F^n + k(1-\theta) - // F^{n-1} \right]$. Note that we use the - // same mesh for all time steps, so that - // $M^n=M^{n,n-1}=M$ and - // $A^n=A^{n,n-1}=A$. What we therefore - // have to do first is to add up $MU^{n-1} - // - k^2\theta(1-\theta) AU^{n-1} + kMV^{n-1}$ and - // the forcing terms, and put the result - // into the system_rhs - // vector. (For these additions, we need a - // temporary vector that we declare before - // the loop to avoid repeated memory - // allocations in each time step.) - // - // The one thing to realize here is how we - // communicate the time variable to the - // object describing the right hand side: - // each object derived from the Function - // class has a time field that can be set - // using the Function::set_time and read by - // Function::get_time. In essence, using - // this mechanism, all functions of space - // and time are therefore considered - // functions of space evaluated at a - // particular time. This matches well what - // we typically need in finite element - // programs, where we almost always work on - // a single time step at a time, and where - // it never happens that, for example, one - // would like to evaluate a space-time - // function for all times at any given - // spatial location. + // The next thing is to loop over all the + // time steps until we reach the end time + // ($T=5$ in this case). In each time step, + // we first have to solve for $U^n$, using + // the equation $(M^n + k^2\theta^2 A^n)U^n + // =$ $(M^{n,n-1} - k^2\theta(1-\theta) + // A^{n,n-1})U^{n-1} + kM^{n,n-1}V^{n-1} +$ + // $k\theta \left[k \theta F^n + k(1-\theta) + // F^{n-1} \right]$. Note that we use the + // same mesh for all time steps, so that + // $M^n=M^{n,n-1}=M$ and + // $A^n=A^{n,n-1}=A$. What we therefore + // have to do first is to add up $MU^{n-1} + // - k^2\theta(1-\theta) AU^{n-1} + kMV^{n-1}$ and + // the forcing terms, and put the result + // into the system_rhs + // vector. (For these additions, we need a + // temporary vector that we declare before + // the loop to avoid repeated memory + // allocations in each time step.) + // + // The one thing to realize here is how we + // communicate the time variable to the + // object describing the right hand side: + // each object derived from the Function + // class has a time field that can be set + // using the Function::set_time and read by + // Function::get_time. In essence, using + // this mechanism, all functions of space + // and time are therefore considered + // functions of space evaluated at a + // particular time. This matches well what + // we typically need in finite element + // programs, where we almost always work on + // a single time step at a time, and where + // it never happens that, for example, one + // would like to evaluate a space-time + // function for all times at any given + // spatial location. Vector tmp (solution_u.size()); Vector forcing_terms (solution_u.size()); @@ -654,18 +654,18 @@ namespace Step23 system_rhs.add (theta * time_step, forcing_terms); - // After so constructing the right hand - // side vector of the first equation, - // all we have to do is apply the - // correct boundary values. As for the - // right hand side, this is a - // space-time function evaluated at a - // particular time, which we - // interpolate at boundary nodes and - // then use the result to apply - // boundary values as we usually - // do. The result is then handed off to - // the solve_u() function: + // After so constructing the right hand + // side vector of the first equation, + // all we have to do is apply the + // correct boundary values. As for the + // right hand side, this is a + // space-time function evaluated at a + // particular time, which we + // interpolate at boundary nodes and + // then use the result to apply + // boundary values as we usually + // do. The result is then handed off to + // the solve_u() function: { BoundaryValuesU boundary_values_u_function; boundary_values_u_function.set_time (time); @@ -676,20 +676,20 @@ namespace Step23 boundary_values_u_function, boundary_values); - // The matrix for solve_u() is the same in - // every time steps, so one could think - // that it is enough to do this only once - // at the beginning of the - // simulation. However, since we need to - // apply boundary values to the linear - // system (which eliminate some matrix rows - // and columns and give contributions to - // the right hand side), we have to refill - // the matrix in every time steps before we - // actually apply boundary data. The actual - // content is very simple: it is the sum of - // the mass matrix and a weighted Laplace - // matrix: + // The matrix for solve_u() is the same in + // every time steps, so one could think + // that it is enough to do this only once + // at the beginning of the + // simulation. However, since we need to + // apply boundary values to the linear + // system (which eliminate some matrix rows + // and columns and give contributions to + // the right hand side), we have to refill + // the matrix in every time steps before we + // actually apply boundary data. The actual + // content is very simple: it is the sum of + // the mass matrix and a weighted Laplace + // matrix: matrix_u.copy_from (mass_matrix); matrix_u.add (theta * theta * time_step * time_step, laplace_matrix); MatrixTools::apply_boundary_values (boundary_values, @@ -700,19 +700,19 @@ namespace Step23 solve_u (); - // The second step, i.e. solving for - // $V^n$, works similarly, except that - // this time the matrix on the left is - // the mass matrix (which we copy again - // in order to be able to apply - // boundary conditions, and the right - // hand side is $MV^{n-1} - k\left[ - // \theta A U^n + (1-\theta) - // AU^{n-1}\right]$ plus forcing - // terms. %Boundary values are applied - // in the same way as before, except - // that now we have to use the - // BoundaryValuesV class: + // The second step, i.e. solving for + // $V^n$, works similarly, except that + // this time the matrix on the left is + // the mass matrix (which we copy again + // in order to be able to apply + // boundary conditions, and the right + // hand side is $MV^{n-1} - k\left[ + // \theta A U^n + (1-\theta) + // AU^{n-1}\right]$ plus forcing + // terms. %Boundary values are applied + // in the same way as before, except + // that now we have to use the + // BoundaryValuesV class: laplace_matrix.vmult (system_rhs, solution_u); system_rhs *= -theta * time_step; @@ -741,21 +741,21 @@ namespace Step23 } solve_v (); - // Finally, after both solution - // components have been computed, we - // output the result, compute the - // energy in the solution, and go on to - // the next time step after shifting - // the present solution into the - // vectors that hold the solution at - // the previous time step. Note the - // function - // SparseMatrix::matrix_norm_square - // that can compute - // $\left$ and - // $\left$ in one step, - // saving us the expense of a temporary - // vector and several lines of code: + // Finally, after both solution + // components have been computed, we + // output the result, compute the + // energy in the solution, and go on to + // the next time step after shifting + // the present solution into the + // vectors that hold the solution at + // the previous time step. Note the + // function + // SparseMatrix::matrix_norm_square + // that can compute + // $\left$ and + // $\left$ in one step, + // saving us the expense of a temporary + // vector and several lines of code: output_results (); std::cout << " Total energy: " @@ -770,12 +770,12 @@ namespace Step23 } - // @sect3{The main function} +// @sect3{The main function} - // What remains is the main function of the - // program. There is nothing here that hasn't - // been shown in several of the previous - // programs: +// What remains is the main function of the +// program. There is nothing here that hasn't +// been shown in several of the previous +// programs: int main () { try diff --git a/deal.II/examples/step-24/step-24.cc b/deal.II/examples/step-24/step-24.cc index 742205e70d..dc525e955c 100644 --- a/deal.II/examples/step-24/step-24.cc +++ b/deal.II/examples/step-24/step-24.cc @@ -9,10 +9,10 @@ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // The following have all been covered - // previously: +// The following have all been covered +// previously: #include #include #include @@ -45,142 +45,142 @@ #include #include - // This is the only new one: We will need a - // library function defined in a class - // GridTools that computes the minimal cell - // diameter. +// This is the only new one: We will need a +// library function defined in a class +// GridTools that computes the minimal cell +// diameter. #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step24 { using namespace dealii; - // @sect3{The "forward problem" class template} + // @sect3{The "forward problem" class template} - // The first part of the main class is - // exactly as in step-23 - // (except for the name): + // The first part of the main class is + // exactly as in step-23 + // (except for the name): template class TATForwardProblem { - public: - TATForwardProblem (); - void run (); - - private: - void setup_system (); - void solve_p (); - void solve_v (); - void output_results () const; - - Triangulation triangulation; - FE_Q fe; - DoFHandler dof_handler; - - ConstraintMatrix constraints; - - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - SparseMatrix mass_matrix; - SparseMatrix laplace_matrix; - - Vector solution_p, solution_v; - Vector old_solution_p, old_solution_v; - Vector system_rhs_p, system_rhs_v; - - double time, time_step; - unsigned int timestep_number; - const double theta; - - // Here's what's new: first, we need - // that boundary mass matrix $B$ that - // came out of the absorbing boundary - // condition. Likewise, since this time - // we consider a realistic medium, we - // must have a measure of the wave speed - // $c_0$ that will enter all the - // formulas with the Laplace matrix - // (which we still define as $(\nabla - // \phi_i,\nabla \phi_j)$): - SparseMatrix boundary_matrix; - const double wave_speed; - - // The last thing we have to take care of - // is that we wanted to evaluate the - // solution at a certain number of - // detector locations. We need an array - // to hold these locations, declared here - // and filled in the constructor: - std::vector > detector_locations; + public: + TATForwardProblem (); + void run (); + + private: + void setup_system (); + void solve_p (); + void solve_v (); + void output_results () const; + + Triangulation triangulation; + FE_Q fe; + DoFHandler dof_handler; + + ConstraintMatrix constraints; + + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + SparseMatrix mass_matrix; + SparseMatrix laplace_matrix; + + Vector solution_p, solution_v; + Vector old_solution_p, old_solution_v; + Vector system_rhs_p, system_rhs_v; + + double time, time_step; + unsigned int timestep_number; + const double theta; + + // Here's what's new: first, we need + // that boundary mass matrix $B$ that + // came out of the absorbing boundary + // condition. Likewise, since this time + // we consider a realistic medium, we + // must have a measure of the wave speed + // $c_0$ that will enter all the + // formulas with the Laplace matrix + // (which we still define as $(\nabla + // \phi_i,\nabla \phi_j)$): + SparseMatrix boundary_matrix; + const double wave_speed; + + // The last thing we have to take care of + // is that we wanted to evaluate the + // solution at a certain number of + // detector locations. We need an array + // to hold these locations, declared here + // and filled in the constructor: + std::vector > detector_locations; }; - // @sect3{Equation data} - - // As usual, we have to define our - // initial values, boundary - // conditions, and right hand side - // functions. Except things are a bit - // simpler this time: we are to - // consider a problem that is driven - // by initial conditions, so there is - // no right hand side function - // (though you could look up in - // step-23 to see how this can be - // done. Secondly, there are no - // boundary conditions: the entire - // boundary of the domain consists of - // absorbing boundary - // conditions. That only leaves - // initial conditions, and there - // things are simple too since for - // this particular application only - // nonzero initial conditions for the - // pressure are prescribed, not for - // the velocity (which is zero at the - // initial time). - // - // So this is all we need: a class that - // specifies initial conditions for the - // pressure. In the physical setting - // considered in this program, these are - // small absorbers, which we model as a - // series of little circles where we assume - // that the pressure surplus is one, whereas - // no absorption and therefore no pressure - // surplus is anywhere else. This is how we - // do things (note that if we wanted to - // expand this program to not only compile - // but also to run, we would have to - // initialize the sources with - // three-dimensional source locations): + // @sect3{Equation data} + + // As usual, we have to define our + // initial values, boundary + // conditions, and right hand side + // functions. Except things are a bit + // simpler this time: we are to + // consider a problem that is driven + // by initial conditions, so there is + // no right hand side function + // (though you could look up in + // step-23 to see how this can be + // done. Secondly, there are no + // boundary conditions: the entire + // boundary of the domain consists of + // absorbing boundary + // conditions. That only leaves + // initial conditions, and there + // things are simple too since for + // this particular application only + // nonzero initial conditions for the + // pressure are prescribed, not for + // the velocity (which is zero at the + // initial time). + // + // So this is all we need: a class that + // specifies initial conditions for the + // pressure. In the physical setting + // considered in this program, these are + // small absorbers, which we model as a + // series of little circles where we assume + // that the pressure surplus is one, whereas + // no absorption and therefore no pressure + // surplus is anywhere else. This is how we + // do things (note that if we wanted to + // expand this program to not only compile + // but also to run, we would have to + // initialize the sources with + // three-dimensional source locations): template class InitialValuesP : public Function { - public: - InitialValuesP () - : - Function() - {} + public: + InitialValuesP () + : + Function() + {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - private: - struct Source - { - Source (const Point &l, - const double r) - : - location (l), - radius (r) - {} - - const Point location; - const double radius; - }; + private: + struct Source + { + Source (const Point &l, + const double r) + : + location (l), + radius (r) + {} + + const Point location; + const double radius; + }; }; @@ -192,7 +192,8 @@ namespace Step24 Source (Point (-0.135, 0), 0.05), Source (Point (0.17, 0), 0.03), Source (Point (-0.25, 0), 0.02), - Source (Point (-0.05, -0.15), 0.015)}; + Source (Point (-0.05, -0.15), 0.015) + }; static const unsigned int n_sources = sizeof(sources)/sizeof(sources[0]); for (unsigned int i=0; iTATForwardProblem class} - - // Let's start again with the - // constructor. Setting the member variables - // is straightforward. We use the acoustic - // wave speed of mineral oil (in millimeters - // per microsecond, a common unit in - // experimental biomedical imaging) since - // this is where many of the experiments we - // want to compare the output with are made - // in. The Crank-Nicolson scheme is used - // again, i.e. theta is set to 0.5. The time - // step is later selected to satisfy $k = - // \frac hc$ + // @sect3{Implementation of the TATForwardProblem class} + + // Let's start again with the + // constructor. Setting the member variables + // is straightforward. We use the acoustic + // wave speed of mineral oil (in millimeters + // per microsecond, a common unit in + // experimental biomedical imaging) since + // this is where many of the experiments we + // want to compare the output with are made + // in. The Crank-Nicolson scheme is used + // again, i.e. theta is set to 0.5. The time + // step is later selected to satisfy $k = + // \frac hc$ template TATForwardProblem::TATForwardProblem () - : - fe (1), - dof_handler (triangulation), - theta (0.5), - wave_speed (1.437) + : + fe (1), + dof_handler (triangulation), + theta (0.5), + wave_speed (1.437) { - // The second task in the constructor is to - // initialize the array that holds the - // detector locations. The results of this - // program were compared with experiments - // in which the step size of the detector - // spacing is 2.25 degree, corresponding to - // 160 detector locations. The radius of - // the scanning circle is selected to be - // half way between the center and the - // boundary to avoid that the remaining - // reflections from the imperfect boundary - // condition spoils our numerical results. - // - // The locations of the detectors are then - // calculated in clockwise order. Note that - // the following of course only works if we - // are computing in 2d, a condition that we - // guard with an assertion. If we later - // wanted to run the same program in 3d, we - // would have to add code here for the - // initialization of detector locations in - // 3d. Due to the assertion, there is no - // way we can forget to do this. + // The second task in the constructor is to + // initialize the array that holds the + // detector locations. The results of this + // program were compared with experiments + // in which the step size of the detector + // spacing is 2.25 degree, corresponding to + // 160 detector locations. The radius of + // the scanning circle is selected to be + // half way between the center and the + // boundary to avoid that the remaining + // reflections from the imperfect boundary + // condition spoils our numerical results. + // + // The locations of the detectors are then + // calculated in clockwise order. Note that + // the following of course only works if we + // are computing in 2d, a condition that we + // guard with an assertion. If we later + // wanted to run the same program in 3d, we + // would have to add code here for the + // initialization of detector locations in + // 3d. Due to the assertion, there is no + // way we can forget to do this. Assert (dim == 2, ExcNotImplemented()); const double detector_step_angle = 2.25; @@ -263,69 +264,69 @@ namespace Step24 - // @sect4{TATForwardProblem::setup_system} - - // The following system is pretty much what - // we've already done in - // step-23, but with two important - // differences. First, we have to create a - // circular (or spherical) mesh around the - // origin, with a radius of 1. This nothing - // new: we've done so before in - // step-6, step-10, and - // step-11, where we also explain - // how to attach a boundary object to a - // triangulation to be used whenever the - // triangulation needs to know where new - // boundary points lie when a cell is - // refined. Following this, the mesh is - // refined a number of times. - // - // One thing we had to make sure is that the - // time step satisfies the CFL condition - // discussed in the introduction of - // step-23. Back in that program, - // we ensured this by hand by setting a - // timestep that matches the mesh width, but - // that was error prone because if we refined - // the mesh once more we would also have to - // make sure the time step is changed. Here, - // we do that automatically: we ask a library - // function for the minimal diameter of any - // cell. Then we set $k=\frac h{c_0}$. The - // only problem is: what exactly is $h$? The - // point is that there is really no good - // theory on this question for the wave - // equation. It is known that for uniformly - // refined meshes consisting of rectangles, - // $h$ is the minimal edge length. But for - // meshes on general quadrilaterals, the - // exact relationship appears to be unknown, - // i.e. it is unknown what properties of - // cells are relevant for the CFL - // condition. The problem is that the CFL - // condition follows from knowledge of the - // smallest eigenvalue of the Laplace matrix, - // and that can only be computed analytically - // for simply structured meshes. - // - // The upshot of all this is that we're not - // quite sure what exactly we should take for - // $h$. The function - // GridTools::minimal_cell_diameter computes - // the minimal diameter of all cells. If the - // cells were all squares or cubes, then the - // minimal edge length would be the minimal - // diameter divided by - // std::sqrt(dim). We simply - // generalize this, without theoretical - // justification, to the case of non-uniform - // meshes. - // - // The only other significant change is that - // we need to build the boundary mass - // matrix. We will comment on this further - // down below. + // @sect4{TATForwardProblem::setup_system} + + // The following system is pretty much what + // we've already done in + // step-23, but with two important + // differences. First, we have to create a + // circular (or spherical) mesh around the + // origin, with a radius of 1. This nothing + // new: we've done so before in + // step-6, step-10, and + // step-11, where we also explain + // how to attach a boundary object to a + // triangulation to be used whenever the + // triangulation needs to know where new + // boundary points lie when a cell is + // refined. Following this, the mesh is + // refined a number of times. + // + // One thing we had to make sure is that the + // time step satisfies the CFL condition + // discussed in the introduction of + // step-23. Back in that program, + // we ensured this by hand by setting a + // timestep that matches the mesh width, but + // that was error prone because if we refined + // the mesh once more we would also have to + // make sure the time step is changed. Here, + // we do that automatically: we ask a library + // function for the minimal diameter of any + // cell. Then we set $k=\frac h{c_0}$. The + // only problem is: what exactly is $h$? The + // point is that there is really no good + // theory on this question for the wave + // equation. It is known that for uniformly + // refined meshes consisting of rectangles, + // $h$ is the minimal edge length. But for + // meshes on general quadrilaterals, the + // exact relationship appears to be unknown, + // i.e. it is unknown what properties of + // cells are relevant for the CFL + // condition. The problem is that the CFL + // condition follows from knowledge of the + // smallest eigenvalue of the Laplace matrix, + // and that can only be computed analytically + // for simply structured meshes. + // + // The upshot of all this is that we're not + // quite sure what exactly we should take for + // $h$. The function + // GridTools::minimal_cell_diameter computes + // the minimal diameter of all cells. If the + // cells were all squares or cubes, then the + // minimal edge length would be the minimal + // diameter divided by + // std::sqrt(dim). We simply + // generalize this, without theoretical + // justification, to the case of non-uniform + // meshes. + // + // The only other significant change is that + // we need to build the boundary mass + // matrix. We will comment on this further + // down below. template void TATForwardProblem::setup_system () { @@ -365,74 +366,74 @@ namespace Step24 MatrixCreator::create_laplace_matrix (dof_handler, QGauss(3), laplace_matrix); - // The second difference, as mentioned, to - // step-23 is that we need - // to build the boundary mass matrix that - // grew out of the absorbing boundary - // conditions. - // - // A first observation would be that this - // matrix is much sparser than the regular - // mass matrix, since none of the shape - // functions with purely interior support - // contributes to this matrix. We could - // therefore optimize the storage pattern - // to this situation and build up a second - // sparsity pattern that only contains the - // nonzero entries that we need. There is a - // trade-off to make here: first, we would - // have to have a second sparsity pattern - // object, so that costs memory. Secondly, - // the matrix attached to this sparsity - // pattern is going to be smaller and - // therefore requires less memory; it would - // also be faster to perform matrix-vector - // multiplications with it. The final - // argument, however, is the one that tips - // the scale: we are not primarily - // interested in performing matrix-vector - // with the boundary matrix alone (though - // we need to do that for the right hand - // side vector once per time step), but - // mostly wish to add it up to the other - // matrices used in the first of the two - // equations since this is the one that is - // going to be multiplied with once per - // iteration of the CG method, - // i.e. significantly more often. It is now - // the case that the SparseMatrix::add - // class allows to add one matrix to - // another, but only if they use the same - // sparsity pattern (the reason being that - // we can't add nonzero entries to a matrix - // after the sparsity pattern has been - // created, so we simply require that the - // two matrices have the same sparsity - // pattern). - // - // So let's go with that: + // The second difference, as mentioned, to + // step-23 is that we need + // to build the boundary mass matrix that + // grew out of the absorbing boundary + // conditions. + // + // A first observation would be that this + // matrix is much sparser than the regular + // mass matrix, since none of the shape + // functions with purely interior support + // contributes to this matrix. We could + // therefore optimize the storage pattern + // to this situation and build up a second + // sparsity pattern that only contains the + // nonzero entries that we need. There is a + // trade-off to make here: first, we would + // have to have a second sparsity pattern + // object, so that costs memory. Secondly, + // the matrix attached to this sparsity + // pattern is going to be smaller and + // therefore requires less memory; it would + // also be faster to perform matrix-vector + // multiplications with it. The final + // argument, however, is the one that tips + // the scale: we are not primarily + // interested in performing matrix-vector + // with the boundary matrix alone (though + // we need to do that for the right hand + // side vector once per time step), but + // mostly wish to add it up to the other + // matrices used in the first of the two + // equations since this is the one that is + // going to be multiplied with once per + // iteration of the CG method, + // i.e. significantly more often. It is now + // the case that the SparseMatrix::add + // class allows to add one matrix to + // another, but only if they use the same + // sparsity pattern (the reason being that + // we can't add nonzero entries to a matrix + // after the sparsity pattern has been + // created, so we simply require that the + // two matrices have the same sparsity + // pattern). + // + // So let's go with that: boundary_matrix.reinit (sparsity_pattern); - // The second thing to do is to actually - // build the matrix. Here, we need to - // integrate over faces of cells, so first - // we need a quadrature object that works - // on dim-1 dimensional - // objects. Secondly, the FEFaceValues - // variant of FEValues that works on faces, - // as its name suggest. And finally, the - // other variables that are part of the - // assembly machinery. All of this we put - // between curly braces to limit the scope - // of these variables to where we actually - // need them. - // - // The actual act of assembling the matrix - // is then fairly straightforward: we loop - // over all cells, over all faces of each - // of these cells, and then do something - // only if that particular face is at the - // boundary of the domain. Like this: + // The second thing to do is to actually + // build the matrix. Here, we need to + // integrate over faces of cells, so first + // we need a quadrature object that works + // on dim-1 dimensional + // objects. Secondly, the FEFaceValues + // variant of FEValues that works on faces, + // as its name suggest. And finally, the + // other variables that are part of the + // assembly machinery. All of this we put + // between curly braces to limit the scope + // of these variables to where we actually + // need them. + // + // The actual act of assembling the matrix + // is then fairly straightforward: we loop + // over all cells, over all faces of each + // of these cells, and then do something + // only if that particular face is at the + // boundary of the domain. Like this: { const QGauss quadrature_formula(3); FEFaceValues fe_values (fe, quadrature_formula, @@ -448,8 +449,8 @@ namespace Step24 typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) for (unsigned int f=0; f::faces_per_cell; ++f) if (cell->at_boundary(f)) @@ -494,14 +495,14 @@ namespace Step24 } - // @sect4{TATForwardProblem::solve_p and TATForwardProblem::solve_v} + // @sect4{TATForwardProblem::solve_p and TATForwardProblem::solve_v} - // The following two functions, solving the - // linear systems for the pressure and the - // velocity variable, are taken pretty much - // verbatim (with the exception of the change - // of name from $u$ to $p$ of the primary - // variable) from step-23: + // The following two functions, solving the + // linear systems for the pressure and the + // velocity variable, are taken pretty much + // verbatim (with the exception of the change + // of name from $u$ to $p$ of the primary + // variable) from step-23: template void TATForwardProblem::solve_p () { @@ -533,10 +534,10 @@ namespace Step24 - // @sect4{TATForwardProblem::output_results} + // @sect4{TATForwardProblem::output_results} - // The same holds here: the function is from - // step-23. + // The same holds here: the function is from + // step-23. template void TATForwardProblem::output_results () const { @@ -557,27 +558,27 @@ namespace Step24 - // @sect4{TATForwardProblem::run} - - // This function that does most of the work - // is pretty much again like in step-23, - // though we make things a bit clearer by - // using the vectors G1 and G2 mentioned in - // the introduction. Compared to the overall - // memory consumption of the program, the - // introduction of a few temporary vectors - // isn't doing much harm. - // - // The only changes to this function are: - // First, that we do not have to project - // initial values for the velocity $v$, since - // we know that it is zero. And second that - // we evaluate the solution at the detector - // locations computed in the - // constructor. This is done using the - // VectorTools::point_value function. These - // values are then written to a file that we - // open at the beginning of the function. + // @sect4{TATForwardProblem::run} + + // This function that does most of the work + // is pretty much again like in step-23, + // though we make things a bit clearer by + // using the vectors G1 and G2 mentioned in + // the introduction. Compared to the overall + // memory consumption of the program, the + // introduction of a few temporary vectors + // isn't doing much harm. + // + // The only changes to this function are: + // First, that we do not have to project + // initial values for the velocity $v$, since + // we know that it is zero. And second that + // we evaluate the solution at the detector + // locations computed in the + // constructor. This is done using the + // VectorTools::point_value function. These + // values are then written to a file that we + // open at the beginning of the function. template void TATForwardProblem::run () { @@ -650,12 +651,12 @@ namespace Step24 - // @sect3{The main function} +// @sect3{The main function} - // What remains is the main function of the - // program. There is nothing here that hasn't - // been shown in several of the previous - // programs: +// What remains is the main function of the +// program. There is nothing here that hasn't +// been shown in several of the previous +// programs: int main () { try diff --git a/deal.II/examples/step-25/step-25.cc b/deal.II/examples/step-25/step-25.cc index eb57fcd4b4..17aa23a7d4 100644 --- a/deal.II/examples/step-25/step-25.cc +++ b/deal.II/examples/step-25/step-25.cc @@ -9,23 +9,23 @@ /* further information on this license. */ - // @sect3{Include files and global variables} - - // For an explanation of the include - // files, the reader should refer to - // the example programs step-1 - // through step-4. They are in the - // standard order, which is - // base -- - // lac -- - // grid -- - // dofs -- - // fe -- - // numerics (since each - // of these categories roughly builds - // upon previous ones), then a few - // C++ headers for file input/output - // and string streams. +// @sect3{Include files and global variables} + +// For an explanation of the include +// files, the reader should refer to +// the example programs step-1 +// through step-4. They are in the +// standard order, which is +// base -- +// lac -- +// grid -- +// dofs -- +// fe -- +// numerics (since each +// of these categories roughly builds +// upon previous ones), then a few +// C++ headers for file input/output +// and string streams. #include #include #include @@ -53,144 +53,144 @@ #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step25 { using namespace dealii; - // @sect3{The SineGordonProblem class template} - - // The entire algorithm for solving the - // problem is encapsulated in this class. As - // in previous example programs, the class is - // declared with a template parameter, which - // is the spatial dimension, so that we can - // solve the sine-Gordon equation in one, two - // or three spatial dimensions. For more on - // the dimension-independent - // class-encapsulation of the problem, the - // reader should consult step-3 and step-4. - // - // Compared to step-23 and step-24, there - // isn't anything newsworthy in the general - // structure of the program (though there is - // of course in the inner workings of the - // various functions!). The most notable - // difference is the presence of the two new - // functions compute_nl_term and - // compute_nl_matrix that - // compute the nonlinear contributions to the - // system matrix and right-hand side of the first - // equation, as discussed in the - // Introduction. In addition, we have to have - // a vector solution_update that - // contains the nonlinear update to the - // solution vector in each Newton step. - // - // As also mentioned in the introduction, we - // do not store the velocity variable in this - // program, but the mass matrix times the - // velocity. This is done in the - // M_x_velocity variable (the - // "x" is intended to stand for - // "times"). - // - // Finally, the - // output_timestep_skip - // variable stores the number of time - // steps to be taken each time before - // graphical output is to be - // generated. This is of importance - // when using fine meshes (and - // consequently small time steps) - // where we would run lots of time - // steps and create lots of output - // files of solutions that look - // almost the same in subsequent - // files. This only clogs up our - // visualization procedures and we - // should avoid creating more output - // than we are really interested - // in. Therefore, if this variable is - // set to a value $n$ bigger than one, - // output is generated only every - // $n$th time step. + // @sect3{The SineGordonProblem class template} + + // The entire algorithm for solving the + // problem is encapsulated in this class. As + // in previous example programs, the class is + // declared with a template parameter, which + // is the spatial dimension, so that we can + // solve the sine-Gordon equation in one, two + // or three spatial dimensions. For more on + // the dimension-independent + // class-encapsulation of the problem, the + // reader should consult step-3 and step-4. + // + // Compared to step-23 and step-24, there + // isn't anything newsworthy in the general + // structure of the program (though there is + // of course in the inner workings of the + // various functions!). The most notable + // difference is the presence of the two new + // functions compute_nl_term and + // compute_nl_matrix that + // compute the nonlinear contributions to the + // system matrix and right-hand side of the first + // equation, as discussed in the + // Introduction. In addition, we have to have + // a vector solution_update that + // contains the nonlinear update to the + // solution vector in each Newton step. + // + // As also mentioned in the introduction, we + // do not store the velocity variable in this + // program, but the mass matrix times the + // velocity. This is done in the + // M_x_velocity variable (the + // "x" is intended to stand for + // "times"). + // + // Finally, the + // output_timestep_skip + // variable stores the number of time + // steps to be taken each time before + // graphical output is to be + // generated. This is of importance + // when using fine meshes (and + // consequently small time steps) + // where we would run lots of time + // steps and create lots of output + // files of solutions that look + // almost the same in subsequent + // files. This only clogs up our + // visualization procedures and we + // should avoid creating more output + // than we are really interested + // in. Therefore, if this variable is + // set to a value $n$ bigger than one, + // output is generated only every + // $n$th time step. template class SineGordonProblem { - public: - SineGordonProblem (); - void run (); - - private: - void make_grid_and_dofs (); - void assemble_system (); - void compute_nl_term (const Vector &old_data, + public: + SineGordonProblem (); + void run (); + + private: + void make_grid_and_dofs (); + void assemble_system (); + void compute_nl_term (const Vector &old_data, + const Vector &new_data, + Vector &nl_term) const; + void compute_nl_matrix (const Vector &old_data, const Vector &new_data, - Vector &nl_term) const; - void compute_nl_matrix (const Vector &old_data, - const Vector &new_data, - SparseMatrix &nl_matrix) const; - unsigned int solve (); - void output_results (const unsigned int timestep_number) const; + SparseMatrix &nl_matrix) const; + unsigned int solve (); + void output_results (const unsigned int timestep_number) const; - Triangulation triangulation; - FE_Q fe; - DoFHandler dof_handler; + Triangulation triangulation; + FE_Q fe; + DoFHandler dof_handler; - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - SparseMatrix mass_matrix; - SparseMatrix laplace_matrix; + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + SparseMatrix mass_matrix; + SparseMatrix laplace_matrix; - const unsigned int n_global_refinements; + const unsigned int n_global_refinements; - double time; - const double final_time, time_step; - const double theta; + double time; + const double final_time, time_step; + const double theta; - Vector solution, solution_update, old_solution; - Vector M_x_velocity; - Vector system_rhs; + Vector solution, solution_update, old_solution; + Vector M_x_velocity; + Vector system_rhs; - const unsigned int output_timestep_skip; + const unsigned int output_timestep_skip; }; - // @sect3{Initial conditions} - - // In the following two classes, we first - // implement the exact solution for 1D, 2D, - // and 3D mentioned in the introduction to - // this program. This space-time solution may - // be of independent interest if one wanted - // to test the accuracy of the program by - // comparing the numerical against the - // analytic solution (note however that the - // program uses a finite domain, whereas - // these are analytic solutions for an - // unbounded domain). This may, for example, - // be done using the - // VectorTools::integrate_difference - // function. Note, again (as was already - // discussed in step-23), how we describe - // space-time functions as spatial functions - // that depend on a time variable that can be - // set and queried using the - // FunctionTime::set_time() and - // FunctionTime::get_time() member functions - // of the FunctionTime base class of the - // Function class. + // @sect3{Initial conditions} + + // In the following two classes, we first + // implement the exact solution for 1D, 2D, + // and 3D mentioned in the introduction to + // this program. This space-time solution may + // be of independent interest if one wanted + // to test the accuracy of the program by + // comparing the numerical against the + // analytic solution (note however that the + // program uses a finite domain, whereas + // these are analytic solutions for an + // unbounded domain). This may, for example, + // be done using the + // VectorTools::integrate_difference + // function. Note, again (as was already + // discussed in step-23), how we describe + // space-time functions as spatial functions + // that depend on a time variable that can be + // set and queried using the + // FunctionTime::set_time() and + // FunctionTime::get_time() member functions + // of the FunctionTime base class of the + // Function class. template class ExactSolution : public Function { - public: - ExactSolution (const unsigned int n_components = 1, - const double time = 0.) : Function(n_components, time) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + public: + ExactSolution (const unsigned int n_components = 1, + const double time = 0.) : Function(n_components, time) {} + virtual double value (const Point &p, + const unsigned int component = 0) const; }; template @@ -201,72 +201,72 @@ namespace Step25 switch (dim) { - case 1: - { - const double m = 0.5; - const double c1 = 0.; - const double c2 = 0.; - return -4.*std::atan (m / - std::sqrt(1.-m*m) * - std::sin(std::sqrt(1.-m*m)*t+c2) / - std::cosh(m*p[0]+c1)); - } - - case 2: - { - const double theta = numbers::PI/4.; - const double lambda = 1.; - const double a0 = 1.; - const double s = 1.; - const double arg = p[0] * std::cos(theta) + - std::sin(theta) * - (p[1] * std::cosh(lambda) + - t * std::sinh(lambda)); - return 4.*std::atan(a0*std::exp(s*arg)); - } - - case 3: - { - double theta = numbers::PI/4; - double phi = numbers::PI/4; - double tau = 1.; - double c0 = 1.; - double s = 1.; - double arg = p[0]*std::cos(theta) + - p[1]*std::sin(theta) * std::cos(phi) + - std::sin(theta) * std::sin(phi) * - (p[2]*std::cosh(tau)+t*std::sinh(tau)); - return 4.*std::atan(c0*std::exp(s*arg)); - } - - default: - Assert (false, ExcNotImplemented()); - return -1e8; + case 1: + { + const double m = 0.5; + const double c1 = 0.; + const double c2 = 0.; + return -4.*std::atan (m / + std::sqrt(1.-m*m) * + std::sin(std::sqrt(1.-m*m)*t+c2) / + std::cosh(m*p[0]+c1)); + } + + case 2: + { + const double theta = numbers::PI/4.; + const double lambda = 1.; + const double a0 = 1.; + const double s = 1.; + const double arg = p[0] * std::cos(theta) + + std::sin(theta) * + (p[1] * std::cosh(lambda) + + t * std::sinh(lambda)); + return 4.*std::atan(a0*std::exp(s*arg)); + } + + case 3: + { + double theta = numbers::PI/4; + double phi = numbers::PI/4; + double tau = 1.; + double c0 = 1.; + double s = 1.; + double arg = p[0]*std::cos(theta) + + p[1]*std::sin(theta) * std::cos(phi) + + std::sin(theta) * std::sin(phi) * + (p[2]*std::cosh(tau)+t*std::sinh(tau)); + return 4.*std::atan(c0*std::exp(s*arg)); + } + + default: + Assert (false, ExcNotImplemented()); + return -1e8; } } - // In the second part of this section, we - // provide the initial conditions. We are lazy - // (and cautious) and don't want to implement - // the same functions as above a second - // time. Rather, if we are queried for - // initial conditions, we create an object - // ExactSolution, set it to the - // correct time, and let it compute whatever - // values the exact solution has at that - // time: + // In the second part of this section, we + // provide the initial conditions. We are lazy + // (and cautious) and don't want to implement + // the same functions as above a second + // time. Rather, if we are queried for + // initial conditions, we create an object + // ExactSolution, set it to the + // correct time, and let it compute whatever + // values the exact solution has at that + // time: template class InitialValues : public Function { - public: - InitialValues (const unsigned int n_components = 1, - const double time = 0.) - : - Function(n_components, time) - {} - - virtual double value (const Point &p, - const unsigned int component = 0) const; + public: + InitialValues (const unsigned int n_components = 1, + const double time = 0.) + : + Function(n_components, time) + {} + + virtual double value (const Point &p, + const unsigned int component = 0) const; }; template @@ -278,78 +278,78 @@ namespace Step25 - // @sect3{Implementation of the SineGordonProblem class} - - // Let's move on to the implementation of the - // main class, as it implements the algorithm - // outlined in the introduction. - - // @sect4{SineGordonProblem::SineGordonProblem} - - // This is the constructor of the - // SineGordonProblem class. It - // specifies the desired polynomial degree of - // the finite elements, associates a - // DoFHandler to the - // triangulation object (just as - // in the example programs step-3 and - // step-4), initializes the current or - // initial time, the final time, the time - // step size, and the value of $\theta$ for - // the time stepping scheme. Since the - // solutions we compute here are - // time-periodic, the actual value of the - // start-time doesn't matter, and we choose - // it so that we start at an interesting - // time. - // - // Note that if we were to chose the explicit - // Euler time stepping scheme ($\theta = 0$), - // then we must pick a time step $k \le h$, - // otherwise the scheme is not stable and - // oscillations might arise in the - // solution. The Crank-Nicolson scheme - // ($\theta = \frac{1}{2}$) and the implicit - // Euler scheme ($\theta=1$) do not suffer - // from this deficiency, since they are - // unconditionally stable. However, even then - // the time step should be chosen to be on - // the order of $h$ in order to obtain a good - // solution. Since we know that our mesh - // results from the uniform subdivision of a - // rectangle, we can compute that time step - // easily; if we had a different domain, the - // technique in step-24 using - // GridTools::minimal_cell_diameter would - // work as well. + // @sect3{Implementation of the SineGordonProblem class} + + // Let's move on to the implementation of the + // main class, as it implements the algorithm + // outlined in the introduction. + + // @sect4{SineGordonProblem::SineGordonProblem} + + // This is the constructor of the + // SineGordonProblem class. It + // specifies the desired polynomial degree of + // the finite elements, associates a + // DoFHandler to the + // triangulation object (just as + // in the example programs step-3 and + // step-4), initializes the current or + // initial time, the final time, the time + // step size, and the value of $\theta$ for + // the time stepping scheme. Since the + // solutions we compute here are + // time-periodic, the actual value of the + // start-time doesn't matter, and we choose + // it so that we start at an interesting + // time. + // + // Note that if we were to chose the explicit + // Euler time stepping scheme ($\theta = 0$), + // then we must pick a time step $k \le h$, + // otherwise the scheme is not stable and + // oscillations might arise in the + // solution. The Crank-Nicolson scheme + // ($\theta = \frac{1}{2}$) and the implicit + // Euler scheme ($\theta=1$) do not suffer + // from this deficiency, since they are + // unconditionally stable. However, even then + // the time step should be chosen to be on + // the order of $h$ in order to obtain a good + // solution. Since we know that our mesh + // results from the uniform subdivision of a + // rectangle, we can compute that time step + // easily; if we had a different domain, the + // technique in step-24 using + // GridTools::minimal_cell_diameter would + // work as well. template SineGordonProblem::SineGordonProblem () - : - fe (1), - dof_handler (triangulation), - n_global_refinements (6), - time (-5.4414), - final_time (2.7207), - time_step (10*1./std::pow(2.,1.*n_global_refinements)), - theta (0.5), - output_timestep_skip (1) + : + fe (1), + dof_handler (triangulation), + n_global_refinements (6), + time (-5.4414), + final_time (2.7207), + time_step (10*1./std::pow(2.,1.*n_global_refinements)), + theta (0.5), + output_timestep_skip (1) {} - // @sect4{SineGordonProblem::make_grid_and_dofs} - - // This function creates a rectangular grid - // in dim dimensions and refines - // it several times. Also, all matrix and - // vector members of the - // SineGordonProblem class are - // initialized to their appropriate sizes - // once the degrees of freedom have been - // assembled. Like step-24, we use the - // MatrixCreator class to - // generate a mass matrix $M$ and a Laplace - // matrix $A$ and store them in the - // appropriate variables for the remainder of - // the program's life. + // @sect4{SineGordonProblem::make_grid_and_dofs} + + // This function creates a rectangular grid + // in dim dimensions and refines + // it several times. Also, all matrix and + // vector members of the + // SineGordonProblem class are + // initialized to their appropriate sizes + // once the degrees of freedom have been + // assembled. Like step-24, we use the + // MatrixCreator class to + // generate a mass matrix $M$ and a Laplace + // matrix $A$ and store them in the + // appropriate variables for the remainder of + // the program's life. template void SineGordonProblem::make_grid_and_dofs () { @@ -393,35 +393,35 @@ namespace Step25 system_rhs.reinit (dof_handler.n_dofs()); } - // @sect4{SineGordonProblem::assemble_system} - - // This functions assembles the system matrix - // and right-hand side vector for each - // iteration of Newton's method. The reader - // should refer to the Introduction for the - // explicit formulas for the system matrix - // and right-hand side. - // - // Note that during each time step, we have to - // add up the various contributions to the - // matrix and right hand sides. In contrast - // to step-23 and step-24, this requires - // assembling a few more terms, since they - // depend on the solution of the previous - // time step or previous nonlinear step. We - // use the functions - // compute_nl_matrix and - // compute_nl_term to do this, - // while the present function provides the - // top-level logic. + // @sect4{SineGordonProblem::assemble_system} + + // This functions assembles the system matrix + // and right-hand side vector for each + // iteration of Newton's method. The reader + // should refer to the Introduction for the + // explicit formulas for the system matrix + // and right-hand side. + // + // Note that during each time step, we have to + // add up the various contributions to the + // matrix and right hand sides. In contrast + // to step-23 and step-24, this requires + // assembling a few more terms, since they + // depend on the solution of the previous + // time step or previous nonlinear step. We + // use the functions + // compute_nl_matrix and + // compute_nl_term to do this, + // while the present function provides the + // top-level logic. template void SineGordonProblem::assemble_system () { - // First we assemble the Jacobian - // matrix $F'_h(U^{n,l})$, where - // $U^{n,l}$ is stored in the vector - // solution for - // convenience. + // First we assemble the Jacobian + // matrix $F'_h(U^{n,l})$, where + // $U^{n,l}$ is stored in the vector + // solution for + // convenience. system_matrix = 0; system_matrix.copy_from (mass_matrix); system_matrix.add (std::pow(time_step*theta,2), laplace_matrix); @@ -430,8 +430,8 @@ namespace Step25 compute_nl_matrix (old_solution, solution, tmp_matrix); system_matrix.add (-std::pow(time_step*theta,2), tmp_matrix); - // Then, we compute the right-hand - // side vector $-F_h(U^{n,l})$. + // Then, we compute the right-hand + // side vector $-F_h(U^{n,l})$. system_rhs = 0; tmp_matrix = 0; @@ -459,44 +459,44 @@ namespace Step25 system_rhs *= -1; } - // @sect4{SineGordonProblem::compute_nl_term} - - // This function computes the vector - // $S(\cdot,\cdot)$, which appears in the - // nonlinear term in the both equations of - // the split formulation. This function not - // only simplifies the repeated computation - // of this term, but it is also a fundamental - // part of the nonlinear iterative solver - // that we use when the time stepping is - // implicit (i.e. $\theta\ne 0$). Moreover, - // we must allow the function to receive as - // input an "old" and a "new" solution. These - // may not be the actual solutions of the - // problem stored in - // old_solution and - // solution, but are simply the - // two functions we linearize about. For the - // purposes of this function, let us call the - // first two arguments $w_{\mathrm{old}}$ and - // $w_{\mathrm{new}}$ in the documentation of - // this class below, respectively. - // - // As a side-note, it is perhaps worth - // investigating what order quadrature - // formula is best suited for this type of - // integration. Since $\sin(\cdot)$ is not a - // polynomial, there are probably no - // quadrature formulas that can integrate - // these terms exactly. It is usually - // sufficient to just make sure that the - // right hand side is integrated up to the - // same order of accuracy as the - // discretization scheme is, but it may be - // possible to improve on the constant in the - // asympotitic statement of convergence by - // choosing a more accurate quadrature - // formula. + // @sect4{SineGordonProblem::compute_nl_term} + + // This function computes the vector + // $S(\cdot,\cdot)$, which appears in the + // nonlinear term in the both equations of + // the split formulation. This function not + // only simplifies the repeated computation + // of this term, but it is also a fundamental + // part of the nonlinear iterative solver + // that we use when the time stepping is + // implicit (i.e. $\theta\ne 0$). Moreover, + // we must allow the function to receive as + // input an "old" and a "new" solution. These + // may not be the actual solutions of the + // problem stored in + // old_solution and + // solution, but are simply the + // two functions we linearize about. For the + // purposes of this function, let us call the + // first two arguments $w_{\mathrm{old}}$ and + // $w_{\mathrm{new}}$ in the documentation of + // this class below, respectively. + // + // As a side-note, it is perhaps worth + // investigating what order quadrature + // formula is best suited for this type of + // integration. Since $\sin(\cdot)$ is not a + // polynomial, there are probably no + // quadrature formulas that can integrate + // these terms exactly. It is usually + // sufficient to just make sure that the + // right hand side is integrated up to the + // same order of accuracy as the + // discretization scheme is, but it may be + // possible to improve on the constant in the + // asympotitic statement of convergence by + // choosing a more accurate quadrature + // formula. template void SineGordonProblem::compute_nl_term (const Vector &old_data, const Vector &new_data, @@ -517,29 +517,29 @@ namespace Step25 std::vector new_data_values (n_q_points); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { - // Once we re-initialize our - // FEValues instantiation - // to the current cell, we make use of - // the get_function_values - // routine to get the values of the - // "old" data (presumably at - // $t=t_{n-1}$) and the "new" data - // (presumably at $t=t_n$) at the nodes - // of the chosen quadrature formula. + // Once we re-initialize our + // FEValues instantiation + // to the current cell, we make use of + // the get_function_values + // routine to get the values of the + // "old" data (presumably at + // $t=t_{n-1}$) and the "new" data + // (presumably at $t=t_n$) at the nodes + // of the chosen quadrature formula. fe_values.reinit (cell); fe_values.get_function_values (old_data, old_data_values); fe_values.get_function_values (new_data, new_data_values); - // Now, we can evaluate $\int_K - // \sin\left[\theta w_{\mathrm{new}} + - // (1-\theta) w_{\mathrm{old}}\right] - // \,\varphi_j\,\mathrm{d}x$ using the - // desired quadrature formula. + // Now, we can evaluate $\int_K + // \sin\left[\theta w_{\mathrm{new}} + + // (1-\theta) w_{\mathrm{old}}\right] + // \,\varphi_j\,\mathrm{d}x$ using the + // desired quadrature formula. for (unsigned int q_point=0; q_pointget_dof_indices (local_dof_indices); for (unsigned int i=0; icompute_nl_term, we must - // allow this function to receive as input an - // "old" and a "new" solution, which we again - // call $w_{\mathrm{old}}$ and - // $w_{\mathrm{new}}$ below, respectively. + // @sect4{SineGordonProblem::compute_nl_matrix} + + // This is the second function dealing with the + // nonlinear scheme. It computes the matrix + // $N(\cdot,\cdot)$, whicih appears in the + // nonlinear term in the Jacobian of + // $F(\cdot)$. Just as + // compute_nl_term, we must + // allow this function to receive as input an + // "old" and a "new" solution, which we again + // call $w_{\mathrm{old}}$ and + // $w_{\mathrm{new}}$ below, respectively. template void SineGordonProblem::compute_nl_matrix (const Vector &old_data, const Vector &new_data, @@ -590,29 +590,29 @@ namespace Step25 std::vector new_data_values (n_q_points); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { - // Again, first we - // re-initialize our - // FEValues - // instantiation to the current - // cell. + // Again, first we + // re-initialize our + // FEValues + // instantiation to the current + // cell. fe_values.reinit (cell); fe_values.get_function_values (old_data, old_data_values); fe_values.get_function_values (new_data, new_data_values); - // Then, we evaluate $\int_K - // \cos\left[\theta - // w_{\mathrm{new}} + - // (1-\theta) - // w_{\mathrm{old}}\right]\, - // \varphi_i\, - // \varphi_j\,\mathrm{d}x$ - // using the desired quadrature - // formula. + // Then, we evaluate $\int_K + // \cos\left[\theta + // w_{\mathrm{new}} + + // (1-\theta) + // w_{\mathrm{old}}\right]\, + // \varphi_i\, + // \varphi_j\,\mathrm{d}x$ + // using the desired quadrature + // formula. for (unsigned int q_point=0; q_pointget_dof_indices (local_dof_indices); for (unsigned int i=0; isolution_update and used to update - // solution in the - // run function. - // - // Note that we re-set the solution update to - // zero before solving for it. This is not - // necessary: iterative solvers can start - // from any point and converge to the correct - // solution. If one has a good estimate about - // the solution of a linear system, it may be - // worthwhile to start from that vector, but - // as a general observation it is a fact that - // the starting point doesn't matter very - // much: it has to be a very, very good guess - // to reduce the number of iterations by more - // than a few. It turns out that for this problem, - // using the previous nonlinear update as a - // starting point actually hurts convergence and - // increases the number of iterations needed, - // so we simply set it to zero. - // - // The function returns the number of - // iterations it took to converge to a - // solution. This number will later be used - // to generate output on the screen showing - // how many iterations were needed in each - // nonlinear iteration. + // @sect4{SineGordonProblem::solve} + + // As discussed in the Introduction, this + // function uses the CG iterative solver on + // the linear system of equations resulting + // from the finite element spatial + // discretization of each iteration of + // Newton's method for the (nonlinear) first + // equation of the split formulation. The + // solution to the system is, in fact, + // $\delta U^{n,l}$ so it is stored in + // solution_update and used to update + // solution in the + // run function. + // + // Note that we re-set the solution update to + // zero before solving for it. This is not + // necessary: iterative solvers can start + // from any point and converge to the correct + // solution. If one has a good estimate about + // the solution of a linear system, it may be + // worthwhile to start from that vector, but + // as a general observation it is a fact that + // the starting point doesn't matter very + // much: it has to be a very, very good guess + // to reduce the number of iterations by more + // than a few. It turns out that for this problem, + // using the previous nonlinear update as a + // starting point actually hurts convergence and + // increases the number of iterations needed, + // so we simply set it to zero. + // + // The function returns the number of + // iterations it took to converge to a + // solution. This number will later be used + // to generate output on the screen showing + // how many iterations were needed in each + // nonlinear iteration. template unsigned int SineGordonProblem::solve () @@ -695,12 +695,12 @@ namespace Step25 return solver_control.last_step(); } - // @sect4{SineGordonProblem::output_results} + // @sect4{SineGordonProblem::output_results} - // This function outputs the results to a - // file. It is pretty much identical to the - // respective functions in step-23 and - // step-24: + // This function outputs the results to a + // file. It is pretty much identical to the + // respective functions in step-23 and + // step-24: template void SineGordonProblem::output_results (const unsigned int timestep_number) const @@ -719,43 +719,43 @@ namespace Step25 data_out.write_vtk (output); } - // @sect4{SineGordonProblem::run} + // @sect4{SineGordonProblem::run} - // This function has the top-level - // control over everything: it runs - // the (outer) time-stepping loop, - // the (inner) nonlinear-solver loop, - // and outputs the solution after each - // time step. + // This function has the top-level + // control over everything: it runs + // the (outer) time-stepping loop, + // the (inner) nonlinear-solver loop, + // and outputs the solution after each + // time step. template void SineGordonProblem::run () { make_grid_and_dofs (); - // To aknowledge the initial - // condition, we must use the - // function $u_0(x)$ to compute - // $U^0$. To this end, below we - // will create an object of type - // InitialValues; note - // that when we create this object - // (which is derived from the - // Function class), we - // set its internal time variable - // to $t_0$, to indicate that the - // initial condition is a function - // of space and time evaluated at - // $t=t_0$. - // - // Then we produce $U^0$ by projecting - // $u_0(x)$ onto the grid using - // VectorTools::project. We - // have to use the same construct using - // hanging node constraints as in step-21: - // the VectorTools::project function - // requires a hanging node constraints - // object, but to be used we first need to - // close it: + // To aknowledge the initial + // condition, we must use the + // function $u_0(x)$ to compute + // $U^0$. To this end, below we + // will create an object of type + // InitialValues; note + // that when we create this object + // (which is derived from the + // Function class), we + // set its internal time variable + // to $t_0$, to indicate that the + // initial condition is a function + // of space and time evaluated at + // $t=t_0$. + // + // Then we produce $U^0$ by projecting + // $u_0(x)$ onto the grid using + // VectorTools::project. We + // have to use the same construct using + // hanging node constraints as in step-21: + // the VectorTools::project function + // requires a hanging node constraints + // object, but to be used we first need to + // close it: { ConstraintMatrix constraints; constraints.close(); @@ -766,20 +766,20 @@ namespace Step25 solution); } - // For completeness, we output the - // zeroth time step to a file just - // like any other other time step. + // For completeness, we output the + // zeroth time step to a file just + // like any other other time step. output_results (0); - // Now we perform the time - // stepping: at every time step we - // solve the matrix equation(s) - // corresponding to the finite - // element discretization of the - // problem, and then advance our - // solution according to the time - // stepping formulas we discussed - // in the Introduction. + // Now we perform the time + // stepping: at every time step we + // solve the matrix equation(s) + // corresponding to the finite + // element discretization of the + // problem, and then advance our + // solution according to the time + // stepping formulas we discussed + // in the Introduction. unsigned int timestep_number = 1; for (time+=time_step; time<=final_time; time+=time_step, ++timestep_number) { @@ -790,28 +790,28 @@ namespace Step25 << "advancing to t = " << time << "." << std::endl; - // At the beginning of each - // time step we must solve the - // nonlinear equation in the - // split formulation via - // Newton's method --- - // i.e. solve for $\delta - // U^{n,l}$ then compute - // $U^{n,l+1}$ and so on. The - // stopping criterion for this - // nonlinear iteration is that - // $\|F_h(U^{n,l})\|_2 \le - // 10^{-6} - // \|F_h(U^{n,0})\|_2$. Consequently, - // we need to record the norm - // of the residual in the first - // iteration. - // - // At the end of each iteration, we - // output to the console how many - // linear solver iterations it took - // us. When the loop below is done, we - // have (an approximation of) $U^n$. + // At the beginning of each + // time step we must solve the + // nonlinear equation in the + // split formulation via + // Newton's method --- + // i.e. solve for $\delta + // U^{n,l}$ then compute + // $U^{n,l+1}$ and so on. The + // stopping criterion for this + // nonlinear iteration is that + // $\|F_h(U^{n,l})\|_2 \le + // 10^{-6} + // \|F_h(U^{n,0})\|_2$. Consequently, + // we need to record the norm + // of the residual in the first + // iteration. + // + // At the end of each iteration, we + // output to the console how many + // linear solver iterations it took + // us. When the loop below is done, we + // have (an approximation of) $U^n$. double initial_rhs_norm = 0.; bool first_iteration = true; do @@ -837,15 +837,15 @@ namespace Step25 std::cout << " CG iterations per nonlinear step." << std::endl; - // Upon obtaining the solution to the - // first equation of the problem at - // $t=t_n$, we must update the - // auxiliary velocity variable - // $V^n$. However, we do not compute - // and store $V^n$ since it is not a - // quantity we use directly in the - // problem. Hence, for simplicity, we - // update $MV^n$ directly: + // Upon obtaining the solution to the + // first equation of the problem at + // $t=t_n$, we must update the + // auxiliary velocity variable + // $V^n$. However, we do not compute + // and store $V^n$ since it is not a + // quantity we use directly in the + // problem. Hence, for simplicity, we + // update $MV^n$ directly: Vector tmp_vector (solution.size()); laplace_matrix.vmult (tmp_vector, solution); M_x_velocity.add (-time_step*theta, tmp_vector); @@ -858,45 +858,45 @@ namespace Step25 compute_nl_term (old_solution, solution, tmp_vector); M_x_velocity.add (-time_step, tmp_vector); - // Oftentimes, in particular - // for fine meshes, we must - // pick the time step to be - // quite small in order for the - // scheme to be - // stable. Therefore, there are - // a lot of time steps during - // which "nothing interesting - // happens" in the solution. To - // improve overall efficiency - // -- in particular, speed up - // the program and save disk - // space -- we only output the - // solution every - // output_timestep_skip - // time steps: + // Oftentimes, in particular + // for fine meshes, we must + // pick the time step to be + // quite small in order for the + // scheme to be + // stable. Therefore, there are + // a lot of time steps during + // which "nothing interesting + // happens" in the solution. To + // improve overall efficiency + // -- in particular, speed up + // the program and save disk + // space -- we only output the + // solution every + // output_timestep_skip + // time steps: if (timestep_number % output_timestep_skip == 0) output_results (timestep_number); } } } - // @sect3{The main function} - - // This is the main function of the - // program. It creates an object of - // top-level class and calls its - // principal function. Also, we - // suppress some of the library output - // by setting - // deallog.depth_console - // to zero. Furthermore, if - // exceptions are thrown during the - // execution of the run method of the - // SineGordonProblem - // class, we catch and report them - // here. For more information about - // exceptions the reader should - // consult step-6. +// @sect3{The main function} + +// This is the main function of the +// program. It creates an object of +// top-level class and calls its +// principal function. Also, we +// suppress some of the library output +// by setting +// deallog.depth_console +// to zero. Furthermore, if +// exceptions are thrown during the +// execution of the run method of the +// SineGordonProblem +// class, we catch and report them +// here. For more information about +// exceptions the reader should +// consult step-6. int main () { try diff --git a/deal.II/examples/step-26/step-26.cc b/deal.II/examples/step-26/step-26.cc index 29b1620855..4076c6d242 100644 --- a/deal.II/examples/step-26/step-26.cc +++ b/deal.II/examples/step-26/step-26.cc @@ -9,13 +9,13 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // The first few (many?) include - // files have already been used in - // the previous example, so we will - // not explain their meaning here - // again. +// The first few (many?) include +// files have already been used in +// the previous example, so we will +// not explain their meaning here +// again. #include #include #include @@ -40,114 +40,114 @@ #include #include - // This is new, however: in the previous - // example we got some unwanted output from - // the linear solvers. If we want to suppress - // it, we have to include this file and add a - // single line somewhere to the program (see - // the main() function below for that): +// This is new, however: in the previous +// example we got some unwanted output from +// the linear solvers. If we want to suppress +// it, we have to include this file and add a +// single line somewhere to the program (see +// the main() function below for that): #include #include #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step26 { using namespace dealii; class PointCloudSurface : public StraightBoundary<3> { - public: - /** - * Constructor. - */ - PointCloudSurface (const std::string &filename); - - /** - * Let the new point be the - * arithmetic mean of the two - * vertices of the line. - * - * Refer to the general - * documentation of this class - * and the documentation of the - * base class for more - * information. - */ - virtual Point<3> - get_new_point_on_line (const Triangulation<3>::line_iterator &line) const; - - /** - * Let the new point be the - * arithmetic mean of the four - * vertices of this quad and the - * four midpoints of the lines, - * which are already created at - * the time of calling this - * function. - * - * Refer to the general - * documentation of this class - * and the documentation of the - * base class for more - * information. - */ - virtual Point<3> - get_new_point_on_quad (const Triangulation<3>::quad_iterator &quad) const; - - /** - * Gives n=points.size() - * points that splits the - * StraightBoundary line into - * $n+1$ partitions of equal - * lengths. - * - * Refer to the general - * documentation of this class - * and the documentation of the - * base class. - */ - virtual void - get_intermediate_points_on_line (const Triangulation<3>::line_iterator &line, - std::vector > &points) const; - - /** - * Gives n=points.size()=m*m - * points that splits the - * p{StraightBoundary} quad into - * (m+1)(m+1) subquads of equal - * size. - * - * Refer to the general - * documentation of this class - * and the documentation of the - * base class. - */ - virtual void - get_intermediate_points_on_quad (const Triangulation<3>::quad_iterator &quad, - std::vector > &points) const; - - /** - * A function that, given a point - * p, returns the closest - * point on the surface defined by the - * input file. For the time being, we - * simply return the closest point in the - * point cloud, rather than doing any - * sort of interpolation. - */ - Point<3> closest_point (const Point<3> &p) const; - private: - std::vector > point_list; + public: + /** + * Constructor. + */ + PointCloudSurface (const std::string &filename); + + /** + * Let the new point be the + * arithmetic mean of the two + * vertices of the line. + * + * Refer to the general + * documentation of this class + * and the documentation of the + * base class for more + * information. + */ + virtual Point<3> + get_new_point_on_line (const Triangulation<3>::line_iterator &line) const; + + /** + * Let the new point be the + * arithmetic mean of the four + * vertices of this quad and the + * four midpoints of the lines, + * which are already created at + * the time of calling this + * function. + * + * Refer to the general + * documentation of this class + * and the documentation of the + * base class for more + * information. + */ + virtual Point<3> + get_new_point_on_quad (const Triangulation<3>::quad_iterator &quad) const; + + /** + * Gives n=points.size() + * points that splits the + * StraightBoundary line into + * $n+1$ partitions of equal + * lengths. + * + * Refer to the general + * documentation of this class + * and the documentation of the + * base class. + */ + virtual void + get_intermediate_points_on_line (const Triangulation<3>::line_iterator &line, + std::vector > &points) const; + + /** + * Gives n=points.size()=m*m + * points that splits the + * p{StraightBoundary} quad into + * (m+1)(m+1) subquads of equal + * size. + * + * Refer to the general + * documentation of this class + * and the documentation of the + * base class. + */ + virtual void + get_intermediate_points_on_quad (const Triangulation<3>::quad_iterator &quad, + std::vector > &points) const; + + /** + * A function that, given a point + * p, returns the closest + * point on the surface defined by the + * input file. For the time being, we + * simply return the closest point in the + * point cloud, rather than doing any + * sort of interpolation. + */ + Point<3> closest_point (const Point<3> &p) const; + private: + std::vector > point_list; }; PointCloudSurface::PointCloudSurface (const std::string &filename) { - // first read in all the points + // first read in all the points { std::ifstream in (filename.c_str()); AssertThrow (in, ExcIO()); @@ -162,12 +162,12 @@ namespace Step26 AssertThrow (point_list.size() > 1, ExcIO()); } - // next fit a linear model through the data - // cloud to rectify it in a local - // coordinate system - // - // the first step is to move the center of - // mass of the points to the origin + // next fit a linear model through the data + // cloud to rectify it in a local + // coordinate system + // + // the first step is to move the center of + // mass of the points to the origin { const Point<3> c_o_m = std::accumulate (point_list.begin(), point_list.end(), @@ -177,17 +177,17 @@ namespace Step26 point_list[i] -= c_o_m; } - // next do a least squares fit to the - // function ax+by. this leads to the - // following equations: - - // min f(a,b) = sum_i (zi-a xi - b yi)^2 / 2 - // - // f_a = sum_i (zi - a xi - b yi) xi = 0 - // f_b = sum_i (zi - a xi - b yi) yi = 0 - // - // f_a = (sum_i zi xi) - (sum xi^2) a - (sum xi yi) b = 0 - // f_a = (sum_i zi yi) - (sum xi yi) a - (sum yi^2) b = 0 + // next do a least squares fit to the + // function ax+by. this leads to the + // following equations: + + // min f(a,b) = sum_i (zi-a xi - b yi)^2 / 2 + // + // f_a = sum_i (zi - a xi - b yi) xi = 0 + // f_b = sum_i (zi - a xi - b yi) yi = 0 + // + // f_a = (sum_i zi xi) - (sum xi^2) a - (sum xi yi) b = 0 + // f_a = (sum_i zi yi) - (sum xi yi) a - (sum yi^2) b = 0 { double A[2][2] = {{0,0},{0,0}}; double B[2] = {0,0}; @@ -207,10 +207,10 @@ namespace Step26 const double b = (A[0][0] * B[1] - A[0][1] * B[0]) / det; - // with this information, we can rotate - // the points so that the corresponding - // least-squares fit would be the x-y - // plane + // with this information, we can rotate + // the points so that the corresponding + // least-squares fit would be the x-y + // plane const Point<2> gradient_direction = Point<2>(a,b) / std::sqrt(a*a+b*b); const Point<2> orthogonal_direction @@ -220,27 +220,27 @@ namespace Step26 for (unsigned int i=0; i xy (point_list[i][0], point_list[i][1]); const double grad_distance = xy * gradient_direction; const double orth_distance = xy * orthogonal_direction; - // we then have to stretch the points - // in the gradient direction. the - // stretch factor is defined above - // (zero if the original plane was - // already the xy plane, infinity if - // it was vertical) + // we then have to stretch the points + // in the gradient direction. the + // stretch factor is defined above + // (zero if the original plane was + // already the xy plane, infinity if + // it was vertical) const Point<2> new_xy = (grad_distance * stretch_factor * gradient_direction + orth_distance * orthogonal_direction); @@ -325,51 +325,51 @@ namespace Step26 - // @sect3{The LaplaceProblem class template} + // @sect3{The LaplaceProblem class template} - // This is again the same - // LaplaceProblem class as in the - // previous example. The only - // difference is that we have now - // declared it as a class with a - // template parameter, and the - // template parameter is of course - // the spatial dimension in which we - // would like to solve the Laplace - // equation. Of course, several of - // the member variables depend on - // this dimension as well, in - // particular the Triangulation - // class, which has to represent - // quadrilaterals or hexahedra, - // respectively. Apart from this, - // everything is as before. + // This is again the same + // LaplaceProblem class as in the + // previous example. The only + // difference is that we have now + // declared it as a class with a + // template parameter, and the + // template parameter is of course + // the spatial dimension in which we + // would like to solve the Laplace + // equation. Of course, several of + // the member variables depend on + // this dimension as well, in + // particular the Triangulation + // class, which has to represent + // quadrilaterals or hexahedra, + // respectively. Apart from this, + // everything is as before. template class LaplaceProblem { - public: - LaplaceProblem (); - void run (); + public: + LaplaceProblem (); + void run (); - private: - void make_grid_and_dofs (); - void assemble_system (); - void solve (); - void output_results () const; + private: + void make_grid_and_dofs (); + void assemble_system (); + void solve (); + void output_results () const; - Triangulation triangulation; - FE_Q fe; - DoFHandler dof_handler; + Triangulation triangulation; + FE_Q fe; + DoFHandler dof_handler; - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; - Vector solution; - Vector system_rhs; + Vector solution; + Vector system_rhs; }; - // @sect3{Right hand side and boundary values} + // @sect3{Right hand side and boundary values} @@ -377,11 +377,11 @@ namespace Step26 template class BoundaryValues : public Function { - public: - BoundaryValues () : Function() {} + public: + BoundaryValues () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -395,95 +395,95 @@ namespace Step26 - // @sect3{Implementation of the LaplaceProblem class} - - // Next for the implementation of the class - // template that makes use of the functions - // above. As before, we will write everything - // as templates that have a formal parameter - // dim that we assume unknown at the time - // we define the template functions. Only - // later, the compiler will find a - // declaration of LaplaceProblem@<2@> (in - // the main function, actually) and - // compile the entire class with dim - // replaced by 2, a process referred to as - // `instantiation of a template'. When doing - // so, it will also replace instances of - // RightHandSide@ by - // RightHandSide@<2@> and instantiate the - // latter class from the class template. - // - // In fact, the compiler will also find a - // declaration LaplaceProblem@<3@> in - // main(). This will cause it to again go - // back to the general - // LaplaceProblem@ template, replace - // all occurrences of dim, this time by - // 3, and compile the class a second - // time. Note that the two instantiations - // LaplaceProblem@<2@> and - // LaplaceProblem@<3@> are completely - // independent classes; their only common - // feature is that they are both instantiated - // from the same general template, but they - // are not convertible into each other, for - // example, and share no code (both - // instantiations are compiled completely - // independently). - - - // @sect4{LaplaceProblem::LaplaceProblem} - - // After this introduction, here is the - // constructor of the LaplaceProblem - // class. It specifies the desired polynomial - // degree of the finite elements and - // associates the DoFHandler to the - // triangulation just as in the previous - // example program, step-3: + // @sect3{Implementation of the LaplaceProblem class} + + // Next for the implementation of the class + // template that makes use of the functions + // above. As before, we will write everything + // as templates that have a formal parameter + // dim that we assume unknown at the time + // we define the template functions. Only + // later, the compiler will find a + // declaration of LaplaceProblem@<2@> (in + // the main function, actually) and + // compile the entire class with dim + // replaced by 2, a process referred to as + // `instantiation of a template'. When doing + // so, it will also replace instances of + // RightHandSide@ by + // RightHandSide@<2@> and instantiate the + // latter class from the class template. + // + // In fact, the compiler will also find a + // declaration LaplaceProblem@<3@> in + // main(). This will cause it to again go + // back to the general + // LaplaceProblem@ template, replace + // all occurrences of dim, this time by + // 3, and compile the class a second + // time. Note that the two instantiations + // LaplaceProblem@<2@> and + // LaplaceProblem@<3@> are completely + // independent classes; their only common + // feature is that they are both instantiated + // from the same general template, but they + // are not convertible into each other, for + // example, and share no code (both + // instantiations are compiled completely + // independently). + + + // @sect4{LaplaceProblem::LaplaceProblem} + + // After this introduction, here is the + // constructor of the LaplaceProblem + // class. It specifies the desired polynomial + // degree of the finite elements and + // associates the DoFHandler to the + // triangulation just as in the previous + // example program, step-3: template LaplaceProblem::LaplaceProblem () : - fe (1), - dof_handler (triangulation) + fe (1), + dof_handler (triangulation) {} - // @sect4{LaplaceProblem::make_grid_and_dofs} - - // Grid creation is something - // inherently dimension - // dependent. However, as long as the - // domains are sufficiently similar - // in 2D or 3D, the library can - // abstract for you. In our case, we - // would like to again solve on the - // square [-1,1]x[-1,1] in 2D, or on - // the cube [-1,1]x[-1,1]x[-1,1] in - // 3D; both can be termed - // hyper_cube, so we may use the - // same function in whatever - // dimension we are. Of course, the - // functions that create a hypercube - // in two and three dimensions are - // very much different, but that is - // something you need not care - // about. Let the library handle the - // difficult things. - // - // Likewise, associating a degree of freedom - // with each vertex is something which - // certainly looks different in 2D and 3D, - // but that does not need to bother you - // either. This function therefore looks - // exactly like in the previous example, - // although it performs actions that in their - // details are quite different if dim - // happens to be 3. The only significant - // difference from a user's perspective is - // the number of cells resulting, which is - // much higher in three than in two space - // dimensions! + // @sect4{LaplaceProblem::make_grid_and_dofs} + + // Grid creation is something + // inherently dimension + // dependent. However, as long as the + // domains are sufficiently similar + // in 2D or 3D, the library can + // abstract for you. In our case, we + // would like to again solve on the + // square [-1,1]x[-1,1] in 2D, or on + // the cube [-1,1]x[-1,1]x[-1,1] in + // 3D; both can be termed + // hyper_cube, so we may use the + // same function in whatever + // dimension we are. Of course, the + // functions that create a hypercube + // in two and three dimensions are + // very much different, but that is + // something you need not care + // about. Let the library handle the + // difficult things. + // + // Likewise, associating a degree of freedom + // with each vertex is something which + // certainly looks different in 2D and 3D, + // but that does not need to bother you + // either. This function therefore looks + // exactly like in the previous example, + // although it performs actions that in their + // details are quite different if dim + // happens to be 3. The only significant + // difference from a user's perspective is + // the number of cells resulting, which is + // much higher in three than in two space + // dimensions! template void LaplaceProblem::make_grid_and_dofs () { @@ -510,7 +510,7 @@ namespace Step26 for (unsigned int i=0; i<4; ++i) { for (typename Triangulation::active_cell_iterator - cell = triangulation.begin_active(); + cell = triangulation.begin_active(); cell != triangulation.end(); ++cell) for (unsigned int f=0; f::faces_per_cell; ++f) if (cell->face(f)->boundary_indicator() == 1) @@ -548,38 +548,38 @@ namespace Step26 } - // @sect4{LaplaceProblem::assemble_system} - - // Unlike in the previous example, we - // would now like to use a - // non-constant right hand side - // function and non-zero boundary - // values. Both are tasks that are - // readily achieved with a only a few - // new lines of code in the - // assemblage of the matrix and right - // hand side. - // - // More interesting, though, is the - // way we assemble matrix and right - // hand side vector dimension - // independently: there is simply no - // difference to the - // two-dimensional case. Since the - // important objects used in this - // function (quadrature formula, - // FEValues) depend on the dimension - // by way of a template parameter as - // well, they can take care of - // setting up properly everything for - // the dimension for which this - // function is compiled. By declaring - // all classes which might depend on - // the dimension using a template - // parameter, the library can make - // nearly all work for you and you - // don't have to care about most - // things. + // @sect4{LaplaceProblem::assemble_system} + + // Unlike in the previous example, we + // would now like to use a + // non-constant right hand side + // function and non-zero boundary + // values. Both are tasks that are + // readily achieved with a only a few + // new lines of code in the + // assemblage of the matrix and right + // hand side. + // + // More interesting, though, is the + // way we assemble matrix and right + // hand side vector dimension + // independently: there is simply no + // difference to the + // two-dimensional case. Since the + // important objects used in this + // function (quadrature formula, + // FEValues) depend on the dimension + // by way of a template parameter as + // well, they can take care of + // setting up properly everything for + // the dimension for which this + // function is compiled. By declaring + // all classes which might depend on + // the dimension using a template + // parameter, the library can make + // nearly all work for you and you + // don't have to care about most + // things. template void LaplaceProblem::assemble_system () { @@ -600,19 +600,19 @@ namespace Step26 } - // @sect4{LaplaceProblem::solve} + // @sect4{LaplaceProblem::solve} - // Solving the linear system of - // equations is something that looks - // almost identical in most - // programs. In particular, it is - // dimension independent, so this - // function is copied verbatim from the - // previous example. + // Solving the linear system of + // equations is something that looks + // almost identical in most + // programs. In particular, it is + // dimension independent, so this + // function is copied verbatim from the + // previous example. template void LaplaceProblem::solve () { - // NEW + // NEW SolverControl solver_control (dof_handler.n_dofs(), 1e-12*system_rhs.l2_norm()); SolverCG<> cg (solver_control); @@ -625,34 +625,34 @@ namespace Step26 } - // @sect4{LaplaceProblem::output_results} - - // This function also does what the - // respective one did in step-3. No changes - // here for dimension independence either. - // - // The only difference to the previous - // example is that we want to write output in - // GMV format, rather than for gnuplot (GMV - // is another graphics program that, contrary - // to gnuplot, shows data in nice colors, - // allows rotation of geometries with the - // mouse, and generates reasonable - // representations of 3d data; for ways to - // obtain it see the ReadMe file of - // deal.II). To write data in this format, we - // simply replace the - // data_out.write_gnuplot call by - // data_out.write_gmv. - // - // Since the program will run both 2d and 3d - // versions of the laplace solver, we use the - // dimension in the filename to generate - // distinct filenames for each run (in a - // better program, one would check whether - // `dim' can have other values than 2 or 3, - // but we neglect this here for the sake of - // brevity). + // @sect4{LaplaceProblem::output_results} + + // This function also does what the + // respective one did in step-3. No changes + // here for dimension independence either. + // + // The only difference to the previous + // example is that we want to write output in + // GMV format, rather than for gnuplot (GMV + // is another graphics program that, contrary + // to gnuplot, shows data in nice colors, + // allows rotation of geometries with the + // mouse, and generates reasonable + // representations of 3d data; for ways to + // obtain it see the ReadMe file of + // deal.II). To write data in this format, we + // simply replace the + // data_out.write_gnuplot call by + // data_out.write_gmv. + // + // Since the program will run both 2d and 3d + // versions of the laplace solver, we use the + // dimension in the filename to generate + // distinct filenames for each run (in a + // better program, one would check whether + // `dim' can have other values than 2 or 3, + // but we neglect this here for the sake of + // brevity). template void LaplaceProblem::output_results () const { @@ -671,13 +671,13 @@ namespace Step26 - // @sect4{LaplaceProblem::run} + // @sect4{LaplaceProblem::run} - // This is the function which has the - // top-level control over - // everything. Apart from one line of - // additional output, it is the same - // as for the previous example. + // This is the function which has the + // top-level control over + // everything. Apart from one line of + // additional output, it is the same + // as for the previous example. template void LaplaceProblem::run () { @@ -691,80 +691,80 @@ namespace Step26 } - // @sect3{The main function} - - // And this is the main function. It also - // looks mostly like in step-3, but if you - // look at the code below, note how we first - // create a variable of type - // LaplaceProblem@<2@> (forcing the - // compiler to compile the class template - // with dim replaced by 2) and run a - // 2d simulation, and then we do the whole - // thing over in 3d. - // - // In practice, this is probably not what you - // would do very frequently (you probably - // either want to solve a 2d problem, or one - // in 3d, but not both at the same - // time). However, it demonstrates the - // mechanism by which we can simply change - // which dimension we want in a single place, - // and thereby force the compiler to - // recompile the dimension independent class - // templates for the dimension we - // request. The emphasis here lies on the - // fact that we only need to change a single - // place. This makes it rather trivial to - // debug the program in 2d where computations - // are fast, and then switch a single place - // to a 3 to run the much more computing - // intensive program in 3d for `real' - // computations. - // - // Each of the two blocks is enclosed in - // braces to make sure that the - // laplace_problem_2d variable goes out - // of scope (and releases the memory it - // holds) before we move on to allocate - // memory for the 3d case. Without the - // additional braces, the - // laplace_problem_2d variable would only - // be destroyed at the end of the function, - // i.e. after running the 3d problem, and - // would needlessly hog memory while the 3d - // run could actually use it. - // - // Finally, the first line of the function is - // used to suppress some output. Remember - // that in the previous example, we had the - // output from the linear solvers about the - // starting residual and the number of the - // iteration where convergence was - // detected. This can be suppressed through - // the deallog.depth_console(0) call. - // - // The rationale here is the following: the - // deallog (i.e. deal-log, not de-allog) - // variable represents a stream to which some - // parts of the library write output. It - // redirects this output to the console and - // if required to a file. The output is - // nested in a way so that each function can - // use a prefix string (separated by colons) - // for each line of output; if it calls - // another function, that may also use its - // prefix which is then printed after the one - // of the calling function. Since output from - // functions which are nested deep below is - // usually not as important as top-level - // output, you can give the deallog variable - // a maximal depth of nested output for - // output to console and file. The depth zero - // which we gave here means that no output is - // written. By changing it you can get more - // information about the innards of the - // library. +// @sect3{The main function} + +// And this is the main function. It also +// looks mostly like in step-3, but if you +// look at the code below, note how we first +// create a variable of type +// LaplaceProblem@<2@> (forcing the +// compiler to compile the class template +// with dim replaced by 2) and run a +// 2d simulation, and then we do the whole +// thing over in 3d. +// +// In practice, this is probably not what you +// would do very frequently (you probably +// either want to solve a 2d problem, or one +// in 3d, but not both at the same +// time). However, it demonstrates the +// mechanism by which we can simply change +// which dimension we want in a single place, +// and thereby force the compiler to +// recompile the dimension independent class +// templates for the dimension we +// request. The emphasis here lies on the +// fact that we only need to change a single +// place. This makes it rather trivial to +// debug the program in 2d where computations +// are fast, and then switch a single place +// to a 3 to run the much more computing +// intensive program in 3d for `real' +// computations. +// +// Each of the two blocks is enclosed in +// braces to make sure that the +// laplace_problem_2d variable goes out +// of scope (and releases the memory it +// holds) before we move on to allocate +// memory for the 3d case. Without the +// additional braces, the +// laplace_problem_2d variable would only +// be destroyed at the end of the function, +// i.e. after running the 3d problem, and +// would needlessly hog memory while the 3d +// run could actually use it. +// +// Finally, the first line of the function is +// used to suppress some output. Remember +// that in the previous example, we had the +// output from the linear solvers about the +// starting residual and the number of the +// iteration where convergence was +// detected. This can be suppressed through +// the deallog.depth_console(0) call. +// +// The rationale here is the following: the +// deallog (i.e. deal-log, not de-allog) +// variable represents a stream to which some +// parts of the library write output. It +// redirects this output to the console and +// if required to a file. The output is +// nested in a way so that each function can +// use a prefix string (separated by colons) +// for each line of output; if it calls +// another function, that may also use its +// prefix which is then printed after the one +// of the calling function. Since output from +// functions which are nested deep below is +// usually not as important as top-level +// output, you can give the deallog variable +// a maximal depth of nested output for +// output to console and file. The depth zero +// which we gave here means that no output is +// written. By changing it you can get more +// information about the innards of the +// library. int main () { try diff --git a/deal.II/examples/step-27/step-27.cc b/deal.II/examples/step-27/step-27.cc index e5c5d798bd..585ea26c9f 100644 --- a/deal.II/examples/step-27/step-27.cc +++ b/deal.II/examples/step-27/step-27.cc @@ -9,12 +9,12 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // The first few files have already - // been covered in previous examples - // and will thus not be further - // commented on. +// The first few files have already +// been covered in previous examples +// and will thus not be further +// commented on. #include #include #include @@ -38,113 +38,113 @@ #include #include - // These are the new files we need. The first - // one provides an alternative to the usual - // SparsityPattern class and the - // CompressedSparsityPattern class already - // discussed in step-11 and step-18. The last - // two provide hp versions of the - // DoFHandler and FEValues classes as - // described in the introduction of this - // program. +// These are the new files we need. The first +// one provides an alternative to the usual +// SparsityPattern class and the +// CompressedSparsityPattern class already +// discussed in step-11 and step-18. The last +// two provide hp versions of the +// DoFHandler and FEValues classes as +// described in the introduction of this +// program. #include #include #include - // The last set of include files are standard - // C++ headers. We need support for complex - // numbers when we compute the Fourier - // transform. +// The last set of include files are standard +// C++ headers. We need support for complex +// numbers when we compute the Fourier +// transform. #include #include #include - // Finally, this is as in previous - // programs: +// Finally, this is as in previous +// programs: namespace Step27 { using namespace dealii; - // @sect3{The main class} - - // The main class of this program looks very - // much like the one already used in the - // first few tutorial programs, for example - // the one in step-6. The main difference is - // that we have merged the refine_grid and - // output_results functions into one since we - // will also want to output some of the - // quantities used in deciding how to refine - // the mesh (in particular the estimated - // smoothness of the solution). There is also - // a function that computes this estimated - // smoothness, as discussed in the - // introduction. - // - // As far as member variables are concerned, - // we use the same structure as already used - // in step-6, but instead of a regular - // DoFHandler we use an object of type - // hp::DoFHandler, and we need collections - // instead of individual finite element, - // quadrature, and face quadrature - // objects. We will fill these collections in - // the constructor of the class. The last - // variable, max_degree, - // indicates the maximal polynomial degree of - // shape functions used. + // @sect3{The main class} + + // The main class of this program looks very + // much like the one already used in the + // first few tutorial programs, for example + // the one in step-6. The main difference is + // that we have merged the refine_grid and + // output_results functions into one since we + // will also want to output some of the + // quantities used in deciding how to refine + // the mesh (in particular the estimated + // smoothness of the solution). There is also + // a function that computes this estimated + // smoothness, as discussed in the + // introduction. + // + // As far as member variables are concerned, + // we use the same structure as already used + // in step-6, but instead of a regular + // DoFHandler we use an object of type + // hp::DoFHandler, and we need collections + // instead of individual finite element, + // quadrature, and face quadrature + // objects. We will fill these collections in + // the constructor of the class. The last + // variable, max_degree, + // indicates the maximal polynomial degree of + // shape functions used. template class LaplaceProblem { - public: - LaplaceProblem (); - ~LaplaceProblem (); + public: + LaplaceProblem (); + ~LaplaceProblem (); - void run (); + void run (); - private: - void setup_system (); - void assemble_system (); - void solve (); - void create_coarse_grid (); - void estimate_smoothness (Vector &smoothness_indicators) const; - void postprocess (const unsigned int cycle); + private: + void setup_system (); + void assemble_system (); + void solve (); + void create_coarse_grid (); + void estimate_smoothness (Vector &smoothness_indicators) const; + void postprocess (const unsigned int cycle); - Triangulation triangulation; + Triangulation triangulation; - hp::DoFHandler dof_handler; - hp::FECollection fe_collection; - hp::QCollection quadrature_collection; - hp::QCollection face_quadrature_collection; + hp::DoFHandler dof_handler; + hp::FECollection fe_collection; + hp::QCollection quadrature_collection; + hp::QCollection face_quadrature_collection; - ConstraintMatrix constraints; + ConstraintMatrix constraints; - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; - Vector solution; - Vector system_rhs; + Vector solution; + Vector system_rhs; - const unsigned int max_degree; + const unsigned int max_degree; }; - // @sect3{Equation data} - // - // Next, let us define the right hand side - // function for this problem. It is $x+1$ in - // 1d, $(x+1)(y+1)$ in 2d, and so on. + // @sect3{Equation data} + // + // Next, let us define the right hand side + // function for this problem. It is $x+1$ in + // 1d, $(x+1)(y+1)$ in 2d, and so on. template class RightHandSide : public Function { - public: - RightHandSide () : Function () {} + public: + RightHandSide () : Function () {} - virtual double value (const Point &p, - const unsigned int component) const; + virtual double value (const Point &p, + const unsigned int component) const; }; @@ -162,32 +162,32 @@ namespace Step27 - // @sect3{Implementation of the main class} + // @sect3{Implementation of the main class} - // @sect4{LaplaceProblem::LaplaceProblem} + // @sect4{LaplaceProblem::LaplaceProblem} - // The constructor of this class is fairly - // straightforward. It associates the - // hp::DoFHandler object with the - // triangulation, and then sets the maximal - // polynomial degree to 7 (in 1d and 2d) or 5 - // (in 3d and higher). We do so because using - // higher order polynomial degrees becomes - // prohibitively expensive, especially in - // higher space dimensions. - // - // Following this, we fill the collections of - // finite element, and cell and face - // quadrature objects. We start with - // quadratic elements, and each quadrature - // formula is chosen so that it is - // appropriate for the matching finite - // element in the hp::FECollection object. + // The constructor of this class is fairly + // straightforward. It associates the + // hp::DoFHandler object with the + // triangulation, and then sets the maximal + // polynomial degree to 7 (in 1d and 2d) or 5 + // (in 3d and higher). We do so because using + // higher order polynomial degrees becomes + // prohibitively expensive, especially in + // higher space dimensions. + // + // Following this, we fill the collections of + // finite element, and cell and face + // quadrature objects. We start with + // quadratic elements, and each quadrature + // formula is chosen so that it is + // appropriate for the matching finite + // element in the hp::FECollection object. template LaplaceProblem::LaplaceProblem () - : - dof_handler (triangulation), - max_degree (dim <= 2 ? 7 : 5) + : + dof_handler (triangulation), + max_degree (dim <= 2 ? 7 : 5) { for (unsigned int degree=2; degree<=max_degree; ++degree) { @@ -198,10 +198,10 @@ namespace Step27 } - // @sect4{LaplaceProblem::~LaplaceProblem} + // @sect4{LaplaceProblem::~LaplaceProblem} - // The destructor is unchanged from what we - // already did in step-6: + // The destructor is unchanged from what we + // already did in step-6: template LaplaceProblem::~LaplaceProblem () { @@ -209,44 +209,44 @@ namespace Step27 } - // @sect4{LaplaceProblem::setup_system} - // - // This function is again an almost - // verbatim copy of what we already did in - // step-6. The first change is that we - // append the Dirichlet boundary conditions - // to the ConstraintMatrix object, which we - // consequently call just - // constraints instead of - // hanging_node_constraints. The - // second difference is that we don't - // directly build the sparsity pattern, but - // first create an intermediate object that - // we later copy into the usual - // SparsityPattern data structure, since - // this is more efficient for the problem - // with many entries per row (and different - // number of entries in different rows). In - // another slight deviation, we do not - // first build the sparsity pattern and - // then condense away constrained degrees - // of freedom, but pass the constraint - // matrix object directly to the function - // that builds the sparsity pattern. We - // disable the insertion of constrained - // entries with false as fourth - // argument in the - // DoFTools::make_sparsity_pattern - // function. All of these changes are - // explained in the introduction of this - // program. - // - // The last change, maybe hidden in plain - // sight, is that the dof_handler variable - // here is an hp object -- nevertheless all - // the function calls we had before still - // work in exactly the same way as they - // always did. + // @sect4{LaplaceProblem::setup_system} + // + // This function is again an almost + // verbatim copy of what we already did in + // step-6. The first change is that we + // append the Dirichlet boundary conditions + // to the ConstraintMatrix object, which we + // consequently call just + // constraints instead of + // hanging_node_constraints. The + // second difference is that we don't + // directly build the sparsity pattern, but + // first create an intermediate object that + // we later copy into the usual + // SparsityPattern data structure, since + // this is more efficient for the problem + // with many entries per row (and different + // number of entries in different rows). In + // another slight deviation, we do not + // first build the sparsity pattern and + // then condense away constrained degrees + // of freedom, but pass the constraint + // matrix object directly to the function + // that builds the sparsity pattern. We + // disable the insertion of constrained + // entries with false as fourth + // argument in the + // DoFTools::make_sparsity_pattern + // function. All of these changes are + // explained in the introduction of this + // program. + // + // The last change, maybe hidden in plain + // sight, is that the dof_handler variable + // here is an hp object -- nevertheless all + // the function calls we had before still + // work in exactly the same way as they + // always did. template void LaplaceProblem::setup_system () { @@ -274,51 +274,51 @@ namespace Step27 - // @sect4{LaplaceProblem::assemble_system} - - // This is the function that assembles the - // global matrix and right hand side vector - // from the local contributions of each - // cell. Its main working is as has been - // described in many of the tutorial programs - // before. The significant deviations are the - // ones necessary for hp finite element - // methods. In particular, that we need to - // use a collection of FEValues object - // (implemented through the hp::FEValues - // class), and that we have to eliminate - // constrained degrees of freedom already - // when copying local contributions into - // global objects. Both of these are - // explained in detail in the introduction of - // this program. - // - // One other slight complication is the fact - // that because we use different polynomial - // degrees on different cells, the matrices - // and vectors holding local contributions do - // not have the same size on all cells. At - // the beginning of the loop over all cells, - // we therefore each time have to resize them - // to the correct size (given by - // dofs_per_cell). Because these - // classes are implement in such a way that - // reducing the size of a matrix or vector - // does not release the currently allocated - // memory (unless the new size is zero), the - // process of resizing at the beginning of - // the loop will only require re-allocation - // of memory during the first few - // iterations. Once we have found in a cell - // with the maximal finite element degree, no - // more re-allocations will happen because - // all subsequent reinit calls - // will only set the size to something that - // fits the currently allocated memory. This - // is important since allocating memory is - // expensive, and doing so every time we - // visit a new cell would take significant - // compute time. + // @sect4{LaplaceProblem::assemble_system} + + // This is the function that assembles the + // global matrix and right hand side vector + // from the local contributions of each + // cell. Its main working is as has been + // described in many of the tutorial programs + // before. The significant deviations are the + // ones necessary for hp finite element + // methods. In particular, that we need to + // use a collection of FEValues object + // (implemented through the hp::FEValues + // class), and that we have to eliminate + // constrained degrees of freedom already + // when copying local contributions into + // global objects. Both of these are + // explained in detail in the introduction of + // this program. + // + // One other slight complication is the fact + // that because we use different polynomial + // degrees on different cells, the matrices + // and vectors holding local contributions do + // not have the same size on all cells. At + // the beginning of the loop over all cells, + // we therefore each time have to resize them + // to the correct size (given by + // dofs_per_cell). Because these + // classes are implement in such a way that + // reducing the size of a matrix or vector + // does not release the currently allocated + // memory (unless the new size is zero), the + // process of resizing at the beginning of + // the loop will only require re-allocation + // of memory during the first few + // iterations. Once we have found in a cell + // with the maximal finite element degree, no + // more re-allocations will happen because + // all subsequent reinit calls + // will only set the size to something that + // fits the currently allocated memory. This + // is important since allocating memory is + // expensive, and doing so every time we + // visit a new cell would take significant + // compute time. template void LaplaceProblem::assemble_system () { @@ -335,8 +335,8 @@ namespace Step27 std::vector local_dof_indices; typename hp::DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { const unsigned int dofs_per_cell = cell->get_fe().dofs_per_cell; @@ -378,28 +378,28 @@ namespace Step27 system_matrix, system_rhs); } - // Now with the loop over all cells - // finished, we are done for this - // function. The steps we still had to do - // at this point in earlier tutorial - // programs, namely condensing hanging - // node constraints and applying - // Dirichlet boundary conditions, have - // been taken care of by the - // ConstraintMatrix object - // constraints on the fly. + // Now with the loop over all cells + // finished, we are done for this + // function. The steps we still had to do + // at this point in earlier tutorial + // programs, namely condensing hanging + // node constraints and applying + // Dirichlet boundary conditions, have + // been taken care of by the + // ConstraintMatrix object + // constraints on the fly. } - // @sect4{LaplaceProblem::solve} + // @sect4{LaplaceProblem::solve} - // The function solving the linear system is - // entirely unchanged from previous - // examples. We simply try to reduce the - // initial residual (which equals the $l_2$ - // norm of the right hand side) by a certain - // factor: + // The function solving the linear system is + // entirely unchanged from previous + // examples. We simply try to reduce the + // initial residual (which equals the $l_2$ + // norm of the right hand side) by a certain + // factor: template void LaplaceProblem::solve () { @@ -418,34 +418,34 @@ namespace Step27 - // @sect4{LaplaceProblem::postprocess} + // @sect4{LaplaceProblem::postprocess} - // After solving the linear system, we will - // want to postprocess the solution. Here, - // all we do is to estimate the error, - // estimate the local smoothness of the - // solution as described in the introduction, - // then write graphical output, and finally - // refine the mesh in both $h$ and $p$ - // according to the indicators computed - // before. We do all this in the same - // function because we want the estimated - // error and smoothness indicators not only - // for refinement, but also include them in - // the graphical output. + // After solving the linear system, we will + // want to postprocess the solution. Here, + // all we do is to estimate the error, + // estimate the local smoothness of the + // solution as described in the introduction, + // then write graphical output, and finally + // refine the mesh in both $h$ and $p$ + // according to the indicators computed + // before. We do all this in the same + // function because we want the estimated + // error and smoothness indicators not only + // for refinement, but also include them in + // the graphical output. template void LaplaceProblem::postprocess (const unsigned int cycle) { - // Let us start with computing estimated - // error and smoothness indicators, which - // each are one number for each active cell - // of our triangulation. For the error - // indicator, we use the - // KellyErrorEstimator class as - // always. Estimating the smoothness is - // done in the respective function of this - // class; that function is discussed - // further down below: + // Let us start with computing estimated + // error and smoothness indicators, which + // each are one number for each active cell + // of our triangulation. For the error + // indicator, we use the + // KellyErrorEstimator class as + // always. Estimating the smoothness is + // done in the respective function of this + // class; that function is discussed + // further down below: Vector estimated_error_per_cell (triangulation.n_active_cells()); KellyErrorEstimator::estimate (dof_handler, face_quadrature_collection, @@ -457,53 +457,53 @@ namespace Step27 Vector smoothness_indicators (triangulation.n_active_cells()); estimate_smoothness (smoothness_indicators); - // Next we want to generate graphical - // output. In addition to the two estimated - // quantities derived above, we would also - // like to output the polynomial degree of - // the finite elements used on each of the - // elements on the mesh. - // - // The way to do that requires that we loop - // over all cells and poll the active - // finite element index of them using - // cell-@>active_fe_index(). We - // then use the result of this operation - // and query the finite element collection - // for the finite element with that index, - // and finally determine the polynomial - // degree of that element. The result we - // put into a vector with one element per - // cell. The DataOut class requires this to - // be a vector of float or - // double, even though our - // values are all integers, so that it what - // we use: + // Next we want to generate graphical + // output. In addition to the two estimated + // quantities derived above, we would also + // like to output the polynomial degree of + // the finite elements used on each of the + // elements on the mesh. + // + // The way to do that requires that we loop + // over all cells and poll the active + // finite element index of them using + // cell-@>active_fe_index(). We + // then use the result of this operation + // and query the finite element collection + // for the finite element with that index, + // and finally determine the polynomial + // degree of that element. The result we + // put into a vector with one element per + // cell. The DataOut class requires this to + // be a vector of float or + // double, even though our + // values are all integers, so that it what + // we use: { Vector fe_degrees (triangulation.n_active_cells()); { typename hp::DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (unsigned int index=0; cell!=endc; ++cell, ++index) fe_degrees(index) = fe_collection[cell->active_fe_index()].degree; } - // With now all data vectors available -- - // solution, estimated errors and - // smoothness indicators, and finite - // element degrees --, we create a - // DataOut object for graphical output - // and attach all data. Note that the - // DataOut class has a second template - // argument (which defaults to - // DoFHandler@, which is why we - // have never seen it in previous - // tutorial programs) that indicates the - // type of DoF handler to be used. Here, - // we have to use the hp::DoFHandler - // class: + // With now all data vectors available -- + // solution, estimated errors and + // smoothness indicators, and finite + // element degrees --, we create a + // DataOut object for graphical output + // and attach all data. Note that the + // DataOut class has a second template + // argument (which defaults to + // DoFHandler@, which is why we + // have never seen it in previous + // tutorial programs) that indicates the + // type of DoF handler to be used. Here, + // we have to use the hp::DoFHandler + // class: DataOut > data_out; data_out.attach_dof_handler (dof_handler); @@ -513,11 +513,11 @@ namespace Step27 data_out.add_data_vector (fe_degrees, "fe_degree"); data_out.build_patches (); - // The final step in generating - // output is to determine a file - // name, open the file, and write - // the data into it (here, we use - // VTK format): + // The final step in generating + // output is to determine a file + // name, open the file, and write + // the data into it (here, we use + // VTK format): const std::string filename = "solution-" + Utilities::int_to_string (cycle, 2) + ".vtk"; @@ -525,52 +525,52 @@ namespace Step27 data_out.write_vtk (output); } - // After this, we would like to actually - // refine the mesh, in both $h$ and - // $p$. The way we are going to do this is - // as follows: first, we use the estimated - // error to flag those cells for refinement - // that have the largest error. This is - // what we have always done: + // After this, we would like to actually + // refine the mesh, in both $h$ and + // $p$. The way we are going to do this is + // as follows: first, we use the estimated + // error to flag those cells for refinement + // that have the largest error. This is + // what we have always done: { GridRefinement::refine_and_coarsen_fixed_number (triangulation, estimated_error_per_cell, 0.3, 0.03); - // Next we would like to figure out which - // of the cells that have been flagged - // for refinement should actually have - // $p$ increased instead of $h$ - // decreased. The strategy we choose here - // is that we look at the smoothness - // indicators of those cells that are - // flagged for refinement, and increase - // $p$ for those with a smoothness larger - // than a certain threshold. For this, we - // first have to determine the maximal - // and minimal values of the smoothness - // indicators of all flagged cells, which - // we do using a loop over all cells and - // comparing current minimal and maximal - // values. (We start with the minimal and - // maximal values of all cells, a - // range within which the minimal and - // maximal values on cells flagged for - // refinement must surely lie.) Absent - // any better strategies, we will then - // set the threshold above which will - // increase $p$ instead of reducing $h$ - // as the mean value between minimal and - // maximal smoothness indicators on cells - // flagged for refinement: + // Next we would like to figure out which + // of the cells that have been flagged + // for refinement should actually have + // $p$ increased instead of $h$ + // decreased. The strategy we choose here + // is that we look at the smoothness + // indicators of those cells that are + // flagged for refinement, and increase + // $p$ for those with a smoothness larger + // than a certain threshold. For this, we + // first have to determine the maximal + // and minimal values of the smoothness + // indicators of all flagged cells, which + // we do using a loop over all cells and + // comparing current minimal and maximal + // values. (We start with the minimal and + // maximal values of all cells, a + // range within which the minimal and + // maximal values on cells flagged for + // refinement must surely lie.) Absent + // any better strategies, we will then + // set the threshold above which will + // increase $p$ instead of reducing $h$ + // as the mean value between minimal and + // maximal smoothness indicators on cells + // flagged for refinement: float max_smoothness = *std::min_element (smoothness_indicators.begin(), smoothness_indicators.end()), - min_smoothness = *std::max_element (smoothness_indicators.begin(), - smoothness_indicators.end()); + min_smoothness = *std::max_element (smoothness_indicators.begin(), + smoothness_indicators.end()); { typename hp::DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (unsigned int index=0; cell!=endc; ++cell, ++index) if (cell->refine_flag_set()) { @@ -582,24 +582,24 @@ namespace Step27 } const float threshold_smoothness = (max_smoothness + min_smoothness) / 2; - // With this, we can go back, loop over - // all cells again, and for those cells - // for which (i) the refinement flag is - // set, (ii) the smoothness indicator is - // larger than the threshold, and (iii) - // we still have a finite element with a - // polynomial degree higher than the - // current one in the finite element - // collection, we then increase the - // polynomial degree and in return remove - // the flag indicating that the cell - // should undergo bisection. For all - // other cells, the refinement flags - // remain untouched: + // With this, we can go back, loop over + // all cells again, and for those cells + // for which (i) the refinement flag is + // set, (ii) the smoothness indicator is + // larger than the threshold, and (iii) + // we still have a finite element with a + // polynomial degree higher than the + // current one in the finite element + // collection, we then increase the + // polynomial degree and in return remove + // the flag indicating that the cell + // should undergo bisection. For all + // other cells, the refinement flags + // remain untouched: { typename hp::DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (unsigned int index=0; cell!=endc; ++cell, ++index) if (cell->refine_flag_set() && @@ -612,30 +612,30 @@ namespace Step27 } } - // At the end of this procedure, we then - // refine the mesh. During this process, - // children of cells undergoing bisection - // inherit their mother cell's finite - // element index: + // At the end of this procedure, we then + // refine the mesh. During this process, + // children of cells undergoing bisection + // inherit their mother cell's finite + // element index: triangulation.execute_coarsening_and_refinement (); } } - // @sect4{LaplaceProblem::create_coarse_grid} - - // The following function is used when - // creating the initial grid. It is a - // specialization for the 2d case, i.e. a - // corresponding function needs to be - // implemented if the program is run in - // anything other then 2d. The function is - // actually stolen from step-14 and generates - // the same mesh used already there, i.e. the - // square domain with the square hole in the - // middle. The meaning of the different parts - // of this function are explained in the - // documentation of step-14: + // @sect4{LaplaceProblem::create_coarse_grid} + + // The following function is used when + // creating the initial grid. It is a + // specialization for the 2d case, i.e. a + // corresponding function needs to be + // implemented if the program is run in + // anything other then 2d. The function is + // actually stolen from step-14 and generates + // the same mesh used already there, i.e. the + // square domain with the square hole in the + // middle. The meaning of the different parts + // of this function are explained in the + // documentation of step-14: template <> void LaplaceProblem<2>::create_coarse_grid () { @@ -669,26 +669,28 @@ namespace Step27 Point<2> (-1./2, 1.), Point<2> (0., 1.), Point<2> (+1./2, 1.), - Point<2> (+1, 1.) }; + Point<2> (+1, 1.) + }; const unsigned int - n_vertices = sizeof(vertices_1) / sizeof(vertices_1[0]); + n_vertices = sizeof(vertices_1) / sizeof(vertices_1[0]); const std::vector > vertices (&vertices_1[0], &vertices_1[n_vertices]); static const int cell_vertices[][GeometryInfo::vertices_per_cell] - = {{0, 1, 5, 6}, - {1, 2, 6, 7}, - {2, 3, 7, 8}, - {3, 4, 8, 9}, - {5, 6, 10, 11}, - {8, 9, 12, 13}, - {10, 11, 14, 15}, - {12, 13, 17, 18}, - {14, 15, 19, 20}, - {15, 16, 20, 21}, - {16, 17, 21, 22}, - {17, 18, 22, 23}}; + = {{0, 1, 5, 6}, + {1, 2, 6, 7}, + {2, 3, 7, 8}, + {3, 4, 8, 9}, + {5, 6, 10, 11}, + {8, 9, 12, 13}, + {10, 11, 14, 15}, + {12, 13, 17, 18}, + {14, 15, 19, 20}, + {15, 16, 20, 21}, + {16, 17, 21, 22}, + {17, 18, 22, 23} + }; const unsigned int - n_cells = sizeof(cell_vertices) / sizeof(cell_vertices[0]); + n_cells = sizeof(cell_vertices) / sizeof(cell_vertices[0]); std::vector > cells (n_cells, CellData()); for (unsigned int i=0; i void LaplaceProblem::run () { @@ -754,155 +756,155 @@ namespace Step27 } - // @sect4{LaplaceProblem::estimate_smoothness} + // @sect4{LaplaceProblem::estimate_smoothness} - // This last function of significance - // implements the algorithm to estimate the - // smoothness exponent using the algorithms - // explained in detail in the - // introduction. We will therefore only - // comment on those points that are of - // implementational importance. + // This last function of significance + // implements the algorithm to estimate the + // smoothness exponent using the algorithms + // explained in detail in the + // introduction. We will therefore only + // comment on those points that are of + // implementational importance. template void LaplaceProblem:: estimate_smoothness (Vector &smoothness_indicators) const { - // The first thing we need to do is - // to define the Fourier vectors - // ${\bf k}$ for which we want to - // compute Fourier coefficients of - // the solution on each cell. In - // 2d, we pick those vectors ${\bf - // k}=(\pi i, \pi j)^T$ for which - // $\sqrt{i^2+j^2}\le N$, with - // $i,j$ integers and $N$ being the - // maximal polynomial degree we use - // for the finite elements in this - // program. The 3d case is handled - // analogously. 1d and dimensions - // higher than 3 are not - // implemented, and we guard our - // implementation by making sure - // that we receive an exception in - // case someone tries to compile - // the program for any of these - // dimensions. - // - // We exclude ${\bf k}=0$ to avoid problems - // computing $|{\bf k}|^{-mu}$ and $\ln - // |{\bf k}|$. The other vectors are stored - // in the field k_vectors. In - // addition, we store the square of the - // magnitude of each of these vectors (up - // to a factor $\pi^2$) in the - // k_vectors_magnitude array - // -- we will need that when we attempt to - // find out which of those Fourier - // coefficients corresponding to Fourier - // vectors of the same magnitude is the - // largest: + // The first thing we need to do is + // to define the Fourier vectors + // ${\bf k}$ for which we want to + // compute Fourier coefficients of + // the solution on each cell. In + // 2d, we pick those vectors ${\bf + // k}=(\pi i, \pi j)^T$ for which + // $\sqrt{i^2+j^2}\le N$, with + // $i,j$ integers and $N$ being the + // maximal polynomial degree we use + // for the finite elements in this + // program. The 3d case is handled + // analogously. 1d and dimensions + // higher than 3 are not + // implemented, and we guard our + // implementation by making sure + // that we receive an exception in + // case someone tries to compile + // the program for any of these + // dimensions. + // + // We exclude ${\bf k}=0$ to avoid problems + // computing $|{\bf k}|^{-mu}$ and $\ln + // |{\bf k}|$. The other vectors are stored + // in the field k_vectors. In + // addition, we store the square of the + // magnitude of each of these vectors (up + // to a factor $\pi^2$) in the + // k_vectors_magnitude array + // -- we will need that when we attempt to + // find out which of those Fourier + // coefficients corresponding to Fourier + // vectors of the same magnitude is the + // largest: const unsigned int N = max_degree; std::vector > k_vectors; std::vector k_vectors_magnitude; switch (dim) { - case 2: - { - for (unsigned int i=0; i(numbers::PI * i, + numbers::PI * j)); + k_vectors_magnitude.push_back (i*i+j*j); + } + + break; + } + + case 3: + { + for (unsigned int i=0; i(numbers::PI * i, - numbers::PI * j)); - k_vectors_magnitude.push_back (i*i+j*j); + numbers::PI * j, + numbers::PI * k)); + k_vectors_magnitude.push_back (i*i+j*j+k*k); } - break; - } - - case 3: - { - for (unsigned int i=0; i(numbers::PI * i, - numbers::PI * j, - numbers::PI * k)); - k_vectors_magnitude.push_back (i*i+j*j+k*k); - } - - break; - } - - default: - Assert (false, ExcNotImplemented()); + break; + } + + default: + Assert (false, ExcNotImplemented()); } - // After we have set up the Fourier - // vectors, we also store their total - // number for simplicity, and compute the - // logarithm of the magnitude of each of - // these vectors since we will need it many - // times over further down below: + // After we have set up the Fourier + // vectors, we also store their total + // number for simplicity, and compute the + // logarithm of the magnitude of each of + // these vectors since we will need it many + // times over further down below: const unsigned n_fourier_modes = k_vectors.size(); std::vector ln_k (n_fourier_modes); for (unsigned int i=0; i > > - fourier_transform_matrices (fe_collection.size()); - - // In order to compute them, we of - // course can't perform the Fourier - // transform analytically, but have - // to approximate it using - // quadrature. To this end, we use - // a quadrature formula that is - // obtained by iterating a 2-point - // Gauss formula as many times as - // the maximal exponent we use for - // the term $e^{i{\bf k}\cdot{\bf - // x}}$: + fourier_transform_matrices (fe_collection.size()); + + // In order to compute them, we of + // course can't perform the Fourier + // transform analytically, but have + // to approximate it using + // quadrature. To this end, we use + // a quadrature formula that is + // obtained by iterating a 2-point + // Gauss formula as many times as + // the maximal exponent we use for + // the term $e^{i{\bf k}\cdot{\bf + // x}}$: QGauss<1> base_quadrature (2); QIterated quadrature (base_quadrature, N); - // With this, we then loop over all finite - // elements in use, reinitialize the - // respective matrix ${\cal F}$ to the - // right size, and integrate each entry of - // the matrix numerically as ${\cal - // F}_{{\bf k},j}=\sum_q e^{i{\bf k}\cdot - // {\bf x}}\varphi_j({\bf x}_q) - // w_q$, where $x_q$ - // are the quadrature points and $w_q$ are - // the quadrature weights. Note that the - // imaginary unit $i=\sqrt{-1}$ is obtained - // from the standard C++ classes using - // std::complex@(0,1). - - // Because we work on the unit cell, we can - // do all this work without a mapping from - // reference to real cell and consequently - // do not need the FEValues class. + // With this, we then loop over all finite + // elements in use, reinitialize the + // respective matrix ${\cal F}$ to the + // right size, and integrate each entry of + // the matrix numerically as ${\cal + // F}_{{\bf k},j}=\sum_q e^{i{\bf k}\cdot + // {\bf x}}\varphi_j({\bf x}_q) + // w_q$, where $x_q$ + // are the quadrature points and $w_q$ are + // the quadrature weights. Note that the + // imaginary unit $i=\sqrt{-1}$ is obtained + // from the standard C++ classes using + // std::complex@(0,1). + + // Because we work on the unit cell, we can + // do all this work without a mapping from + // reference to real cell and consequently + // do not need the FEValues class. for (unsigned int fe=0; fe > fourier_coefficients (n_fourier_modes); Vector local_dof_values; - // Then here is the loop: + // Then here is the loop: typename hp::DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (unsigned int index=0; cell!=endc; ++cell, ++index) { - // Inside the loop, we first need to - // get the values of the local degrees - // of freedom (which we put into the - // local_dof_values array - // after setting it to the right size) - // and then need to compute the Fourier - // transform by multiplying this vector - // with the matrix ${\cal F}$ - // corresponding to this finite - // element. We need to write out the - // multiplication by hand because the - // objects holding the data do not have - // vmult-like functions - // declared: + // Inside the loop, we first need to + // get the values of the local degrees + // of freedom (which we put into the + // local_dof_values array + // after setting it to the right size) + // and then need to compute the Fourier + // transform by multiplying this vector + // with the matrix ${\cal F}$ + // corresponding to this finite + // element. We need to write out the + // multiplication by hand because the + // objects holding the data do not have + // vmult-like functions + // declared: local_dof_values.reinit (cell->get_fe().dofs_per_cell); cell->get_dof_values (solution, local_dof_values); @@ -969,20 +971,20 @@ namespace Step27 local_dof_values(i); } - // The next thing, as explained in the - // introduction, is that we wanted to - // only fit our exponential decay of - // Fourier coefficients to the largest - // coefficients for each possible value - // of $|{\bf k}|$. To this end, we - // create a map that for each magnitude - // $|{\bf k}|$ stores the largest $|\hat - // U_{{\bf k}}|$ found so far, i.e. we - // overwrite the existing value (or add - // it to the map) if no value for the - // current $|{\bf k}|$ exists yet, or if - // the current value is larger than the - // previously stored one: + // The next thing, as explained in the + // introduction, is that we wanted to + // only fit our exponential decay of + // Fourier coefficients to the largest + // coefficients for each possible value + // of $|{\bf k}|$. To this end, we + // create a map that for each magnitude + // $|{\bf k}|$ stores the largest $|\hat + // U_{{\bf k}}|$ found so far, i.e. we + // overwrite the existing value (or add + // it to the map) if no value for the + // current $|{\bf k}|$ exists yet, or if + // the current value is larger than the + // previously stored one: std::map k_to_max_U_map; for (unsigned int f=0; ftry block and catch whatever - // exceptions are thrown, thereby producing - // meaningful output if anything should go - // wrong: +// The main function is again verbatim what +// we had before: wrap creating and running +// an object of the main class into a +// try block and catch whatever +// exceptions are thrown, thereby producing +// meaningful output if anything should go +// wrong: int main () { try diff --git a/deal.II/examples/step-28/step-28.cc b/deal.II/examples/step-28/step-28.cc index 64b0276c58..0363c2dad6 100644 --- a/deal.II/examples/step-28/step-28.cc +++ b/deal.II/examples/step-28/step-28.cc @@ -9,12 +9,12 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // We start with a bunch of include - // files that have already been - // explained in previous tutorial - // programs: +// We start with a bunch of include +// files that have already been +// explained in previous tutorial +// programs: #include #include #include @@ -50,246 +50,246 @@ #include - // We use the next include file to - // access block vectors which provide - // us a convenient way to manage - // solution and right hand side - // vectors of all energy groups: +// We use the next include file to +// access block vectors which provide +// us a convenient way to manage +// solution and right hand side +// vectors of all energy groups: #include - // This include file is for - // transferring solutions from one - // mesh to another different mesh. We - // use it when we are initializing - // solutions after each mesh - // iteration: +// This include file is for +// transferring solutions from one +// mesh to another different mesh. We +// use it when we are initializing +// solutions after each mesh +// iteration: #include - // When integrating functions defined - // on one mesh against shape - // functions defined on a different - // mesh, we need a function @p - // get_finest_common_cells (as - // discussed in the introduction) - // which is defined in the following - // header file: +// When integrating functions defined +// on one mesh against shape +// functions defined on a different +// mesh, we need a function @p +// get_finest_common_cells (as +// discussed in the introduction) +// which is defined in the following +// header file: #include - // Here are two more C++ standard - // headers that we use to define list - // data types as well as to fine-tune - // the output we generate: +// Here are two more C++ standard +// headers that we use to define list +// data types as well as to fine-tune +// the output we generate: #include #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step28 { using namespace dealii; - // @sect3{Material data} - - // First up, we need to define a - // class that provides material data - // (including diffusion coefficients, - // removal cross sections, scattering - // cross sections, fission cross - // sections and fission spectra) to - // the main class. - // - // The parameter to the constructor - // determines for how many energy - // groups we set up the relevant - // tables. At present, this program - // only includes data for 2 energy - // groups, but a more sophisticated - // program may be able to initialize - // the data structures for more - // groups as well, depending on how - // many energy groups are selected in - // the parameter file. - // - // For each of the different - // coefficient types, there is one - // function that returns the value of - // this coefficient for a particular - // energy group (or combination of - // energy groups, as for the - // distribution cross section - // $\chi_g\nu\Sigma_{f,g'}$ or - // scattering cross section - // $\Sigma_{s,g'\to g}$). In addition - // to the energy group or groups, - // these coefficients depend on the - // type of fuel or control rod, as - // explained in the introduction. The - // functions therefore take an - // additional parameter, @p - // material_id, that identifies the - // particular kind of rod. Within - // this program, we use - // n_materials=8 - // different kinds of rods. - // - // Except for the scattering cross - // section, each of the coefficients - // therefore can be represented as an - // entry in a two-dimensional array - // of floating point values indexed - // by the energy group number as well - // as the material ID. The Table - // class template is the ideal way to - // store such data. Finally, the - // scattering coefficient depends on - // both two energy group indices and - // therefore needs to be stored in a - // three-dimensional array, for which - // we again use the Table class, - // where this time the first template - // argument (denoting the - // dimensionality of the array) of - // course needs to be three: + // @sect3{Material data} + + // First up, we need to define a + // class that provides material data + // (including diffusion coefficients, + // removal cross sections, scattering + // cross sections, fission cross + // sections and fission spectra) to + // the main class. + // + // The parameter to the constructor + // determines for how many energy + // groups we set up the relevant + // tables. At present, this program + // only includes data for 2 energy + // groups, but a more sophisticated + // program may be able to initialize + // the data structures for more + // groups as well, depending on how + // many energy groups are selected in + // the parameter file. + // + // For each of the different + // coefficient types, there is one + // function that returns the value of + // this coefficient for a particular + // energy group (or combination of + // energy groups, as for the + // distribution cross section + // $\chi_g\nu\Sigma_{f,g'}$ or + // scattering cross section + // $\Sigma_{s,g'\to g}$). In addition + // to the energy group or groups, + // these coefficients depend on the + // type of fuel or control rod, as + // explained in the introduction. The + // functions therefore take an + // additional parameter, @p + // material_id, that identifies the + // particular kind of rod. Within + // this program, we use + // n_materials=8 + // different kinds of rods. + // + // Except for the scattering cross + // section, each of the coefficients + // therefore can be represented as an + // entry in a two-dimensional array + // of floating point values indexed + // by the energy group number as well + // as the material ID. The Table + // class template is the ideal way to + // store such data. Finally, the + // scattering coefficient depends on + // both two energy group indices and + // therefore needs to be stored in a + // three-dimensional array, for which + // we again use the Table class, + // where this time the first template + // argument (denoting the + // dimensionality of the array) of + // course needs to be three: class MaterialData { - public: - MaterialData (const unsigned int n_groups); - - double get_diffusion_coefficient (const unsigned int group, - const unsigned int material_id) const; - double get_removal_XS (const unsigned int group, - const unsigned int material_id) const; - double get_fission_XS (const unsigned int group, - const unsigned int material_id) const; - double get_fission_dist_XS (const unsigned int group_1, - const unsigned int group_2, - const unsigned int material_id) const; - double get_scattering_XS (const unsigned int group_1, + public: + MaterialData (const unsigned int n_groups); + + double get_diffusion_coefficient (const unsigned int group, + const unsigned int material_id) const; + double get_removal_XS (const unsigned int group, + const unsigned int material_id) const; + double get_fission_XS (const unsigned int group, + const unsigned int material_id) const; + double get_fission_dist_XS (const unsigned int group_1, const unsigned int group_2, const unsigned int material_id) const; - double get_fission_spectrum (const unsigned int group, - const unsigned int material_id) const; - - private: - const unsigned int n_groups; - const unsigned int n_materials; - - Table<2,double> diffusion; - Table<2,double> sigma_r; - Table<2,double> nu_sigma_f; - Table<3,double> sigma_s; - Table<2,double> chi; + double get_scattering_XS (const unsigned int group_1, + const unsigned int group_2, + const unsigned int material_id) const; + double get_fission_spectrum (const unsigned int group, + const unsigned int material_id) const; + + private: + const unsigned int n_groups; + const unsigned int n_materials; + + Table<2,double> diffusion; + Table<2,double> sigma_r; + Table<2,double> nu_sigma_f; + Table<3,double> sigma_s; + Table<2,double> chi; }; - // The constructor of the class is - // used to initialize all the - // material data arrays. It takes the - // number of energy groups as an - // argument (an throws an error if - // that value is not equal to two, - // since at presently only data for - // two energy groups is implemented; - // however, using this, the function - // remains flexible and extendible - // into the future). In the member - // initialization part at the - // beginning, it also resizes the - // arrays to their correct sizes. - // - // At present, material data is - // stored for 8 different types of - // material. This, as well, may - // easily be extended in the future. + // The constructor of the class is + // used to initialize all the + // material data arrays. It takes the + // number of energy groups as an + // argument (an throws an error if + // that value is not equal to two, + // since at presently only data for + // two energy groups is implemented; + // however, using this, the function + // remains flexible and extendible + // into the future). In the member + // initialization part at the + // beginning, it also resizes the + // arrays to their correct sizes. + // + // At present, material data is + // stored for 8 different types of + // material. This, as well, may + // easily be extended in the future. MaterialData::MaterialData (const unsigned int n_groups) - : - n_groups (n_groups), - n_materials (8), - diffusion (n_materials, n_groups), - sigma_r (n_materials, n_groups), - nu_sigma_f (n_materials, n_groups), - sigma_s (n_materials, n_groups, n_groups), - chi (n_materials, n_groups) + : + n_groups (n_groups), + n_materials (8), + diffusion (n_materials, n_groups), + sigma_r (n_materials, n_groups), + nu_sigma_f (n_materials, n_groups), + sigma_s (n_materials, n_groups, n_groups), + chi (n_materials, n_groups) { switch (n_groups) { - case 2: - { - for (unsigned int m=0; mEnergyGroup class} - - // The first interesting class is the - // one that contains everything that - // is specific to a single energy - // group. To group things that belong - // together into individual objects, - // we declare a structure that holds - // the Triangulation and DoFHandler - // objects for the mesh used for a - // single energy group, and a number - // of other objects and member - // functions that we will discuss in - // the following sections. - // - // The main reason for this class is - // as follows: for both the forward - // problem (with a specified right - // hand side) as well as for the - // eigenvalue problem, one typically - // solves a sequence of problems for - // a single energy group each, rather - // than the fully coupled - // problem. This becomes - // understandable once one realizes - // that the system matrix for a - // single energy group is symmetric - // and positive definite (it is - // simply a diffusion operator), - // whereas the matrix for the fully - // coupled problem is generally - // nonsymmetric and not definite. It - // is also very large and quite full - // if more than a few energy groups - // are involved. - // - // Let us first look at the equation - // to solve in the case of an - // external right hand side (for the time - // independent case): - // @f{eqnarray*} - // -\nabla \cdot(D_g(x) \nabla \phi_g(x)) - // + - // \Sigma_{r,g}(x)\phi_g(x) - // = - // \chi_g\sum_{g'=1}^G\nu\Sigma_{f,g'}(x)\phi_{g'}(x) - // + - // \sum_{g'\ne g}\Sigma_{s,g'\to g}(x)\phi_{g'}(x) - // + - // s_{\mathrm{ext},g}(x) - // @f} - // - // We would typically solve this - // equation by moving all the terms - // on the right hand side with $g'=g$ - // to the left hand side, and solving - // for $\phi_g$. Of course, we don't - // know $\phi_{g'}$ yet, since the - // equations for those variables - // include right hand side terms - // involving $\phi_g$. What one - // typically does in such situations - // is to iterate: compute - // @f{eqnarray*} - // -\nabla \cdot(D_g(x) \nabla \phi^{(n)}_g(x)) - // &+& - // \Sigma_{r,g}(x)\phi^{(n)}_g(x) - // \\ &=& - // \chi_g\sum_{g'=1}^{g-1}\nu\Sigma_{f,g'}(x)\phi^{(n)}_{g'}(x) - // + - // \chi_g\sum_{g'=g}^G\nu\Sigma_{f,g'}(x)\phi^{(n-1)}_{g'}(x) - // + - // \sum_{g'\ne g, g'g}\Sigma_{s,g'\to g}(x)\phi^{(n-1)}_{g'}(x) - // + - // s_{\mathrm{ext},g}(x) - // @f} - // - // In other words, we solve the - // equation one by one, using values - // for $\phi_{g'}$ from the previous - // iteration $n-1$ if $g'\ge g$ and - // already computed values for - // $\phi_{g'}$ from the present - // iteration if $g'EnergyGroup class} + + // The first interesting class is the + // one that contains everything that + // is specific to a single energy + // group. To group things that belong + // together into individual objects, + // we declare a structure that holds + // the Triangulation and DoFHandler + // objects for the mesh used for a + // single energy group, and a number + // of other objects and member + // functions that we will discuss in + // the following sections. + // + // The main reason for this class is + // as follows: for both the forward + // problem (with a specified right + // hand side) as well as for the + // eigenvalue problem, one typically + // solves a sequence of problems for + // a single energy group each, rather + // than the fully coupled + // problem. This becomes + // understandable once one realizes + // that the system matrix for a + // single energy group is symmetric + // and positive definite (it is + // simply a diffusion operator), + // whereas the matrix for the fully + // coupled problem is generally + // nonsymmetric and not definite. It + // is also very large and quite full + // if more than a few energy groups + // are involved. + // + // Let us first look at the equation + // to solve in the case of an + // external right hand side (for the time + // independent case): + // @f{eqnarray*} + // -\nabla \cdot(D_g(x) \nabla \phi_g(x)) + // + + // \Sigma_{r,g}(x)\phi_g(x) + // = + // \chi_g\sum_{g'=1}^G\nu\Sigma_{f,g'}(x)\phi_{g'}(x) + // + + // \sum_{g'\ne g}\Sigma_{s,g'\to g}(x)\phi_{g'}(x) + // + + // s_{\mathrm{ext},g}(x) + // @f} + // + // We would typically solve this + // equation by moving all the terms + // on the right hand side with $g'=g$ + // to the left hand side, and solving + // for $\phi_g$. Of course, we don't + // know $\phi_{g'}$ yet, since the + // equations for those variables + // include right hand side terms + // involving $\phi_g$. What one + // typically does in such situations + // is to iterate: compute + // @f{eqnarray*} + // -\nabla \cdot(D_g(x) \nabla \phi^{(n)}_g(x)) + // &+& + // \Sigma_{r,g}(x)\phi^{(n)}_g(x) + // \\ &=& + // \chi_g\sum_{g'=1}^{g-1}\nu\Sigma_{f,g'}(x)\phi^{(n)}_{g'}(x) + // + + // \chi_g\sum_{g'=g}^G\nu\Sigma_{f,g'}(x)\phi^{(n-1)}_{g'}(x) + // + + // \sum_{g'\ne g, g'g}\Sigma_{s,g'\to g}(x)\phi^{(n-1)}_{g'}(x) + // + + // s_{\mathrm{ext},g}(x) + // @f} + // + // In other words, we solve the + // equation one by one, using values + // for $\phi_{g'}$ from the previous + // iteration $n-1$ if $g'\ge g$ and + // already computed values for + // $\phi_{g'}$ from the present + // iteration if $g' class EnergyGroup { - public: - - // @sect5{Public member functions} - // - // The class has a good number of - // public member functions, since - // its the way it operates is - // controlled from the outside, - // and therefore all functions - // that do something significant - // need to be called from another - // class. Let's start off with - // book-keeping: the class - // obviously needs to know which - // energy group it represents, - // which material data to use, - // and from what coarse grid to - // start. The constructor takes - // this information and - // initializes the relevant - // member variables with that - // (see below). - // - // Then we also need functions - // that set up the linear system, - // i.e. correctly size the matrix - // and its sparsity pattern, etc, - // given a finite element object - // to use. The - // setup_linear_system - // function does that. Finally, - // for this initial block, there - // are two functions that return - // the number of active cells and - // degrees of freedom used in - // this object -- using this, we - // can make the triangulation and - // DoF handler member variables - // private, and do not have to - // grant external use to it, - // enhancing encapsulation: - EnergyGroup (const unsigned int group, - const MaterialData &material_data, - const Triangulation &coarse_grid, - const FiniteElement &fe); - - void setup_linear_system (); - - unsigned int n_active_cells () const; - unsigned int n_dofs () const; - - // Then there are functions that - // assemble the linear system for - // each iteration and the present - // energy group. Note that the - // matrix is independent of the - // iteration number, so only has - // to be computed once for each - // refinement cycle. The - // situation is a bit more - // involved for the right hand - // side that has to be updated in - // each inverse power iteration, - // and that is further - // complicated by the fact that - // computing it may involve - // several different meshes as - // explained in the - // introduction. To make things - // more flexible with regard to - // solving the forward or the - // eigenvalue problem, we split - // the computation of the right - // hand side into a function that - // assembles the extraneous - // source and in-group - // contributions (which we will - // call with a zero function as - // source terms for the - // eigenvalue problem) and one - // that computes contributions to - // the right hand side from - // another energy group: - void assemble_system_matrix (); - void assemble_ingroup_rhs (const Function &extraneous_source); - void assemble_cross_group_rhs (const EnergyGroup &g_prime); - - // Next we need a set of - // functions that actually - // compute the solution of a - // linear system, and do - // something with it (such as - // computing the fission source - // contribution mentioned in the - // introduction, writing - // graphical information to an - // output file, computing error - // indicators, or actually - // refining the grid based on - // these criteria and thresholds - // for refinement and - // coarsening). All these - // functions will later be called - // from the driver class - // NeutronDiffusionProblem, - // or any other class you may - // want to implement to solve a - // problem involving the neutron - // flux equations: - void solve (); - - double get_fission_source () const; - - void output_results (const unsigned int cycle) const; - - void estimate_errors (Vector &error_indicators) const; - - void refine_grid (const Vector &error_indicators, - const double refine_threshold, - const double coarsen_threshold); - - // @sect5{Public data members} - // - // As is good practice in object - // oriented programming, we hide - // most data members by making - // them private. However, we have - // to grant the class that drives - // the process access to the - // solution vector as well as the - // solution of the previous - // iteration, since in the power - // iteration, the solution vector - // is scaled in every iteration - // by the present guess of the - // eigenvalue we are looking for: - public: - - Vector solution; - Vector solution_old; - - - // @sect5{Private data members} - // - // The rest of the data members - // are private. Compared to all - // the previous tutorial - // programs, the only new data - // members are an integer storing - // which energy group this object - // represents, and a reference to - // the material data object that - // this object's constructor gets - // passed from the driver - // class. Likewise, the - // constructor gets a reference - // to the finite element object - // we are to use. - // - // Finally, we have to apply - // boundary values to the linear - // system in each iteration, - // i.e. quite frequently. Rather - // than interpolating them every - // time, we interpolate them once - // on each new mesh and then - // store them along with all the - // other data of this class: - private: - - const unsigned int group; - const MaterialData &material_data; - - Triangulation triangulation; - const FiniteElement &fe; - DoFHandler dof_handler; - - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - - Vector system_rhs; - - std::map boundary_values; - ConstraintMatrix hanging_node_constraints; - - - // @sect5{Private member functionss} - // - // There is one private member - // function in this class. It - // recursively walks over cells - // of two meshes to compute the - // cross-group right hand side - // terms. The algorithm for this - // is explained in the - // introduction to this - // program. The arguments to this - // function are a reference to an - // object representing the energy - // group against which we want to - // integrate a right hand side - // term, an iterator to a cell of - // the mesh used for the present - // energy group, an iterator to a - // corresponding cell on the - // other mesh, and the matrix - // that interpolates the degrees - // of freedom from the coarser of - // the two cells to the finer - // one: - private: - - void - assemble_cross_group_rhs_recursive (const EnergyGroup &g_prime, - const typename DoFHandler::cell_iterator &cell_g, - const typename DoFHandler::cell_iterator &cell_g_prime, - const FullMatrix prolongation_matrix); + public: + + // @sect5{Public member functions} + // + // The class has a good number of + // public member functions, since + // its the way it operates is + // controlled from the outside, + // and therefore all functions + // that do something significant + // need to be called from another + // class. Let's start off with + // book-keeping: the class + // obviously needs to know which + // energy group it represents, + // which material data to use, + // and from what coarse grid to + // start. The constructor takes + // this information and + // initializes the relevant + // member variables with that + // (see below). + // + // Then we also need functions + // that set up the linear system, + // i.e. correctly size the matrix + // and its sparsity pattern, etc, + // given a finite element object + // to use. The + // setup_linear_system + // function does that. Finally, + // for this initial block, there + // are two functions that return + // the number of active cells and + // degrees of freedom used in + // this object -- using this, we + // can make the triangulation and + // DoF handler member variables + // private, and do not have to + // grant external use to it, + // enhancing encapsulation: + EnergyGroup (const unsigned int group, + const MaterialData &material_data, + const Triangulation &coarse_grid, + const FiniteElement &fe); + + void setup_linear_system (); + + unsigned int n_active_cells () const; + unsigned int n_dofs () const; + + // Then there are functions that + // assemble the linear system for + // each iteration and the present + // energy group. Note that the + // matrix is independent of the + // iteration number, so only has + // to be computed once for each + // refinement cycle. The + // situation is a bit more + // involved for the right hand + // side that has to be updated in + // each inverse power iteration, + // and that is further + // complicated by the fact that + // computing it may involve + // several different meshes as + // explained in the + // introduction. To make things + // more flexible with regard to + // solving the forward or the + // eigenvalue problem, we split + // the computation of the right + // hand side into a function that + // assembles the extraneous + // source and in-group + // contributions (which we will + // call with a zero function as + // source terms for the + // eigenvalue problem) and one + // that computes contributions to + // the right hand side from + // another energy group: + void assemble_system_matrix (); + void assemble_ingroup_rhs (const Function &extraneous_source); + void assemble_cross_group_rhs (const EnergyGroup &g_prime); + + // Next we need a set of + // functions that actually + // compute the solution of a + // linear system, and do + // something with it (such as + // computing the fission source + // contribution mentioned in the + // introduction, writing + // graphical information to an + // output file, computing error + // indicators, or actually + // refining the grid based on + // these criteria and thresholds + // for refinement and + // coarsening). All these + // functions will later be called + // from the driver class + // NeutronDiffusionProblem, + // or any other class you may + // want to implement to solve a + // problem involving the neutron + // flux equations: + void solve (); + + double get_fission_source () const; + + void output_results (const unsigned int cycle) const; + + void estimate_errors (Vector &error_indicators) const; + + void refine_grid (const Vector &error_indicators, + const double refine_threshold, + const double coarsen_threshold); + + // @sect5{Public data members} + // + // As is good practice in object + // oriented programming, we hide + // most data members by making + // them private. However, we have + // to grant the class that drives + // the process access to the + // solution vector as well as the + // solution of the previous + // iteration, since in the power + // iteration, the solution vector + // is scaled in every iteration + // by the present guess of the + // eigenvalue we are looking for: + public: + + Vector solution; + Vector solution_old; + + + // @sect5{Private data members} + // + // The rest of the data members + // are private. Compared to all + // the previous tutorial + // programs, the only new data + // members are an integer storing + // which energy group this object + // represents, and a reference to + // the material data object that + // this object's constructor gets + // passed from the driver + // class. Likewise, the + // constructor gets a reference + // to the finite element object + // we are to use. + // + // Finally, we have to apply + // boundary values to the linear + // system in each iteration, + // i.e. quite frequently. Rather + // than interpolating them every + // time, we interpolate them once + // on each new mesh and then + // store them along with all the + // other data of this class: + private: + + const unsigned int group; + const MaterialData &material_data; + + Triangulation triangulation; + const FiniteElement &fe; + DoFHandler dof_handler; + + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + + Vector system_rhs; + + std::map boundary_values; + ConstraintMatrix hanging_node_constraints; + + + // @sect5{Private member functionss} + // + // There is one private member + // function in this class. It + // recursively walks over cells + // of two meshes to compute the + // cross-group right hand side + // terms. The algorithm for this + // is explained in the + // introduction to this + // program. The arguments to this + // function are a reference to an + // object representing the energy + // group against which we want to + // integrate a right hand side + // term, an iterator to a cell of + // the mesh used for the present + // energy group, an iterator to a + // corresponding cell on the + // other mesh, and the matrix + // that interpolates the degrees + // of freedom from the coarser of + // the two cells to the finer + // one: + private: + + void + assemble_cross_group_rhs_recursive (const EnergyGroup &g_prime, + const typename DoFHandler::cell_iterator &cell_g, + const typename DoFHandler::cell_iterator &cell_g_prime, + const FullMatrix prolongation_matrix); }; - // @sect4{Implementation of the EnergyGroup class} - - // The first few functions of this - // class are mostly - // self-explanatory. The constructor - // only sets a few data members and - // creates a copy of the given - // triangulation as the base for the - // triangulation used for this energy - // group. The next two functions - // simply return data from private - // data members, thereby enabling us - // to make these data members - // private. + // @sect4{Implementation of the EnergyGroup class} + + // The first few functions of this + // class are mostly + // self-explanatory. The constructor + // only sets a few data members and + // creates a copy of the given + // triangulation as the base for the + // triangulation used for this energy + // group. The next two functions + // simply return data from private + // data members, thereby enabling us + // to make these data members + // private. template EnergyGroup::EnergyGroup (const unsigned int group, const MaterialData &material_data, const Triangulation &coarse_grid, const FiniteElement &fe) - : - group (group), - material_data (material_data), - fe (fe), - dof_handler (triangulation) + : + group (group), + material_data (material_data), + fe (fe), + dof_handler (triangulation) { triangulation.copy_triangulation (coarse_grid); dof_handler.distribute_dofs (fe); @@ -778,27 +778,27 @@ namespace Step28 - // @sect5{EnergyGroup::setup_linear_system} - // - // The first "real" function is the - // one that sets up the mesh, - // matrices, etc, on the new mesh or - // after mesh refinement. We use this - // function to initialize sparse - // system matrices, and the right - // hand side vector. If the solution - // vector has never been set before - // (as indicated by a zero size), we - // also initialize it and set it to a - // default value. We don't do that if - // it already has a non-zero size - // (i.e. this function is called - // after mesh refinement) since in - // that case we want to preserve the - // solution across mesh refinement - // (something we do in the - // EnergyGroup::refine_grid - // function). + // @sect5{EnergyGroup::setup_linear_system} + // + // The first "real" function is the + // one that sets up the mesh, + // matrices, etc, on the new mesh or + // after mesh refinement. We use this + // function to initialize sparse + // system matrices, and the right + // hand side vector. If the solution + // vector has never been set before + // (as indicated by a zero size), we + // also initialize it and set it to a + // default value. We don't do that if + // it already has a non-zero size + // (i.e. this function is called + // after mesh refinement) since in + // that case we want to preserve the + // solution across mesh refinement + // (something we do in the + // EnergyGroup::refine_grid + // function). template void EnergyGroup::setup_linear_system () @@ -831,56 +831,56 @@ namespace Step28 } - // At the end of this function, we - // update the list of boundary - // nodes and their values, by first - // clearing this list and the - // re-interpolating boundary values - // (remember that this function is - // called after first setting up - // the mesh, and each time after - // mesh refinement). - // - // To understand the code, it is - // necessary to realize that we - // create the mesh using the - // GridGenerator::subdivided_hyper_rectangle - // function (in - // NeutronDiffusionProblem::initialize_problem) - // where we set the last parameter - // to true. This means that - // boundaries of the domain are - // "colored", i.e. the four (or - // six, in 3d) sides of the domain - // are assigned different boundary - // indicators. As it turns out, the - // bottom boundary gets indicator - // zero, the top one boundary - // indicator one, and left and - // right boundaries get indicators - // two and three, respectively. - // - // In this program, we simulate - // only one, namely the top right, - // quarter of a reactor. That is, - // we want to interpolate boundary - // conditions only on the top and - // right boundaries, while do - // nothing on the bottom and left - // boundaries (i.e. impose natural, - // no-flux Neumann boundary - // conditions). This is most easily - // generalized to arbitrary - // dimension by saying that we want - // to interpolate on those - // boundaries with indicators 1, 3, - // ..., which we do in the - // following loop (note that calls - // to - // VectorTools::interpolate_boundary_values - // are additive, i.e. they do not - // first clear the boundary value - // map): + // At the end of this function, we + // update the list of boundary + // nodes and their values, by first + // clearing this list and the + // re-interpolating boundary values + // (remember that this function is + // called after first setting up + // the mesh, and each time after + // mesh refinement). + // + // To understand the code, it is + // necessary to realize that we + // create the mesh using the + // GridGenerator::subdivided_hyper_rectangle + // function (in + // NeutronDiffusionProblem::initialize_problem) + // where we set the last parameter + // to true. This means that + // boundaries of the domain are + // "colored", i.e. the four (or + // six, in 3d) sides of the domain + // are assigned different boundary + // indicators. As it turns out, the + // bottom boundary gets indicator + // zero, the top one boundary + // indicator one, and left and + // right boundaries get indicators + // two and three, respectively. + // + // In this program, we simulate + // only one, namely the top right, + // quarter of a reactor. That is, + // we want to interpolate boundary + // conditions only on the top and + // right boundaries, while do + // nothing on the bottom and left + // boundaries (i.e. impose natural, + // no-flux Neumann boundary + // conditions). This is most easily + // generalized to arbitrary + // dimension by saying that we want + // to interpolate on those + // boundaries with indicators 1, 3, + // ..., which we do in the + // following loop (note that calls + // to + // VectorTools::interpolate_boundary_values + // are additive, i.e. they do not + // first clear the boundary value + // map): boundary_values.clear(); for (unsigned int i=0; iEnergyGroup::assemble_system_matrix} - // - // Next we need functions assembling - // the system matrix and right hand - // sides. Assembling the matrix is - // straightforward given the - // equations outlined in the - // introduction as well as what we've - // seen in previous example - // programs. Note the use of - // cell->material_id() to get at - // the kind of material from which a - // cell is made up of. Note also how - // we set the order of the quadrature - // formula so that it is always - // appropriate for the finite element - // in use. - // - // Finally, note that since we only - // assemble the system matrix here, - // we can't yet eliminate boundary - // values (we need the right hand - // side vector for this). We defer - // this to the EnergyGroup::solve - // function, at which point all the - // information is available. + // @sect5{EnergyGroup::assemble_system_matrix} + // + // Next we need functions assembling + // the system matrix and right hand + // sides. Assembling the matrix is + // straightforward given the + // equations outlined in the + // introduction as well as what we've + // seen in previous example + // programs. Note the use of + // cell->material_id() to get at + // the kind of material from which a + // cell is made up of. Note also how + // we set the order of the quadrature + // formula so that it is always + // appropriate for the finite element + // in use. + // + // Finally, note that since we only + // assemble the system matrix here, + // we can't yet eliminate boundary + // values (we need the right hand + // side vector for this). We defer + // this to the EnergyGroup::solve + // function, at which point all the + // information is available. template void EnergyGroup::assemble_system_matrix () @@ -937,8 +937,8 @@ namespace Step28 std::vector local_dof_indices (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { @@ -978,35 +978,35 @@ namespace Step28 - // @sect5{EnergyGroup::assemble_ingroup_rhs} - // - // As explained in the documentation - // of the EnergyGroup class, we - // split assembling the right hand - // side into two parts: the ingroup - // and the cross-group - // couplings. First, we need a - // function to assemble the right - // hand side of one specific group - // here, i.e. including an extraneous - // source (that we will set to zero - // for the eigenvalue problem) as - // well as the ingroup fission - // contributions. (In-group - // scattering has already been - // accounted for with the definition - // of removal cross section.) The - // function's workings are pretty - // standard as far as assembling - // right hand sides go, and therefore - // does not require more comments - // except that we mention that the - // right hand side vector is set to - // zero at the beginning of the - // function -- something we are not - // going to do for the cross-group - // terms that simply add to the right - // hand side vector. + // @sect5{EnergyGroup::assemble_ingroup_rhs} + // + // As explained in the documentation + // of the EnergyGroup class, we + // split assembling the right hand + // side into two parts: the ingroup + // and the cross-group + // couplings. First, we need a + // function to assemble the right + // hand side of one specific group + // here, i.e. including an extraneous + // source (that we will set to zero + // for the eigenvalue problem) as + // well as the ingroup fission + // contributions. (In-group + // scattering has already been + // accounted for with the definition + // of removal cross section.) The + // function's workings are pretty + // standard as far as assembling + // right hand sides go, and therefore + // does not require more comments + // except that we mention that the + // right hand side vector is set to + // zero at the beginning of the + // function -- something we are not + // going to do for the cross-group + // terms that simply add to the right + // hand side vector. template void EnergyGroup::assemble_ingroup_rhs (const Function &extraneous_source) { @@ -1028,8 +1028,8 @@ namespace Step28 std::vector local_dof_indices (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { @@ -1063,31 +1063,31 @@ namespace Step28 - // @sect5{EnergyGroup::assemble_cross_group_rhs} - // - // The more interesting function for - // assembling the right hand side - // vector for the equation of a - // single energy group is the one - // that couples energy group $g$ and - // $g'$. As explained in the - // introduction, we first have to - // find the set of cells common to - // the meshes of the two energy - // groups. First we call - // get_finest_common_cells to - // obtain this list of pairs of - // common cells from both - // meshes. Both cells in a pair may - // not be active but at least one of - // them is. We then hand each of - // these cell pairs off to a function - // tha computes the right hand side - // terms recursively. - // - // Note that ingroup coupling is - // handled already before, so we exit - // the function early if $g=g'$. + // @sect5{EnergyGroup::assemble_cross_group_rhs} + // + // The more interesting function for + // assembling the right hand side + // vector for the equation of a + // single energy group is the one + // that couples energy group $g$ and + // $g'$. As explained in the + // introduction, we first have to + // find the set of cells common to + // the meshes of the two energy + // groups. First we call + // get_finest_common_cells to + // obtain this list of pairs of + // common cells from both + // meshes. Both cells in a pair may + // not be active but at least one of + // them is. We then hand each of + // these cell pairs off to a function + // tha computes the right hand side + // terms recursively. + // + // Note that ingroup coupling is + // handled already before, so we exit + // the function early if $g=g'$. template void EnergyGroup::assemble_cross_group_rhs (const EnergyGroup &g_prime) { @@ -1095,15 +1095,15 @@ namespace Step28 return; const std::list::cell_iterator, - typename DoFHandler::cell_iterator> > - cell_list - = GridTools::get_finest_common_cells (dof_handler, - g_prime.dof_handler); + typename DoFHandler::cell_iterator> > + cell_list + = GridTools::get_finest_common_cells (dof_handler, + g_prime.dof_handler); typename std::list::cell_iterator, - typename DoFHandler::cell_iterator> > - ::const_iterator - cell_iter = cell_list.begin(); + typename DoFHandler::cell_iterator> > + ::const_iterator + cell_iter = cell_list.begin(); for (; cell_iter!=cell_list.end(); ++cell_iter) { @@ -1119,51 +1119,51 @@ namespace Step28 - // @sect5{EnergyGroup::assemble_cross_group_rhs_recursive} - // - // This is finally the function that - // handles assembling right hand side - // terms on potentially different - // meshes recursively, using the - // algorithm described in the - // introduction. The function takes a - // reference to the object - // representing energy group $g'$, as - // well as iterators to corresponding - // cells in the meshes for energy - // groups $g$ and $g'$. At first, - // i.e. when this function is called - // from the one above, these two - // cells will be matching cells on - // two meshes; however, one of the - // two may be further refined, and we - // will call the function recursively - // with one of the two iterators - // replaced by one of the children of - // the original cell. - // - // The last argument is the matrix - // product matrix $B_{c^{(k)}}^T - // \cdots B_{c'}^T B_c^T$ from the - // introduction that interpolates - // from the coarser of the two cells - // to the finer one. If the two cells - // match, then this is the identity - // matrix -- exactly what we pass to - // this function initially. - // - // The function has to consider two - // cases: that both of the two cells - // are not further refined, i.e. have - // no children, in which case we can - // finally assemble the right hand - // side contributions of this pair of - // cells; and that one of the two - // cells is further refined, in which - // case we have to keep recursing by - // looping over the children of the - // one cell that is not active. These - // two cases will be discussed below: + // @sect5{EnergyGroup::assemble_cross_group_rhs_recursive} + // + // This is finally the function that + // handles assembling right hand side + // terms on potentially different + // meshes recursively, using the + // algorithm described in the + // introduction. The function takes a + // reference to the object + // representing energy group $g'$, as + // well as iterators to corresponding + // cells in the meshes for energy + // groups $g$ and $g'$. At first, + // i.e. when this function is called + // from the one above, these two + // cells will be matching cells on + // two meshes; however, one of the + // two may be further refined, and we + // will call the function recursively + // with one of the two iterators + // replaced by one of the children of + // the original cell. + // + // The last argument is the matrix + // product matrix $B_{c^{(k)}}^T + // \cdots B_{c'}^T B_c^T$ from the + // introduction that interpolates + // from the coarser of the two cells + // to the finer one. If the two cells + // match, then this is the identity + // matrix -- exactly what we pass to + // this function initially. + // + // The function has to consider two + // cases: that both of the two cells + // are not further refined, i.e. have + // no children, in which case we can + // finally assemble the right hand + // side contributions of this pair of + // cells; and that one of the two + // cells is further refined, in which + // case we have to keep recursing by + // looping over the children of the + // one cell that is not active. These + // two cases will be discussed below: template void EnergyGroup:: @@ -1172,25 +1172,25 @@ namespace Step28 const typename DoFHandler::cell_iterator &cell_g_prime, const FullMatrix prolongation_matrix) { - // The first case is that both - // cells are no further refined. In - // that case, we can assemble the - // relevant terms (see the - // introduction). This involves - // assembling the mass matrix on - // the finer of the two cells (in - // fact there are two mass matrices - // with different coefficients, one - // for the fission distribution - // cross section - // $\chi_g\nu\Sigma_{f,g'}$ and one - // for the scattering cross section - // $\Sigma_{s,g'\to g}$). This is - // straight forward, but note how - // we determine which of the two - // cells is the finer one by - // looking at the refinement level - // of the two cells: + // The first case is that both + // cells are no further refined. In + // that case, we can assemble the + // relevant terms (see the + // introduction). This involves + // assembling the mass matrix on + // the finer of the two cells (in + // fact there are two mass matrices + // with different coefficients, one + // for the fission distribution + // cross section + // $\chi_g\nu\Sigma_{f,g'}$ and one + // for the scattering cross section + // $\Sigma_{s,g'\to g}$). This is + // straight forward, but note how + // we determine which of the two + // cells is the finer one by + // looking at the refinement level + // of the two cells: if (!cell_g->has_children() && !cell_g_prime->has_children()) { const QGauss quadrature_formula (fe.degree+1); @@ -1231,37 +1231,37 @@ namespace Step28 fe_values.JxW(q_point)); } - // Now we have all the - // interpolation (prolongation) - // matrices as well as local - // mass matrices, so we only - // have to form the product - // @f[ - // F_i|_{K_{cc'\cdots - // c^{(k)}}} = [B_c B_{c'} - // \cdots B_{c^{(k)}} - // M_{K_{cc'\cdots - // c^{(k)}}}]^{ij} - // \phi_{g'}^j, - // @f] - // or - // @f[ - // F_i|_{K_{cc'\cdots - // c^{(k)}}} = [(B_c B_{c'} - // \cdots B_{c^{(k)}} - // M_{K_{cc'\cdots - // c^{(k)}}})^T]^{ij} - // \phi_{g'}^j, - // @f] - // depending on which of the two - // cells is the finer. We do this - // using either the matrix-vector - // product provided by the vmult - // function, or the product with the - // transpose matrix using Tvmult. - // After doing so, we transfer the - // result into the global right hand - // side vector of energy group $g$. + // Now we have all the + // interpolation (prolongation) + // matrices as well as local + // mass matrices, so we only + // have to form the product + // @f[ + // F_i|_{K_{cc'\cdots + // c^{(k)}}} = [B_c B_{c'} + // \cdots B_{c^{(k)}} + // M_{K_{cc'\cdots + // c^{(k)}}}]^{ij} + // \phi_{g'}^j, + // @f] + // or + // @f[ + // F_i|_{K_{cc'\cdots + // c^{(k)}}} = [(B_c B_{c'} + // \cdots B_{c^{(k)}} + // M_{K_{cc'\cdots + // c^{(k)}}})^T]^{ij} + // \phi_{g'}^j, + // @f] + // depending on which of the two + // cells is the finer. We do this + // using either the matrix-vector + // product provided by the vmult + // function, or the product with the + // transpose matrix using Tvmult. + // After doing so, we transfer the + // result into the global right hand + // side vector of energy group $g$. Vector g_prime_new_values (fe.dofs_per_cell); Vector g_prime_old_values (fe.dofs_per_cell); cell_g_prime->get_dof_values (g_prime.solution_old, g_prime_old_values); @@ -1294,24 +1294,24 @@ namespace Step28 system_rhs(local_dof_indices[i]) += cell_rhs(i); } - // The alternative is that one of - // the two cells is further - // refined. In that case, we have - // to loop over all the children, - // multiply the existing - // interpolation (prolongation) - // product of matrices from the - // left with the interpolation from - // the present cell to its child - // (using the matrix-matrix - // multiplication function - // mmult), and then hand the - // result off to this very same - // function again, but with the - // cell that has children replaced - // by one of its children: + // The alternative is that one of + // the two cells is further + // refined. In that case, we have + // to loop over all the children, + // multiply the existing + // interpolation (prolongation) + // product of matrices from the + // left with the interpolation from + // the present cell to its child + // (using the matrix-matrix + // multiplication function + // mmult), and then hand the + // result off to this very same + // function again, but with the + // cell that has children replaced + // by one of its children: else - for (unsigned int child=0; child::max_children_per_cell;++child) + for (unsigned int child=0; child::max_children_per_cell; ++child) { FullMatrix new_matrix (fe.dofs_per_cell, fe.dofs_per_cell); fe.get_prolongation_matrix(child).mmult (new_matrix, @@ -1329,14 +1329,14 @@ namespace Step28 } - // @sect5{EnergyGroup::get_fission_source} - // - // In the (inverse) power iteration, - // we use the integrated fission - // source to update the - // $k$-eigenvalue. Given its - // definition, the following function - // is essentially self-explanatory: + // @sect5{EnergyGroup::get_fission_source} + // + // In the (inverse) power iteration, + // we use the integrated fission + // source to update the + // $k$-eigenvalue. Given its + // definition, the following function + // is essentially self-explanatory: template double EnergyGroup::get_fission_source () const { @@ -1351,8 +1351,8 @@ namespace Step28 double fission_source = 0; typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { fe_values.reinit (cell); @@ -1372,17 +1372,17 @@ namespace Step28 } - // @sect5{EnergyGroup::solve} - // - // Next a function that solves the - // linear system assembled - // before. Things are pretty much - // standard, except that we delayed - // applying boundary values until we - // get here, since in all the - // previous functions we were still - // adding up contributions the right - // hand side vector. + // @sect5{EnergyGroup::solve} + // + // Next a function that solves the + // linear system assembled + // before. Things are pretty much + // standard, except that we delayed + // applying boundary values until we + // get here, since in all the + // previous functions we were still + // adding up contributions the right + // hand side vector. template void EnergyGroup::solve () @@ -1407,18 +1407,18 @@ namespace Step28 - // @sect5{EnergyGroup::estimate_errors} - // - // Mesh refinement is split into two - // functions. The first estimates the - // error for each cell, normalizes it - // by the magnitude of the solution, - // and returns it in the vector given - // as an argument. The calling - // function collects all error - // indicators from all energy groups, - // and computes thresholds for - // refining and coarsening cells. + // @sect5{EnergyGroup::estimate_errors} + // + // Mesh refinement is split into two + // functions. The first estimates the + // error for each cell, normalizes it + // by the magnitude of the solution, + // and returns it in the vector given + // as an argument. The calling + // function collects all error + // indicators from all energy groups, + // and computes thresholds for + // refining and coarsening cells. template void EnergyGroup::estimate_errors (Vector &error_indicators) const { @@ -1432,35 +1432,35 @@ namespace Step28 - // @sect5{EnergyGroup::refine_grid} - // - // The second part is to refine the - // grid given the error indicators - // compute in the previous function - // and error thresholds above which - // cells shall be refined or below - // which cells shall be - // coarsened. Note that we do not use - // any of the functions in - // GridRefinement here, - // but rather set refinement flags - // ourselves. - // - // After setting these flags, we use - // the SolutionTransfer class to move - // the solution vector from the old - // to the new mesh. The procedure - // used here is described in detail - // in the documentation of that - // class: + // @sect5{EnergyGroup::refine_grid} + // + // The second part is to refine the + // grid given the error indicators + // compute in the previous function + // and error thresholds above which + // cells shall be refined or below + // which cells shall be + // coarsened. Note that we do not use + // any of the functions in + // GridRefinement here, + // but rather set refinement flags + // ourselves. + // + // After setting these flags, we use + // the SolutionTransfer class to move + // the solution vector from the old + // to the new mesh. The procedure + // used here is described in detail + // in the documentation of that + // class: template void EnergyGroup::refine_grid (const Vector &error_indicators, const double refine_threshold, const double coarsen_threshold) { typename Triangulation::active_cell_iterator - cell = triangulation.begin_active(), - endc = triangulation.end(); + cell = triangulation.begin_active(), + endc = triangulation.end(); for (unsigned int cell_index=0; cell!=endc; ++cell, ++cell_index) if (error_indicators(cell_index) > refine_threshold) @@ -1484,23 +1484,23 @@ namespace Step28 } - // @sect5{EnergyGroup::output_results} - // - // The last function of this class - // outputs meshes and solutions after - // each mesh iteration. This has been - // shown many times before. The only - // thing worth pointing out is the - // use of the - // Utilities::int_to_string - // function to convert an integer - // into its string - // representation. The second - // argument of that function denotes - // how many digits we shall use -- if - // this value was larger than one, - // then the number would be padded by - // leading zeros. + // @sect5{EnergyGroup::output_results} + // + // The last function of this class + // outputs meshes and solutions after + // each mesh iteration. This has been + // shown many times before. The only + // thing worth pointing out is the + // use of the + // Utilities::int_to_string + // function to convert an integer + // into its string + // representation. The second + // argument of that function denotes + // how many digits we shall use -- if + // this value was larger than one, + // then the number would be padded by + // leading zeros. template void EnergyGroup::output_results (const unsigned int cycle) const @@ -1537,171 +1537,171 @@ namespace Step28 - // @sect3{The NeutronDiffusionProblem class template} - - // This is the main class of the - // program, not because it implements - // all the functionality (in fact, - // most of it is implemented in the - // EnergyGroup class) - // but because it contains the - // driving algorithm that determines - // what to compute and when. It is - // mostly as shown in many of the - // other tutorial programs in that it - // has a public run - // function and private functions - // doing all the rest. In several - // places, we have to do something - // for all energy groups, in which - // case we will start threads for - // each group to let these things run - // in parallel if deal.II was - // configured for multithreading. - // For strategies of parallelization, - // take a look at the @ref threads module. - // - // The biggest difference to previous - // example programs is that we also - // declare a nested class that has - // member variables for all the - // run-time parameters that can be - // passed to the program in an input - // file. Right now, these are the - // number of energy groups, the - // number of refinement cycles, the - // polynomial degree of the finite - // element to be used, and the - // tolerance used to determine when - // convergence of the inverse power - // iteration has occurred. In - // addition, we have a constructor of - // this class that sets all these - // values to their default values, a - // function - // declare_parameters - // that described to the - // ParameterHandler class already - // used in step-19 - // what parameters are accepted in - // the input file, and a function - // get_parameters that - // can extract the values of these - // parameters from a ParameterHandler - // object. + // @sect3{The NeutronDiffusionProblem class template} + + // This is the main class of the + // program, not because it implements + // all the functionality (in fact, + // most of it is implemented in the + // EnergyGroup class) + // but because it contains the + // driving algorithm that determines + // what to compute and when. It is + // mostly as shown in many of the + // other tutorial programs in that it + // has a public run + // function and private functions + // doing all the rest. In several + // places, we have to do something + // for all energy groups, in which + // case we will start threads for + // each group to let these things run + // in parallel if deal.II was + // configured for multithreading. + // For strategies of parallelization, + // take a look at the @ref threads module. + // + // The biggest difference to previous + // example programs is that we also + // declare a nested class that has + // member variables for all the + // run-time parameters that can be + // passed to the program in an input + // file. Right now, these are the + // number of energy groups, the + // number of refinement cycles, the + // polynomial degree of the finite + // element to be used, and the + // tolerance used to determine when + // convergence of the inverse power + // iteration has occurred. In + // addition, we have a constructor of + // this class that sets all these + // values to their default values, a + // function + // declare_parameters + // that described to the + // ParameterHandler class already + // used in step-19 + // what parameters are accepted in + // the input file, and a function + // get_parameters that + // can extract the values of these + // parameters from a ParameterHandler + // object. template class NeutronDiffusionProblem { + public: + class Parameters + { public: - class Parameters - { - public: - Parameters (); - - static void declare_parameters (ParameterHandler &prm); - void get_parameters (ParameterHandler &prm); - - unsigned int n_groups; - unsigned int n_refinement_cycles; - - unsigned int fe_degree; - - double convergence_tolerance; - }; - - - - NeutronDiffusionProblem (const Parameters ¶meters); - ~NeutronDiffusionProblem (); - - void run (); - - private: - // @sect5{Private member functions} - - // There are not that many member - // functions in this class since - // most of the functionality has - // been moved into the - // EnergyGroup class - // and is simply called from the - // run() member - // function of this class. The - // ones that remain have - // self-explanatory names: - void initialize_problem(); - - void refine_grid (); - - double get_total_fission_source () const; - - - // @sect5{Private member variables} - - // Next, we have a few member - // variables. In particular, - // these are (i) a reference to - // the parameter object (owned by - // the main function of this - // program, and passed to the - // constructor of this class), - // (ii) an object describing the - // material parameters for the - // number of energy groups - // requested in the input file, - // and (iii) the finite element - // to be used by all energy - // groups: - const Parameters ¶meters; - const MaterialData material_data; - FE_Q fe; - - // Furthermore, we have (iv) the - // value of the computed - // eigenvalue at the present - // iteration. This is, in fact, - // the only part of the solution - // that is shared between all - // energy groups -- all other - // parts of the solution, such as - // neutron fluxes are particular - // to one or the other energy - // group, and are therefore - // stored in objects that - // describe a single energy - // group: - double k_eff; - - // Finally, (v), we have an array - // of pointers to the energy - // group objects. The length of - // this array is, of course, - // equal to the number of energy - // groups specified in the - // parameter file. - std::vector*> energy_groups; + Parameters (); + + static void declare_parameters (ParameterHandler &prm); + void get_parameters (ParameterHandler &prm); + + unsigned int n_groups; + unsigned int n_refinement_cycles; + + unsigned int fe_degree; + + double convergence_tolerance; + }; + + + + NeutronDiffusionProblem (const Parameters ¶meters); + ~NeutronDiffusionProblem (); + + void run (); + + private: + // @sect5{Private member functions} + + // There are not that many member + // functions in this class since + // most of the functionality has + // been moved into the + // EnergyGroup class + // and is simply called from the + // run() member + // function of this class. The + // ones that remain have + // self-explanatory names: + void initialize_problem(); + + void refine_grid (); + + double get_total_fission_source () const; + + + // @sect5{Private member variables} + + // Next, we have a few member + // variables. In particular, + // these are (i) a reference to + // the parameter object (owned by + // the main function of this + // program, and passed to the + // constructor of this class), + // (ii) an object describing the + // material parameters for the + // number of energy groups + // requested in the input file, + // and (iii) the finite element + // to be used by all energy + // groups: + const Parameters ¶meters; + const MaterialData material_data; + FE_Q fe; + + // Furthermore, we have (iv) the + // value of the computed + // eigenvalue at the present + // iteration. This is, in fact, + // the only part of the solution + // that is shared between all + // energy groups -- all other + // parts of the solution, such as + // neutron fluxes are particular + // to one or the other energy + // group, and are therefore + // stored in objects that + // describe a single energy + // group: + double k_eff; + + // Finally, (v), we have an array + // of pointers to the energy + // group objects. The length of + // this array is, of course, + // equal to the number of energy + // groups specified in the + // parameter file. + std::vector*> energy_groups; }; - // @sect4{Implementation of the NeutronDiffusionProblem::Parameters class} + // @sect4{Implementation of the NeutronDiffusionProblem::Parameters class} - // Before going on to the - // implementation of the outer class, - // we have to implement the functions - // of the parameters structure. This - // is pretty straightforward and, in - // fact, looks pretty much the same - // for all such parameters classes - // using the ParameterHandler - // capabilities. We will therefore - // not comment further on this: + // Before going on to the + // implementation of the outer class, + // we have to implement the functions + // of the parameters structure. This + // is pretty straightforward and, in + // fact, looks pretty much the same + // for all such parameters classes + // using the ParameterHandler + // capabilities. We will therefore + // not comment further on this: template NeutronDiffusionProblem::Parameters::Parameters () - : - n_groups (2), - n_refinement_cycles (5), - fe_degree (2), - convergence_tolerance (1e-12) + : + n_groups (2), + n_refinement_cycles (5), + fe_degree (2), + convergence_tolerance (1e-12) {} @@ -1742,20 +1742,20 @@ namespace Step28 - // @sect4{Implementation of the NeutronDiffusionProblem class} + // @sect4{Implementation of the NeutronDiffusionProblem class} - // Now for the - // NeutronDiffusionProblem - // class. The constructor and - // destructor have nothing of much - // interest: + // Now for the + // NeutronDiffusionProblem + // class. The constructor and + // destructor have nothing of much + // interest: template NeutronDiffusionProblem:: NeutronDiffusionProblem (const Parameters ¶meters) - : - parameters (parameters), - material_data (parameters.n_groups), - fe (parameters.fe_degree) + : + parameters (parameters), + material_data (parameters.n_groups), + fe (parameters.fe_degree) {} @@ -1769,32 +1769,32 @@ namespace Step28 energy_groups.resize (0); } - // @sect5{NeutronDiffusionProblem::initialize_problem} - // - // The first function of interest is - // the one that sets up the geometry - // of the reactor core. This is - // described in more detail in the - // introduction. - // - // The first part of the function - // defines geometry data, and then - // creates a coarse mesh that has as - // many cells as there are fuel rods - // (or pin cells, for that matter) in - // that part of the reactor core that - // we simulate. As mentioned when - // interpolating boundary values - // above, the last parameter to the - // GridGenerator::subdivided_hyper_rectangle - // function specifies that sides of - // the domain shall have unique - // boundary indicators that will - // later allow us to determine in a - // simple way which of the boundaries - // have Neumann and which have - // Dirichlet conditions attached to - // them. + // @sect5{NeutronDiffusionProblem::initialize_problem} + // + // The first function of interest is + // the one that sets up the geometry + // of the reactor core. This is + // described in more detail in the + // introduction. + // + // The first part of the function + // defines geometry data, and then + // creates a coarse mesh that has as + // many cells as there are fuel rods + // (or pin cells, for that matter) in + // that part of the reactor core that + // we simulate. As mentioned when + // interpolating boundary values + // above, the last parameter to the + // GridGenerator::subdivided_hyper_rectangle + // function specifies that sides of + // the domain shall have unique + // boundary indicators that will + // later allow us to determine in a + // simple way which of the boundaries + // have Neumann and which have + // Dirichlet conditions attached to + // them. template void NeutronDiffusionProblem::initialize_problem() { @@ -1833,150 +1833,151 @@ namespace Step28 true); - // The second part of the function - // deals with material numbers of - // pin cells of each type of - // assembly. Here, we define four - // different types of assembly, for - // which we describe the - // arrangement of fuel rods in the - // following tables. - // - // The assemblies described here - // are taken from the benchmark - // mentioned in the introduction - // and are (in this order): - //
    - //
  1. 'UX' Assembly: UO2 fuel assembly - // with 24 guide tubes and a central - // Moveable Fission Chamber - //
  2. 'UA' Assembly: UO2 fuel assembly - // with 24 AIC and a central - // Moveable Fission Chamber - //
  3. 'PX' Assembly: MOX fuel assembly - // with 24 guide tubes and a central - // Moveable Fission Chamber - //
  4. 'R' Assembly: a reflector. - //
- // - // Note that the numbers listed - // here and taken from the - // benchmark description are, in - // good old Fortran fashion, - // one-based. We will later - // subtract one from each number - // when assigning materials to - // individual cells to convert - // things into the C-style - // zero-based indexing. + // The second part of the function + // deals with material numbers of + // pin cells of each type of + // assembly. Here, we define four + // different types of assembly, for + // which we describe the + // arrangement of fuel rods in the + // following tables. + // + // The assemblies described here + // are taken from the benchmark + // mentioned in the introduction + // and are (in this order): + //
    + //
  1. 'UX' Assembly: UO2 fuel assembly + // with 24 guide tubes and a central + // Moveable Fission Chamber + //
  2. 'UA' Assembly: UO2 fuel assembly + // with 24 AIC and a central + // Moveable Fission Chamber + //
  3. 'PX' Assembly: MOX fuel assembly + // with 24 guide tubes and a central + // Moveable Fission Chamber + //
  4. 'R' Assembly: a reflector. + //
+ // + // Note that the numbers listed + // here and taken from the + // benchmark description are, in + // good old Fortran fashion, + // one-based. We will later + // subtract one from each number + // when assigning materials to + // individual cells to convert + // things into the C-style + // zero-based indexing. const unsigned int n_assemblies=4; const unsigned int - assembly_materials[n_assemblies][rods_per_assembly_x][rods_per_assembly_y] - = { - { - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 5, 1, 1, 5, 1, 1, 7, 1, 1, 5, 1, 1, 5, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } - }, - { - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 8, 1, 1, 8, 1, 1, 7, 1, 1, 8, 1, 1, 8, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } - }, - { - { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, - { 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2 }, - { 2, 3, 3, 3, 3, 5, 3, 3, 5, 3, 3, 5, 3, 3, 3, 3, 2 }, - { 2, 3, 3, 5, 3, 4, 4, 4, 4, 4, 4, 4, 3, 5, 3, 3, 2 }, - { 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 2 }, - { 2, 3, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 3, 2 }, - { 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2 }, - { 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2 }, - { 2, 3, 5, 4, 4, 5, 4, 4, 7, 4, 4, 5, 4, 4, 5, 3, 2 }, - { 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2 }, - { 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2 }, - { 2, 3, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 3, 2 }, - { 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 2 }, - { 2, 3, 3, 5, 3, 4, 4, 4, 4, 4, 4, 4, 3, 5, 3, 3, 2 }, - { 2, 3, 3, 3, 3, 5, 3, 3, 5, 3, 3, 5, 3, 3, 3, 3, 2 }, - { 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2 }, - { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 } - }, - { - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 } - } + assembly_materials[n_assemblies][rods_per_assembly_x][rods_per_assembly_y] + = + { + { + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 5, 1, 1, 5, 1, 1, 7, 1, 1, 5, 1, 1, 5, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } + }, + { + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 8, 1, 1, 8, 1, 1, 7, 1, 1, 8, 1, 1, 8, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 8, 1, 1, 8, 1, 1, 8, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } + }, + { + { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, + { 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2 }, + { 2, 3, 3, 3, 3, 5, 3, 3, 5, 3, 3, 5, 3, 3, 3, 3, 2 }, + { 2, 3, 3, 5, 3, 4, 4, 4, 4, 4, 4, 4, 3, 5, 3, 3, 2 }, + { 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 2 }, + { 2, 3, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 3, 2 }, + { 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2 }, + { 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2 }, + { 2, 3, 5, 4, 4, 5, 4, 4, 7, 4, 4, 5, 4, 4, 5, 3, 2 }, + { 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2 }, + { 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2 }, + { 2, 3, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 3, 2 }, + { 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 2 }, + { 2, 3, 3, 5, 3, 4, 4, 4, 4, 4, 4, 4, 3, 5, 3, 3, 2 }, + { 2, 3, 3, 3, 3, 5, 3, 3, 5, 3, 3, 5, 3, 3, 3, 3, 2 }, + { 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2 }, + { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 } + }, + { + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 } + } }; - // After the description of the - // materials that make up an - // assembly, we have to specify the - // arrangement of assemblies within - // the core. We use a symmetric - // pattern that in fact only uses - // the 'UX' and 'PX' assemblies: + // After the description of the + // materials that make up an + // assembly, we have to specify the + // arrangement of assemblies within + // the core. We use a symmetric + // pattern that in fact only uses + // the 'UX' and 'PX' assemblies: const unsigned int core[assemblies_x][assemblies_y][assemblies_z] - = {{{0}, {2}}, {{2}, {0}}}; - - // We are now in a position to - // actually set material IDs for - // each cell. To this end, we loop - // over all cells, look at the - // location of the cell's center, - // and determine which assembly and - // fuel rod this would be in. (We - // add a few checks to see that the - // locations we compute are within - // the bounds of the arrays in - // which we have to look up - // materials.) At the end of the - // loop, we set material - // identifiers accordingly: + = {{{0}, {2}}, {{2}, {0}}}; + + // We are now in a position to + // actually set material IDs for + // each cell. To this end, we loop + // over all cells, look at the + // location of the cell's center, + // and determine which assembly and + // fuel rod this would be in. (We + // add a few checks to see that the + // locations we compute are within + // the bounds of the arrays in + // which we have to look up + // materials.) At the end of the + // loop, we set material + // identifiers accordingly: for (typename Triangulation::active_cell_iterator - cell = coarse_grid.begin_active(); + cell = coarse_grid.begin_active(); cell!=coarse_grid.end(); ++cell) { @@ -2008,13 +2009,13 @@ namespace Step28 cell->set_material_id(assembly_materials[core[ax][ay][az]][cx][cy] - 1); } - // With the coarse mesh so - // initialized, we create the - // appropriate number of energy - // group objects and let them - // initialize their individual - // meshes with the coarse mesh - // generated above: + // With the coarse mesh so + // initialized, we create the + // appropriate number of energy + // group objects and let them + // initialize their individual + // meshes with the coarse mesh + // generated above: energy_groups.resize (parameters.n_groups); for (unsigned int group=0; group (group, material_data, @@ -2022,42 +2023,42 @@ namespace Step28 } - // @sect5{NeutronDiffusionProblem::get_total_fission_source} - // - // In the eigenvalue computation, we - // need to calculate total fission - // neutron source after each power - // iteration. The total power then is - // used to renew k-effective. - // - // Since the total fission source is a sum - // over all the energy groups, and since each - // of these sums can be computed - // independently, we actually do this in - // parallel. One of the problems is that the - // function in the EnergyGroup - // class that computes the fission source - // returns a value. If we now simply spin off - // a new thread, we have to later capture the - // return value of the function run on that - // thread. The way this can be done is to use - // the return value of the - // Threads::new_thread function, which - // returns an object of type - // Threads::Thread@ if the function - // spawned returns a double. We can then later - // ask this object for the returned value - // (when doing so, the - // Threads::Thread::return_value - // function first waits for the thread to - // finish if it hasn't done so already). - // - // The way this function then works - // is to first spawn one thread for - // each energy group we work with, - // then one-by-one collecting the - // returned values of each thread and - // return the sum. + // @sect5{NeutronDiffusionProblem::get_total_fission_source} + // + // In the eigenvalue computation, we + // need to calculate total fission + // neutron source after each power + // iteration. The total power then is + // used to renew k-effective. + // + // Since the total fission source is a sum + // over all the energy groups, and since each + // of these sums can be computed + // independently, we actually do this in + // parallel. One of the problems is that the + // function in the EnergyGroup + // class that computes the fission source + // returns a value. If we now simply spin off + // a new thread, we have to later capture the + // return value of the function run on that + // thread. The way this can be done is to use + // the return value of the + // Threads::new_thread function, which + // returns an object of type + // Threads::Thread@ if the function + // spawned returns a double. We can then later + // ask this object for the returned value + // (when doing so, the + // Threads::Thread::return_value + // function first waits for the thread to + // finish if it hasn't done so already). + // + // The way this function then works + // is to first spawn one thread for + // each energy group we work with, + // then one-by-one collecting the + // returned values of each thread and + // return the sum. template double NeutronDiffusionProblem::get_total_fission_source () const { @@ -2076,23 +2077,23 @@ namespace Step28 - // @sect5{NeutronDiffusionProblem::refine_grid} - // - // The next function lets the - // individual energy group objects - // refine their meshes. Much of this, - // again, is a task that can be done - // independently in parallel: first, - // let all the energy group objects - // calculate their error indicators - // in parallel, then compute the - // maximum error indicator over all - // energy groups and determine - // thresholds for refinement and - // coarsening of cells, and then ask - // all the energy groups to refine - // their meshes accordingly, again in - // parallel. + // @sect5{NeutronDiffusionProblem::refine_grid} + // + // The next function lets the + // individual energy group objects + // refine their meshes. Much of this, + // again, is a task that can be done + // independently in parallel: first, + // let all the energy group objects + // calculate their error indicators + // in parallel, then compute the + // maximum error indicator over all + // energy groups and determine + // thresholds for refinement and + // coarsening of cells, and then ask + // all the energy groups to refine + // their meshes accordingly, again in + // parallel. template void NeutronDiffusionProblem::refine_grid () { @@ -2128,18 +2129,18 @@ namespace Step28 } - // @sect5{NeutronDiffusionProblem::run} - // - // Finally, this is the function - // where the meat is: iterate on a - // sequence of meshes, and on each of - // them do a power iteration to - // compute the eigenvalue. - // - // Given the description of the - // algorithm in the introduction, - // there is actually not much to - // comment on: + // @sect5{NeutronDiffusionProblem::run} + // + // Finally, this is the function + // where the meat is: iterate on a + // sequence of meshes, and on each of + // them do a power iteration to + // compute the eigenvalue. + // + // Given the description of the + // algorithm in the introduction, + // there is actually not much to + // comment on: template void NeutronDiffusionProblem::run () { @@ -2214,9 +2215,9 @@ namespace Step28 ++iteration; } - while((error > parameters.convergence_tolerance) - && - (iteration < 500)); + while ((error > parameters.convergence_tolerance) + && + (iteration < 500)); for (unsigned int group=0; groupoutput_results (cycle); @@ -2236,38 +2237,38 @@ namespace Step28 - // @sect3{The main() function} - // - // The last thing in the program in - // the main() - // function. The structure is as in - // most other tutorial programs, with - // the only exception that we here - // handle a parameter file. To this - // end, we first look at the command - // line arguments passed to this - // function: if no input file is - // specified on the command line, - // then use "project.prm", otherwise - // take the filename given as the - // first argument on the command - // line. - // - // With this, we create a - // ParameterHandler object, let the - // NeutronDiffusionProblem::Parameters - // class declare all the parameters - // it wants to see in the input file - // (or, take the default values, if - // nothing is listed in the parameter - // file), then read the input file, - // ask the parameters object to - // extract the values, and finally - // hand everything off to an object - // of type - // NeutronDiffusionProblem - // for computation of the eigenvalue: -int main (int argc, char ** argv) +// @sect3{The main() function} +// +// The last thing in the program in +// the main() +// function. The structure is as in +// most other tutorial programs, with +// the only exception that we here +// handle a parameter file. To this +// end, we first look at the command +// line arguments passed to this +// function: if no input file is +// specified on the command line, +// then use "project.prm", otherwise +// take the filename given as the +// first argument on the command +// line. +// +// With this, we create a +// ParameterHandler object, let the +// NeutronDiffusionProblem::Parameters +// class declare all the parameters +// it wants to see in the input file +// (or, take the default values, if +// nothing is listed in the parameter +// file), then read the input file, +// ask the parameters object to +// extract the values, and finally +// hand everything off to an object +// of type +// NeutronDiffusionProblem +// for computation of the eigenvalue: +int main (int argc, char **argv) { try { diff --git a/deal.II/examples/step-29/step-29.cc b/deal.II/examples/step-29/step-29.cc index 183e15034a..4e4bff6dd9 100644 --- a/deal.II/examples/step-29/step-29.cc +++ b/deal.II/examples/step-29/step-29.cc @@ -10,10 +10,10 @@ - // @sect3{Include files} +// @sect3{Include files} - // The following header files are unchanged - // from step-7 and have been discussed before: +// The following header files are unchanged +// from step-7 and have been discussed before: #include #include @@ -42,90 +42,90 @@ #include - // This header file contains the - // necessary declarations for the - // ParameterHandler class that we - // will use to read our parameters - // from a configuration file: +// This header file contains the +// necessary declarations for the +// ParameterHandler class that we +// will use to read our parameters +// from a configuration file: #include - // For solving the linear system, - // we'll use the sparse - // LU-decomposition provided by - // UMFPACK (see the SparseDirectUMFPACK - // class), for which the following - // header file is needed. Note that - // in order to compile this tutorial - // program, the deal.II-library needs - // to be built with UMFPACK support, - // which can be most easily achieved - // by giving the - // --with-umfpack switch when - // configuring the library: +// For solving the linear system, +// we'll use the sparse +// LU-decomposition provided by +// UMFPACK (see the SparseDirectUMFPACK +// class), for which the following +// header file is needed. Note that +// in order to compile this tutorial +// program, the deal.II-library needs +// to be built with UMFPACK support, +// which can be most easily achieved +// by giving the +// --with-umfpack switch when +// configuring the library: #include - // The FESystem class allows us to - // stack several FE-objects to one - // compound, vector-valued finite - // element field. The necessary - // declarations for this class are - // provided in this header file: +// The FESystem class allows us to +// stack several FE-objects to one +// compound, vector-valued finite +// element field. The necessary +// declarations for this class are +// provided in this header file: #include - // Finally, include the header file - // that declares the Timer class that - // we will use to determine how much - // time each of the operations of our - // program takes: +// Finally, include the header file +// that declares the Timer class that +// we will use to determine how much +// time each of the operations of our +// program takes: #include - // As the last step at the beginning of this - // program, we put everything that is in this - // program into its namespace and, within it, - // make everything that is in the deal.II - // namespace globally available, without the - // need to prefix everything with - // dealii::: +// As the last step at the beginning of this +// program, we put everything that is in this +// program into its namespace and, within it, +// make everything that is in the deal.II +// namespace globally available, without the +// need to prefix everything with +// dealii::: namespace Step29 { using namespace dealii; - // @sect3{The DirichletBoundaryValues class} - - // First we define a class for the - // function representing the - // Dirichlet boundary values. This - // has been done many times before - // and therefore does not need much - // explanation. - // - // Since there are two values $v$ and - // $w$ that need to be prescribed at - // the boundary, we have to tell the - // base class that this is a - // vector-valued function with two - // components, and the - // vector_value function - // and its cousin - // vector_value_list must - // return vectors with two entries. In - // our case the function is very - // simple, it just returns 1 for the - // real part $v$ and 0 for the - // imaginary part $w$ regardless of - // the point where it is evaluated. + // @sect3{The DirichletBoundaryValues class} + + // First we define a class for the + // function representing the + // Dirichlet boundary values. This + // has been done many times before + // and therefore does not need much + // explanation. + // + // Since there are two values $v$ and + // $w$ that need to be prescribed at + // the boundary, we have to tell the + // base class that this is a + // vector-valued function with two + // components, and the + // vector_value function + // and its cousin + // vector_value_list must + // return vectors with two entries. In + // our case the function is very + // simple, it just returns 1 for the + // real part $v$ and 0 for the + // imaginary part $w$ regardless of + // the point where it is evaluated. template class DirichletBoundaryValues : public Function { - public: - DirichletBoundaryValues() : Function (2) {}; + public: + DirichletBoundaryValues() : Function (2) {}; - virtual void vector_value (const Point &p, - Vector &values) const; + virtual void vector_value (const Point &p, + Vector &values) const; - virtual void vector_value_list (const std::vector > &points, - std::vector > &value_list) const; + virtual void vector_value_list (const std::vector > &points, + std::vector > &value_list) const; }; @@ -152,68 +152,68 @@ namespace Step29 DirichletBoundaryValues::vector_value (points[p], value_list[p]); } - // @sect3{The ParameterReader class} - - // The next class is responsible for - // preparing the ParameterHandler - // object and reading parameters from - // an input file. It includes a - // function - // declare_parameters - // that declares all the necessary - // parameters and a - // read_parameters - // function that is called from - // outside to initiate the parameter - // reading process. + // @sect3{The ParameterReader class} + + // The next class is responsible for + // preparing the ParameterHandler + // object and reading parameters from + // an input file. It includes a + // function + // declare_parameters + // that declares all the necessary + // parameters and a + // read_parameters + // function that is called from + // outside to initiate the parameter + // reading process. class ParameterReader : public Subscriptor { - public: - ParameterReader(ParameterHandler &); - void read_parameters(const std::string); + public: + ParameterReader(ParameterHandler &); + void read_parameters(const std::string); - private: - void declare_parameters(); - ParameterHandler &prm; + private: + void declare_parameters(); + ParameterHandler &prm; }; - // The constructor stores a reference to - // the ParameterHandler object that is passed to it: + // The constructor stores a reference to + // the ParameterHandler object that is passed to it: ParameterReader::ParameterReader(ParameterHandler ¶mhandler) - : - prm(paramhandler) + : + prm(paramhandler) {} - // @sect4{ParameterReader::declare_parameters} - - // The declare_parameters - // function declares all the - // parameters that our - // ParameterHandler object will be - // able to read from input files, - // along with their types, range - // conditions and the subsections they - // appear in. We will wrap all the - // entries that go into a section in a - // pair of braces to force the editor - // to indent them by one level, making - // it simpler to read which entries - // together form a section: + // @sect4{ParameterReader::declare_parameters} + + // The declare_parameters + // function declares all the + // parameters that our + // ParameterHandler object will be + // able to read from input files, + // along with their types, range + // conditions and the subsections they + // appear in. We will wrap all the + // entries that go into a section in a + // pair of braces to force the editor + // to indent them by one level, making + // it simpler to read which entries + // together form a section: void ParameterReader::declare_parameters() { - // Parameters for mesh and geometry - // include the number of global - // refinement steps that are applied - // to the initial coarse mesh and the - // focal distance $d$ of the - // transducer lens. For the number of - // refinement steps, we allow integer - // values in the range $[0,\infty)$, - // where the omitted second argument - // to the Patterns::Integer object - // denotes the half-open interval. - // For the focal distance any number - // greater than zero is accepted: + // Parameters for mesh and geometry + // include the number of global + // refinement steps that are applied + // to the initial coarse mesh and the + // focal distance $d$ of the + // transducer lens. For the number of + // refinement steps, we allow integer + // values in the range $[0,\infty)$, + // where the omitted second argument + // to the Patterns::Integer object + // denotes the half-open interval. + // For the focal distance any number + // greater than zero is accepted: prm.enter_subsection ("Mesh & geometry parameters"); { prm.declare_entry("Number of refinements", "6", @@ -228,15 +228,15 @@ namespace Step29 } prm.leave_subsection (); - // The next subsection is devoted to - // the physical parameters appearing - // in the equation, which are the - // frequency $\omega$ and wave speed - // $c$. Again, both need to lie in the - // half-open interval $[0,\infty)$ - // represented by calling the - // Patterns::Double class with only - // the left end-point as argument: + // The next subsection is devoted to + // the physical parameters appearing + // in the equation, which are the + // frequency $\omega$ and wave speed + // $c$. Again, both need to lie in the + // half-open interval $[0,\infty)$ + // represented by calling the + // Patterns::Double class with only + // the left end-point as argument: prm.enter_subsection ("Physical constants"); { prm.declare_entry("c", "1.5e5", @@ -250,88 +250,88 @@ namespace Step29 prm.leave_subsection (); - // Last but not least we would like - // to be able to change some - // properties of the output, like - // filename and format, through - // entries in the configuration - // file, which is the purpose of - // the last subsection: + // Last but not least we would like + // to be able to change some + // properties of the output, like + // filename and format, through + // entries in the configuration + // file, which is the purpose of + // the last subsection: prm.enter_subsection ("Output parameters"); { prm.declare_entry("Output file", "solution", Patterns::Anything(), "Name of the output file (without extension)"); - // Since different output formats - // may require different - // parameters for generating - // output (like for example, - // postscript output needs - // viewpoint angles, line widths, - // colors etc), it would be - // cumbersome if we had to - // declare all these parameters - // by hand for every possible - // output format supported in the - // library. Instead, each output - // format has a - // FormatFlags::declare_parameters - // function, which declares all - // the parameters specific to - // that format in an own - // subsection. The following call - // of - // DataOutInterface<1>::declare_parameters - // executes - // declare_parameters - // for all available output - // formats, so that for each - // format an own subsection will - // be created with parameters - // declared for that particular - // output format. (The actual - // value of the template - // parameter in the call, - // @<1@> above, does - // not matter here: the function - // does the same work independent - // of the dimension, but happens - // to be in a - // template-parameter-dependent - // class.) To find out what - // parameters there are for which - // output format, you can either - // consult the documentation of - // the DataOutBase class, or - // simply run this program - // without a parameter file - // present. It will then create a - // file with all declared - // parameters set to their - // default values, which can - // conveniently serve as a - // starting point for setting the - // parameters to the values you - // desire. + // Since different output formats + // may require different + // parameters for generating + // output (like for example, + // postscript output needs + // viewpoint angles, line widths, + // colors etc), it would be + // cumbersome if we had to + // declare all these parameters + // by hand for every possible + // output format supported in the + // library. Instead, each output + // format has a + // FormatFlags::declare_parameters + // function, which declares all + // the parameters specific to + // that format in an own + // subsection. The following call + // of + // DataOutInterface<1>::declare_parameters + // executes + // declare_parameters + // for all available output + // formats, so that for each + // format an own subsection will + // be created with parameters + // declared for that particular + // output format. (The actual + // value of the template + // parameter in the call, + // @<1@> above, does + // not matter here: the function + // does the same work independent + // of the dimension, but happens + // to be in a + // template-parameter-dependent + // class.) To find out what + // parameters there are for which + // output format, you can either + // consult the documentation of + // the DataOutBase class, or + // simply run this program + // without a parameter file + // present. It will then create a + // file with all declared + // parameters set to their + // default values, which can + // conveniently serve as a + // starting point for setting the + // parameters to the values you + // desire. DataOutInterface<1>::declare_parameters (prm); } prm.leave_subsection (); } - // @sect4{ParameterReader::read_parameters} - - // This is the main function in the - // ParameterReader class. It gets - // called from outside, first - // declares all the parameters, and - // then reads them from the input - // file whose filename is provided by - // the caller. After the call to this - // function is complete, the - // prm object can be - // used to retrieve the values of the - // parameters read in from the file: + // @sect4{ParameterReader::read_parameters} + + // This is the main function in the + // ParameterReader class. It gets + // called from outside, first + // declares all the parameters, and + // then reads them from the input + // file whose filename is provided by + // the caller. After the call to this + // function is complete, the + // prm object can be + // used to retrieve the values of the + // parameters read in from the file: void ParameterReader::read_parameters (const std::string parameter_file) { declare_parameters(); @@ -341,181 +341,181 @@ namespace Step29 - // @sect3{The ComputeIntensity class} - - // As mentioned in the introduction, - // the quantity that we are really - // after is the spatial distribution - // of the intensity of the ultrasound - // wave, which corresponds to - // $|u|=\sqrt{v^2+w^2}$. Now we could - // just be content with having $v$ - // and $w$ in our output, and use a - // suitable visualization or - // postprocessing tool to derive - // $|u|$ from the solution we - // computed. However, there is also a - // way to output data derived from - // the solution in deal.II, and we - // are going to make use of this - // mechanism here. - - // So far we have always used the - // DataOut::add_data_vector function - // to add vectors containing output - // data to a DataOut object. There - // is a special version of this - // function that in addition to the - // data vector has an additional - // argument of type - // DataPostprocessor. What happens - // when this function is used for - // output is that at each point where - // output data is to be generated, - // the DataPostprocessor::compute_derived_quantities_scalar or DataPostprocessor::compute_derived_quantities_vector - // function of the specified - // DataPostprocessor object is - // invoked to compute the output - // quantities from the values, the - // gradients and the second - // derivatives of the finite element - // function represented by the data - // vector (in the case of face - // related data, normal vectors are - // available as well). Hence, this - // allows us to output any quantity - // that can locally be derived from - // the values of the solution and its - // derivatives. Of course, the - // ultrasound intensity $|u|$ is such - // a quantity and its computation - // doesn't even involve any - // derivatives of $v$ or $w$. - - // In practice, the - // DataPostprocessor class only - // provides an interface to this - // functionality, and we need to - // derive our own class from it in - // order to implement the functions - // specified by the interface. In - // the most general case one has to - // implement several member - // functions but if the output - // quantity is a single scalar then - // some of this boilerplate code - // can be handled by a more - // specialized class, - // DataPostprocessorScalar and we - // can derive from that one - // instead. This is what the - // ComputeIntensity - // class does: + // @sect3{The ComputeIntensity class} + + // As mentioned in the introduction, + // the quantity that we are really + // after is the spatial distribution + // of the intensity of the ultrasound + // wave, which corresponds to + // $|u|=\sqrt{v^2+w^2}$. Now we could + // just be content with having $v$ + // and $w$ in our output, and use a + // suitable visualization or + // postprocessing tool to derive + // $|u|$ from the solution we + // computed. However, there is also a + // way to output data derived from + // the solution in deal.II, and we + // are going to make use of this + // mechanism here. + + // So far we have always used the + // DataOut::add_data_vector function + // to add vectors containing output + // data to a DataOut object. There + // is a special version of this + // function that in addition to the + // data vector has an additional + // argument of type + // DataPostprocessor. What happens + // when this function is used for + // output is that at each point where + // output data is to be generated, + // the DataPostprocessor::compute_derived_quantities_scalar or DataPostprocessor::compute_derived_quantities_vector + // function of the specified + // DataPostprocessor object is + // invoked to compute the output + // quantities from the values, the + // gradients and the second + // derivatives of the finite element + // function represented by the data + // vector (in the case of face + // related data, normal vectors are + // available as well). Hence, this + // allows us to output any quantity + // that can locally be derived from + // the values of the solution and its + // derivatives. Of course, the + // ultrasound intensity $|u|$ is such + // a quantity and its computation + // doesn't even involve any + // derivatives of $v$ or $w$. + + // In practice, the + // DataPostprocessor class only + // provides an interface to this + // functionality, and we need to + // derive our own class from it in + // order to implement the functions + // specified by the interface. In + // the most general case one has to + // implement several member + // functions but if the output + // quantity is a single scalar then + // some of this boilerplate code + // can be handled by a more + // specialized class, + // DataPostprocessorScalar and we + // can derive from that one + // instead. This is what the + // ComputeIntensity + // class does: template class ComputeIntensity : public DataPostprocessorScalar { - public: - ComputeIntensity (); - - virtual - void - compute_derived_quantities_vector (const std::vector< Vector< double > > &uh, - const std::vector< std::vector< Tensor< 1, dim > > > &duh, - const std::vector< std::vector< Tensor< 2, dim > > > &dduh, - const std::vector< Point< dim > > &normals, - const std::vector > &evaluation_points, - std::vector< Vector< double > > &computed_quantities) const; + public: + ComputeIntensity (); + + virtual + void + compute_derived_quantities_vector (const std::vector< Vector< double > > &uh, + const std::vector< std::vector< Tensor< 1, dim > > > &duh, + const std::vector< std::vector< Tensor< 2, dim > > > &dduh, + const std::vector< Point< dim > > &normals, + const std::vector > &evaluation_points, + std::vector< Vector< double > > &computed_quantities) const; }; - // In the constructor, we need to - // call the constructor of the base - // class with two arguments. The - // first denotes the name by which - // the single scalar quantity - // computed by this class should be - // represented in output files. In - // our case, the postprocessor has - // $|u|$ as output, so we use - // "Intensity". - // - // The second argument is a set of - // flags that indicate which data is - // needed by the postprocessor in - // order to compute the output - // quantities. This can be any - // subset of update_values, - // update_gradients and - // update_hessians (and, in the case - // of face data, also - // update_normal_vectors), which are - // documented in UpdateFlags. Of - // course, computation of the - // derivatives requires additional - // resources, so only the flags for - // data that is really needed should - // be given here, just as we do when - // we use FEValues objects. In our - // case, only the function values of - // $v$ and $w$ are needed to compute - // $|u|$, so we're good with the - // update_values flag. + // In the constructor, we need to + // call the constructor of the base + // class with two arguments. The + // first denotes the name by which + // the single scalar quantity + // computed by this class should be + // represented in output files. In + // our case, the postprocessor has + // $|u|$ as output, so we use + // "Intensity". + // + // The second argument is a set of + // flags that indicate which data is + // needed by the postprocessor in + // order to compute the output + // quantities. This can be any + // subset of update_values, + // update_gradients and + // update_hessians (and, in the case + // of face data, also + // update_normal_vectors), which are + // documented in UpdateFlags. Of + // course, computation of the + // derivatives requires additional + // resources, so only the flags for + // data that is really needed should + // be given here, just as we do when + // we use FEValues objects. In our + // case, only the function values of + // $v$ and $w$ are needed to compute + // $|u|$, so we're good with the + // update_values flag. template ComputeIntensity::ComputeIntensity () - : - DataPostprocessorScalar ("Intensity", - update_values) + : + DataPostprocessorScalar ("Intensity", + update_values) {} - // The actual prostprocessing happens - // in the following function. Its - // inputs are a vector representing - // values of the function (which is - // here vector-valued) representing - // the data vector given to - // DataOut::add_data_vector, - // evaluated at all evaluation points - // where we generate output, and some - // tensor objects representing - // derivatives (that we don't use - // here since $|u|$ is computed from - // just $v$ and $w$, and for which we - // assign no name to the - // corresponding function argument). - // The derived quantities are - // returned in the - // computed_quantities - // vector. Remember that this - // function may only use data for - // which the respective update flag - // is specified by - // get_needed_update_flags. For - // example, we may not use the - // derivatives here, since our - // implementation of - // get_needed_update_flags - // requests that only function values - // are provided. + // The actual prostprocessing happens + // in the following function. Its + // inputs are a vector representing + // values of the function (which is + // here vector-valued) representing + // the data vector given to + // DataOut::add_data_vector, + // evaluated at all evaluation points + // where we generate output, and some + // tensor objects representing + // derivatives (that we don't use + // here since $|u|$ is computed from + // just $v$ and $w$, and for which we + // assign no name to the + // corresponding function argument). + // The derived quantities are + // returned in the + // computed_quantities + // vector. Remember that this + // function may only use data for + // which the respective update flag + // is specified by + // get_needed_update_flags. For + // example, we may not use the + // derivatives here, since our + // implementation of + // get_needed_update_flags + // requests that only function values + // are provided. template void ComputeIntensity::compute_derived_quantities_vector ( - const std::vector< Vector< double > > & uh, + const std::vector< Vector< double > > &uh, const std::vector< std::vector< Tensor< 1, dim > > > & /*duh*/, const std::vector< std::vector< Tensor< 2, dim > > > & /*dduh*/, const std::vector< Point< dim > > & /*normals*/, const std::vector > & /*evaluation_points*/, - std::vector< Vector< double > > & computed_quantities + std::vector< Vector< double > > &computed_quantities ) const { Assert(computed_quantities.size() == uh.size(), ExcDimensionMismatch (computed_quantities.size(), uh.size())); - // The computation itself is - // straightforward: We iterate over - // each entry in the output vector - // and compute $|u|$ from the - // corresponding values of $v$ and - // $w$: + // The computation itself is + // straightforward: We iterate over + // each entry in the output vector + // and compute $|u|$ from the + // corresponding values of $v$ and + // $w$: for (unsigned int i=0; iUltrasoundProblem class} - - // Finally here is the main class of - // this program. It's member - // functions are very similar to the - // previous examples, in particular - // step-4, and the list of member - // variables does not contain any - // major surprises either. The - // ParameterHandler object that is - // passed to the constructor is - // stored as a reference to allow - // easy access to the parameters from - // all functions of the class. Since - // we are working with vector valued - // finite elements, the FE object we - // are using is of type FESystem. + // @sect3{The UltrasoundProblem class} + + // Finally here is the main class of + // this program. It's member + // functions are very similar to the + // previous examples, in particular + // step-4, and the list of member + // variables does not contain any + // major surprises either. The + // ParameterHandler object that is + // passed to the constructor is + // stored as a reference to allow + // easy access to the parameters from + // all functions of the class. Since + // we are working with vector valued + // finite elements, the FE object we + // are using is of type FESystem. template class UltrasoundProblem { - public: - UltrasoundProblem (ParameterHandler &); - ~UltrasoundProblem (); - void run (); - - private: - void make_grid (); - void setup_system (); - void assemble_system (); - void solve (); - void output_results () const; - - ParameterHandler &prm; - - Triangulation triangulation; - DoFHandler dof_handler; - FESystem fe; - - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - Vector solution, system_rhs; + public: + UltrasoundProblem (ParameterHandler &); + ~UltrasoundProblem (); + void run (); + + private: + void make_grid (); + void setup_system (); + void assemble_system (); + void solve (); + void output_results () const; + + ParameterHandler &prm; + + Triangulation triangulation; + DoFHandler dof_handler; + FESystem fe; + + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + Vector solution, system_rhs; }; - // The constructor takes the - // ParameterHandler object and stores - // it in a reference. It also - // initializes the DoF-Handler and - // the finite element system, which - // consists of two copies of the - // scalar Q1 field, one for $v$ and - // one for $w$: + // The constructor takes the + // ParameterHandler object and stores + // it in a reference. It also + // initializes the DoF-Handler and + // the finite element system, which + // consists of two copies of the + // scalar Q1 field, one for $v$ and + // one for $w$: template - UltrasoundProblem::UltrasoundProblem (ParameterHandler& param) - : - prm(param), - dof_handler(triangulation), - fe(FE_Q(1), 2) + UltrasoundProblem::UltrasoundProblem (ParameterHandler ¶m) + : + prm(param), + dof_handler(triangulation), + fe(FE_Q(1), 2) {} @@ -595,31 +595,31 @@ namespace Step29 dof_handler.clear(); } - // @sect4{UltrasoundProblem::make_grid} + // @sect4{UltrasoundProblem::make_grid} - // Here we setup the grid for our - // domain. As mentioned in the - // exposition, the geometry is just a - // unit square (in 2d) with the part - // of the boundary that represents - // the transducer lens replaced by a - // sector of a circle. + // Here we setup the grid for our + // domain. As mentioned in the + // exposition, the geometry is just a + // unit square (in 2d) with the part + // of the boundary that represents + // the transducer lens replaced by a + // sector of a circle. template void UltrasoundProblem::make_grid () { - // First we generate some logging - // output and start a timer so we - // can compute execution time when - // this function is done: + // First we generate some logging + // output and start a timer so we + // can compute execution time when + // this function is done: deallog << "Generating grid... "; Timer timer; timer.start (); - // Then we query the values for the - // focal distance of the transducer - // lens and the number of mesh - // refinement steps from our - // ParameterHandler object: + // Then we query the values for the + // focal distance of the transducer + // lens and the number of mesh + // refinement steps from our + // ParameterHandler object: prm.enter_subsection ("Mesh & geometry parameters"); const double focal_distance = prm.get_double("Focal distance"); @@ -627,60 +627,60 @@ namespace Step29 prm.leave_subsection (); - // Next, two points are defined for - // position and focal point of the - // transducer lens, which is the - // center of the circle whose - // segment will form the transducer - // part of the boundary. We compute - // the radius of this circle in - // such a way that the segment fits - // in the interval [0.4,0.6] on the - // x-axis. Notice that this is the - // only point in the program where - // things are slightly different in - // 2D and 3D. Even though this - // tutorial only deals with the 2D - // case, the necessary additions to - // make this program functional in - // 3D are so minimal that we opt - // for including them: + // Next, two points are defined for + // position and focal point of the + // transducer lens, which is the + // center of the circle whose + // segment will form the transducer + // part of the boundary. We compute + // the radius of this circle in + // such a way that the segment fits + // in the interval [0.4,0.6] on the + // x-axis. Notice that this is the + // only point in the program where + // things are slightly different in + // 2D and 3D. Even though this + // tutorial only deals with the 2D + // case, the necessary additions to + // make this program functional in + // 3D are so minimal that we opt + // for including them: const Point transducer = (dim == 2) ? Point (0.5, 0.0) : Point (0.5, 0.5, 0.0), - focal_point = (dim == 2) ? - Point (0.5, focal_distance) : - Point (0.5, 0.5, focal_distance); + focal_point = (dim == 2) ? + Point (0.5, focal_distance) : + Point (0.5, 0.5, focal_distance); const double radius = std::sqrt( (focal_point.distance(transducer) * focal_point.distance(transducer)) + ((dim==2) ? 0.01 : 0.02)); - // As initial coarse grid we take a - // simple unit square with 5 - // subdivisions in each - // direction. The number of - // subdivisions is chosen so that - // the line segment $[0.4,0.6]$ - // that we want to designate as the - // transducer boundary is spanned - // by a single face. Then we step - // through all cells to find the - // faces where the transducer is to - // be located, which in fact is - // just the single edge from 0.4 to - // 0.6 on the x-axis. This is where - // we want the refinements to be - // made according to a circle - // shaped boundary, so we mark this - // edge with a different boundary - // indicator. + // As initial coarse grid we take a + // simple unit square with 5 + // subdivisions in each + // direction. The number of + // subdivisions is chosen so that + // the line segment $[0.4,0.6]$ + // that we want to designate as the + // transducer boundary is spanned + // by a single face. Then we step + // through all cells to find the + // faces where the transducer is to + // be located, which in fact is + // just the single edge from 0.4 to + // 0.6 on the x-axis. This is where + // we want the refinements to be + // made according to a circle + // shaped boundary, so we mark this + // edge with a different boundary + // indicator. GridGenerator::subdivided_hyper_cube (triangulation, 5, 0, 1); typename Triangulation::cell_iterator - cell = triangulation.begin (), - endc = triangulation.end(); + cell = triangulation.begin (), + endc = triangulation.end(); for (; cell!=endc; ++cell) for (unsigned int face=0; face::faces_per_cell; ++face) @@ -689,36 +689,36 @@ namespace Step29 cell->face(face)->set_boundary_indicator (1); - // For the circle part of the - // transducer lens, a hyper-ball - // object is used (which, of course, - // in 2D just represents a circle), - // with radius and center as computed - // above. By marking this object as - // static, we ensure that - // it lives until the end of the - // program and thereby longer than the - // triangulation object we will - // associated with it. We then assign - // this boundary-object to the part of - // the boundary with boundary - // indicator 1: + // For the circle part of the + // transducer lens, a hyper-ball + // object is used (which, of course, + // in 2D just represents a circle), + // with radius and center as computed + // above. By marking this object as + // static, we ensure that + // it lives until the end of the + // program and thereby longer than the + // triangulation object we will + // associated with it. We then assign + // this boundary-object to the part of + // the boundary with boundary + // indicator 1: static const HyperBallBoundary boundary(focal_point, radius); triangulation.set_boundary(1, boundary); - // Now global refinement is - // executed. Cells near the - // transducer location will be - // automatically refined according - // to the circle shaped boundary of - // the transducer lens: + // Now global refinement is + // executed. Cells near the + // transducer location will be + // automatically refined according + // to the circle shaped boundary of + // the transducer lens: triangulation.refine_global (n_refinements); - // Lastly, we generate some more - // logging output. We stop the - // timer and query the number of - // CPU seconds elapsed since the - // beginning of the function: + // Lastly, we generate some more + // logging output. We stop the + // timer and query the number of + // CPU seconds elapsed since the + // beginning of the function: timer.stop (); deallog << "done (" << timer() @@ -731,16 +731,16 @@ namespace Step29 } - // @sect4{UltrasoundProblem::setup_system} - // - // Initialization of the system - // matrix, sparsity patterns and - // vectors are the same as in - // previous examples and therefore do - // not need further comment. As in - // the previous function, we also - // output the run time of what we do - // here: + // @sect4{UltrasoundProblem::setup_system} + // + // Initialization of the system + // matrix, sparsity patterns and + // vectors are the same as in + // previous examples and therefore do + // not need further comment. As in + // the previous function, we also + // output the run time of what we do + // here: template void UltrasoundProblem::setup_system () { @@ -773,10 +773,10 @@ namespace Step29 } - // @sect4{UltrasoundProblem::assemble_system} - // As before, this function takes - // care of assembling the system - // matrix and right hand side vector: + // @sect4{UltrasoundProblem::assemble_system} + // As before, this function takes + // care of assembling the system + // matrix and right hand side vector: template void UltrasoundProblem::assemble_system () { @@ -784,12 +784,12 @@ namespace Step29 Timer timer; timer.start (); - // First we query wavespeed and - // frequency from the - // ParameterHandler object and - // store them in local variables, - // as they will be used frequently - // throughout this function. + // First we query wavespeed and + // frequency from the + // ParameterHandler object and + // store them in local variables, + // as they will be used frequently + // throughout this function. prm.enter_subsection ("Physical constants"); @@ -798,15 +798,15 @@ namespace Step29 prm.leave_subsection (); - // As usual, for computing - // integrals ordinary Gauss - // quadrature rule is used. Since - // our bilinear form involves - // boundary integrals on - // $\Gamma_2$, we also need a - // quadrature rule for surface - // integration on the faces, which - // are $dim-1$ dimensional: + // As usual, for computing + // integrals ordinary Gauss + // quadrature rule is used. Since + // our bilinear form involves + // boundary integrals on + // $\Gamma_2$, we also need a + // quadrature rule for surface + // integration on the faces, which + // are $dim-1$ dimensional: QGauss quadrature_formula(2); QGauss face_quadrature_formula(2); @@ -814,19 +814,19 @@ namespace Step29 n_face_q_points = face_quadrature_formula.size(), dofs_per_cell = fe.dofs_per_cell; - // The FEValues objects will - // evaluate the shape functions for - // us. For the part of the - // bilinear form that involves - // integration on $\Omega$, we'll - // need the values and gradients of - // the shape functions, and of - // course the quadrature weights. - // For the terms involving the - // boundary integrals, only shape - // function values and the - // quadrature weights are - // necessary. + // The FEValues objects will + // evaluate the shape functions for + // us. For the part of the + // bilinear form that involves + // integration on $\Omega$, we'll + // need the values and gradients of + // the shape functions, and of + // course the quadrature weights. + // For the terms involving the + // boundary integrals, only shape + // function values and the + // quadrature weights are + // necessary. FEValues fe_values (fe, quadrature_formula, update_values | update_gradients | update_JxW_values); @@ -834,31 +834,31 @@ namespace Step29 FEFaceValues fe_face_values (fe, face_quadrature_formula, update_values | update_JxW_values); - // As usual, the system matrix is - // assembled cell by cell, and we - // need a matrix for storing the - // local cell contributions as well - // as an index vector to transfer - // the cell contributions to the - // appropriate location in the - // global system matrix after. + // As usual, the system matrix is + // assembled cell by cell, and we + // need a matrix for storing the + // local cell contributions as well + // as an index vector to transfer + // the cell contributions to the + // appropriate location in the + // global system matrix after. FullMatrix cell_matrix (dofs_per_cell, dofs_per_cell); std::vector local_dof_indices (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { - // On each cell, we first need - // to reset the local - // contribution matrix and - // request the FEValues object - // to compute the shape - // functions for the current - // cell: + // On each cell, we first need + // to reset the local + // contribution matrix and + // request the FEValues object + // to compute the shape + // functions for the current + // cell: cell_matrix = 0; fe_values.reinit (cell); @@ -867,147 +867,147 @@ namespace Step29 for (unsigned int j=0; j::faces_per_cell; ++face) if (cell->face(face)->at_boundary() && (cell->face(face)->boundary_indicator() == 0) ) { - // These faces will - // certainly contribute - // to the off-diagonal - // blocks of the system - // matrix, so we ask the - // FEFaceValues object to - // provide us with the - // shape function values - // on this face: + // These faces will + // certainly contribute + // to the off-diagonal + // blocks of the system + // matrix, so we ask the + // FEFaceValues object to + // provide us with the + // shape function values + // on this face: fe_face_values.reinit (cell, face); - // Next, we loop through - // all DoFs of the - // current cell to find - // pairs that belong to - // different components - // and both have support - // on the current face: + // Next, we loop through + // all DoFs of the + // current cell to find + // pairs that belong to + // different components + // and both have support + // on the current face: for (unsigned int i=0; iget_dof_indices (local_dof_indices); - // ...and then add the entries to - // the system matrix one by - // one: + // ...and then add the entries to + // the system matrix one by + // one: for (unsigned int i=0; iDirichletBoundaryValues - // class we defined above: + // The only thing left are the + // Dirichlet boundary values on + // $\Gamma_1$, which is + // characterized by the boundary + // indicator 1. The Dirichlet + // values are provided by the + // DirichletBoundaryValues + // class we defined above: std::map boundary_values; VectorTools::interpolate_boundary_values (dof_handler, 1, @@ -1226,37 +1226,37 @@ namespace Step29 - // @sect4{UltrasoundProblem::solve} - - // As already mentioned in the - // introduction, the system matrix is - // neither symmetric nor definite, - // and so it is not quite obvious how - // to come up with an iterative - // solver and a preconditioner that - // do a good job on this matrix. We - // chose instead to go a different - // way and solve the linear system - // with the sparse LU decomposition - // provided by UMFPACK. This is often - // a good first choice for 2D - // problems and works reasonably well - // even for a large number of DoFs. - // The deal.II interface to UMFPACK - // is given by the - // SparseDirectUMFPACK class, which - // is very easy to use and allows us - // to solve our linear system with - // just 3 lines of code. - - // Note again that for compiling this - // example program, you need to have - // the deal.II library built with - // UMFPACK support, which can be - // achieved by providing the - // --with-umfpack switch to - // the configure script prior to - // compilation of the library. + // @sect4{UltrasoundProblem::solve} + + // As already mentioned in the + // introduction, the system matrix is + // neither symmetric nor definite, + // and so it is not quite obvious how + // to come up with an iterative + // solver and a preconditioner that + // do a good job on this matrix. We + // chose instead to go a different + // way and solve the linear system + // with the sparse LU decomposition + // provided by UMFPACK. This is often + // a good first choice for 2D + // problems and works reasonably well + // even for a large number of DoFs. + // The deal.II interface to UMFPACK + // is given by the + // SparseDirectUMFPACK class, which + // is very easy to use and allows us + // to solve our linear system with + // just 3 lines of code. + + // Note again that for compiling this + // example program, you need to have + // the deal.II library built with + // UMFPACK support, which can be + // achieved by providing the + // --with-umfpack switch to + // the configure script prior to + // compilation of the library. template void UltrasoundProblem::solve () { @@ -1264,29 +1264,29 @@ namespace Step29 Timer timer; timer.start (); - // The code to solve the linear - // system is short: First, we - // allocate an object of the right - // type. The following - // initialize call - // provides the matrix that we - // would like to invert to the - // SparseDirectUMFPACK object, and - // at the same time kicks off the - // LU-decomposition. Hence, this is - // also the point where most of the - // computational work in this - // program happens. + // The code to solve the linear + // system is short: First, we + // allocate an object of the right + // type. The following + // initialize call + // provides the matrix that we + // would like to invert to the + // SparseDirectUMFPACK object, and + // at the same time kicks off the + // LU-decomposition. Hence, this is + // also the point where most of the + // computational work in this + // program happens. SparseDirectUMFPACK A_direct; A_direct.initialize(system_matrix); - // After the decomposition, we can - // use A_direct like a - // matrix representing the inverse - // of our system matrix, so to - // compute the solution we just - // have to multiply with the right - // hand side vector: + // After the decomposition, we can + // use A_direct like a + // matrix representing the inverse + // of our system matrix, so to + // compute the solution we just + // have to multiply with the right + // hand side vector: A_direct.vmult (solution, system_rhs); timer.stop (); @@ -1298,21 +1298,21 @@ namespace Step29 - // @sect4{UltrasoundProblem::output_results} + // @sect4{UltrasoundProblem::output_results} - // Here we output our solution $v$ - // and $w$ as well as the derived - // quantity $|u|$ in the format - // specified in the parameter - // file. Most of the work for - // deriving $|u|$ from $v$ and $w$ - // was already done in the - // implementation of the - // ComputeIntensity - // class, so that the output routine - // is rather straightforward and very - // similar to what is done in the - // previous tutorials. + // Here we output our solution $v$ + // and $w$ as well as the derived + // quantity $|u|$ in the format + // specified in the parameter + // file. Most of the work for + // deriving $|u|$ from $v$ and $w$ + // was already done in the + // implementation of the + // ComputeIntensity + // class, so that the output routine + // is rather straightforward and very + // similar to what is done in the + // previous tutorials. template void UltrasoundProblem::output_results () const { @@ -1320,27 +1320,27 @@ namespace Step29 Timer timer; timer.start (); - // Define objects of our - // ComputeIntensity - // class and a DataOut object: + // Define objects of our + // ComputeIntensity + // class and a DataOut object: ComputeIntensity intensities; DataOut data_out; data_out.attach_dof_handler (dof_handler); - // Next we query the output-related - // parameters from the - // ParameterHandler. The - // DataOut::parse_parameters call - // acts as a counterpart to the - // DataOutInterface<1>::declare_parameters - // call in - // ParameterReader::declare_parameters. It - // collects all the output format - // related parameters from the - // ParameterHandler and sets the - // corresponding properties of the - // DataOut object accordingly. + // Next we query the output-related + // parameters from the + // ParameterHandler. The + // DataOut::parse_parameters call + // acts as a counterpart to the + // DataOutInterface<1>::declare_parameters + // call in + // ParameterReader::declare_parameters. It + // collects all the output format + // related parameters from the + // ParameterHandler and sets the + // corresponding properties of the + // DataOut object accordingly. prm.enter_subsection("Output parameters"); const std::string output_file = prm.get("Output file"); @@ -1348,42 +1348,42 @@ namespace Step29 prm.leave_subsection (); - // Now we put together the filename from - // the base name provided by the - // ParameterHandler and the suffix which is - // provided by the DataOut class (the - // default suffix is set to the right type - // that matches the one set in the .prm - // file through parse_parameters()): + // Now we put together the filename from + // the base name provided by the + // ParameterHandler and the suffix which is + // provided by the DataOut class (the + // default suffix is set to the right type + // that matches the one set in the .prm + // file through parse_parameters()): const std::string filename = output_file + data_out.default_suffix(); std::ofstream output (filename.c_str()); - // The solution vectors $v$ and $w$ - // are added to the DataOut object - // in the usual way: + // The solution vectors $v$ and $w$ + // are added to the DataOut object + // in the usual way: std::vector solution_names; solution_names.push_back ("Re_u"); solution_names.push_back ("Im_u"); data_out.add_data_vector (solution, solution_names); - // For the intensity, we just call - // add_data_vector - // again, but this with our - // ComputeIntensity - // object as the second argument, - // which effectively adds $|u|$ to - // the output data: + // For the intensity, we just call + // add_data_vector + // again, but this with our + // ComputeIntensity + // object as the second argument, + // which effectively adds $|u|$ to + // the output data: data_out.add_data_vector (solution, intensities); - // The last steps are as before. Note - // that the actual output format is - // now determined by what is stated in - // the input file, i.e. one can change - // the output format without having to - // re-compile this program: + // The last steps are as before. Note + // that the actual output format is + // now determined by what is stated in + // the input file, i.e. one can change + // the output format without having to + // re-compile this program: data_out.build_patches (); data_out.write (output); @@ -1396,9 +1396,9 @@ namespace Step29 - // @sect4{UltrasoundProblem::run} - // Here we simply execute our - // functions one after the other: + // @sect4{UltrasoundProblem::run} + // Here we simply execute our + // functions one after the other: template void UltrasoundProblem::run () { @@ -1411,22 +1411,22 @@ namespace Step29 } - // @sect4{The main function} - - // Finally the main - // function of the program. It has the - // same structure as in almost all of - // the other tutorial programs. The - // only exception is that we define - // ParameterHandler and - // ParameterReader - // objects, and let the latter read in - // the parameter values from a - // textfile called - // step-29.prm. The - // values so read are then handed over - // to an instance of the - // UltrasoundProblem class: +// @sect4{The main function} + +// Finally the main +// function of the program. It has the +// same structure as in almost all of +// the other tutorial programs. The +// only exception is that we define +// ParameterHandler and +// ParameterReader +// objects, and let the latter read in +// the parameter values from a +// textfile called +// step-29.prm. The +// values so read are then handed over +// to an instance of the +// UltrasoundProblem class: int main () { try diff --git a/deal.II/examples/step-3/step-3.cc b/deal.II/examples/step-3/step-3.cc index 5b4976d421..f31f2ed4bf 100644 --- a/deal.II/examples/step-3/step-3.cc +++ b/deal.II/examples/step-3/step-3.cc @@ -10,72 +10,72 @@ /* further information on this license. */ - // @sect3{Many new include files} +// @sect3{Many new include files} - // These include files are already - // known to you. They declare the - // classes which handle - // triangulations and enumeration of - // degrees of freedom: +// These include files are already +// known to you. They declare the +// classes which handle +// triangulations and enumeration of +// degrees of freedom: #include #include - // And this is the file in which the - // functions are declared that - // create grids: +// And this is the file in which the +// functions are declared that +// create grids: #include - // The next three files contain classes which - // are needed for loops over all cells and to - // get the information from the cell - // objects. The first two have been used - // before to get geometric information from - // cells; the last one is new and provides - // information about the degrees of freedom - // local to a cell: +// The next three files contain classes which +// are needed for loops over all cells and to +// get the information from the cell +// objects. The first two have been used +// before to get geometric information from +// cells; the last one is new and provides +// information about the degrees of freedom +// local to a cell: #include #include #include - // In this file contains the description of - // the Lagrange interpolation finite element: +// In this file contains the description of +// the Lagrange interpolation finite element: #include - // And this file is needed for the - // creation of sparsity patterns of - // sparse matrices, as shown in - // previous examples: +// And this file is needed for the +// creation of sparsity patterns of +// sparse matrices, as shown in +// previous examples: #include - // The next two file are needed for - // assembling the matrix using - // quadrature on each cell. The - // classes declared in them will be - // explained below: +// The next two file are needed for +// assembling the matrix using +// quadrature on each cell. The +// classes declared in them will be +// explained below: #include #include - // The following three include files - // we need for the treatment of - // boundary values: +// The following three include files +// we need for the treatment of +// boundary values: #include #include #include - // We're now almost to the end. The second to - // last group of include files is for the - // linear algebra which we employ to solve - // the system of equations arising from the - // finite element discretization of the - // Laplace equation. We will use vectors and - // full matrices for assembling the system of - // equations locally on each cell, and - // transfer the results into a sparse - // matrix. We will then use a Conjugate - // Gradient solver to solve the problem, for - // which we need a preconditioner (in this - // program, we use the identity - // preconditioner which does nothing, but we - // need to include the file anyway): +// We're now almost to the end. The second to +// last group of include files is for the +// linear algebra which we employ to solve +// the system of equations arising from the +// finite element discretization of the +// Laplace equation. We will use vectors and +// full matrices for assembling the system of +// equations locally on each cell, and +// transfer the results into a sparse +// matrix. We will then use a Conjugate +// Gradient solver to solve the problem, for +// which we need a preconditioner (in this +// program, we use the identity +// preconditioner which does nothing, but we +// need to include the file anyway): #include #include #include @@ -83,491 +83,491 @@ #include #include - // Finally, this is for output to a - // file and to the console: +// Finally, this is for output to a +// file and to the console: #include #include #include - // ...and this is to import the - // deal.II namespace into the global - // scope: +// ...and this is to import the +// deal.II namespace into the global +// scope: using namespace dealii; - // @sect3{The Step3 class} - - // Instead of the procedural programming of - // previous examples, we encapsulate - // everything into a class for this - // program. The class consists of functions - // which each perform certain aspects of a - // finite element program, a `main' function - // which controls what is done first and what - // is done next, and a list of member - // variables. - - // The public part of the class is rather - // short: it has a constructor and a function - // `run' that is called from the outside and - // acts as something like the `main' - // function: it coordinates which operations - // of this class shall be run in which - // order. Everything else in the class, - // i.e. all the functions that actually do - // anything, are in the private section of - // the class: +// @sect3{The Step3 class} + +// Instead of the procedural programming of +// previous examples, we encapsulate +// everything into a class for this +// program. The class consists of functions +// which each perform certain aspects of a +// finite element program, a `main' function +// which controls what is done first and what +// is done next, and a list of member +// variables. + +// The public part of the class is rather +// short: it has a constructor and a function +// `run' that is called from the outside and +// acts as something like the `main' +// function: it coordinates which operations +// of this class shall be run in which +// order. Everything else in the class, +// i.e. all the functions that actually do +// anything, are in the private section of +// the class: class Step3 { - public: - Step3 (); - - void run (); - - // Then there are the member functions - // that mostly do what their names - // suggest and whose have been discussed - // in the introduction already. Since - // they do not need to be called from - // outside, they are made private to this - // class. - - private: - void make_grid (); - void setup_system (); - void assemble_system (); - void solve (); - void output_results () const; - - // And finally we have some member - // variables. There are variables - // describing the triangulation - // and the global numbering of the - // degrees of freedom (we will - // specify the exact polynomial - // degree of the finite element - // in the constructor of this - // class)... - Triangulation<2> triangulation; - FE_Q<2> fe; - DoFHandler<2> dof_handler; - - // ...variables for the sparsity - // pattern and values of the - // system matrix resulting from - // the discretization of the - // Laplace equation... - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - - // ...and variables which will - // hold the right hand side and - // solution vectors. - Vector solution; - Vector system_rhs; +public: + Step3 (); + + void run (); + + // Then there are the member functions + // that mostly do what their names + // suggest and whose have been discussed + // in the introduction already. Since + // they do not need to be called from + // outside, they are made private to this + // class. + +private: + void make_grid (); + void setup_system (); + void assemble_system (); + void solve (); + void output_results () const; + + // And finally we have some member + // variables. There are variables + // describing the triangulation + // and the global numbering of the + // degrees of freedom (we will + // specify the exact polynomial + // degree of the finite element + // in the constructor of this + // class)... + Triangulation<2> triangulation; + FE_Q<2> fe; + DoFHandler<2> dof_handler; + + // ...variables for the sparsity + // pattern and values of the + // system matrix resulting from + // the discretization of the + // Laplace equation... + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + + // ...and variables which will + // hold the right hand side and + // solution vectors. + Vector solution; + Vector system_rhs; }; - // @sect4{Step3::Step3} - - // Here comes the constructor. It does not - // much more than first to specify that we - // want bi-linear elements (denoted by the - // parameter to the finite element object, - // which indicates the polynomial degree), - // and to associate the dof_handler variable - // to the triangulation we use. (Note that - // the triangulation isn't set up with a mesh - // at all at the present time, but the - // DoFHandler doesn't care: it only wants to - // know which triangulation it will be - // associated with, and it only starts to - // care about an actual mesh once you try to - // distribute degree of freedom on the mesh - // using the distribute_dofs() function.) All - // the other member variables of the - // Step3 class have a default - // constructor which does all we want. +// @sect4{Step3::Step3} + +// Here comes the constructor. It does not +// much more than first to specify that we +// want bi-linear elements (denoted by the +// parameter to the finite element object, +// which indicates the polynomial degree), +// and to associate the dof_handler variable +// to the triangulation we use. (Note that +// the triangulation isn't set up with a mesh +// at all at the present time, but the +// DoFHandler doesn't care: it only wants to +// know which triangulation it will be +// associated with, and it only starts to +// care about an actual mesh once you try to +// distribute degree of freedom on the mesh +// using the distribute_dofs() function.) All +// the other member variables of the +// Step3 class have a default +// constructor which does all we want. Step3::Step3 () - : - fe (1), - dof_handler (triangulation) + : + fe (1), + dof_handler (triangulation) {} - // @sect4{Step3::make_grid} +// @sect4{Step3::make_grid} - // Now, the first thing we've got to - // do is to generate the - // triangulation on which we would - // like to do our computation and - // number each vertex with a degree - // of freedom. We have seen this in - // the previous examples before. +// Now, the first thing we've got to +// do is to generate the +// triangulation on which we would +// like to do our computation and +// number each vertex with a degree +// of freedom. We have seen this in +// the previous examples before. void Step3::make_grid () { - // First create the grid and refine - // all cells five times. Since the - // initial grid (which is the - // square [-1,1]x[-1,1]) consists - // of only one cell, the final grid - // has 32 times 32 cells, for a - // total of 1024. + // First create the grid and refine + // all cells five times. Since the + // initial grid (which is the + // square [-1,1]x[-1,1]) consists + // of only one cell, the final grid + // has 32 times 32 cells, for a + // total of 1024. GridGenerator::hyper_cube (triangulation, -1, 1); triangulation.refine_global (5); - // Unsure that 1024 is the correct number? - // Let's see: n_active_cells returns the - // number of active cells: + // Unsure that 1024 is the correct number? + // Let's see: n_active_cells returns the + // number of active cells: std::cout << "Number of active cells: " << triangulation.n_active_cells() << std::endl; - // Here, by active we mean the cells that aren't - // refined any further. We stress the - // adjective `active', since there are more - // cells, namely the parent cells of the - // finest cells, their parents, etc, up to - // the one cell which made up the initial - // grid. Of course, on the next coarser - // level, the number of cells is one - // quarter that of the cells on the finest - // level, i.e. 256, then 64, 16, 4, and - // 1. We can get the total number of cells - // like this: + // Here, by active we mean the cells that aren't + // refined any further. We stress the + // adjective `active', since there are more + // cells, namely the parent cells of the + // finest cells, their parents, etc, up to + // the one cell which made up the initial + // grid. Of course, on the next coarser + // level, the number of cells is one + // quarter that of the cells on the finest + // level, i.e. 256, then 64, 16, 4, and + // 1. We can get the total number of cells + // like this: std::cout << "Total number of cells: " << triangulation.n_cells() << std::endl; - // Note the distinction between - // n_active_cells() and n_cells(). + // Note the distinction between + // n_active_cells() and n_cells(). } - // @sect4{Step3::setup_system} - - // Next we enumerate all the degrees of - // freedom and set up matrix and vector - // objects to hold the system - // data. Enumerating is done by using - // DoFHandler::distribute_dofs(), as we have - // seen in the step-2 example. Since we use - // the FE_Q class and have set the polynomial - // degree to 1 in the constructor, - // i.e. bilinear elements, this associates - // one degree of freedom with each - // vertex. While we're at generating output, - // let us also take a look at how many - // degrees of freedom are generated: +// @sect4{Step3::setup_system} + +// Next we enumerate all the degrees of +// freedom and set up matrix and vector +// objects to hold the system +// data. Enumerating is done by using +// DoFHandler::distribute_dofs(), as we have +// seen in the step-2 example. Since we use +// the FE_Q class and have set the polynomial +// degree to 1 in the constructor, +// i.e. bilinear elements, this associates +// one degree of freedom with each +// vertex. While we're at generating output, +// let us also take a look at how many +// degrees of freedom are generated: void Step3::setup_system () { dof_handler.distribute_dofs (fe); std::cout << "Number of degrees of freedom: " << dof_handler.n_dofs() << std::endl; - // There should be one DoF for each - // vertex. Since we have a 32 times - // 32 grid, the number of DoFs - // should be 33 times 33, or 1089. - - // As we have seen in the previous example, - // we set up a sparsity pattern by first - // creating a temporary structure, tagging - // those entries that might be nonzero, and - // then copying the data over to the - // SparsityPattern object that can then be - // used by the system matrix. + // There should be one DoF for each + // vertex. Since we have a 32 times + // 32 grid, the number of DoFs + // should be 33 times 33, or 1089. + + // As we have seen in the previous example, + // we set up a sparsity pattern by first + // creating a temporary structure, tagging + // those entries that might be nonzero, and + // then copying the data over to the + // SparsityPattern object that can then be + // used by the system matrix. CompressedSparsityPattern c_sparsity(dof_handler.n_dofs()); DoFTools::make_sparsity_pattern (dof_handler, c_sparsity); sparsity_pattern.copy_from(c_sparsity); - // Note that the - // SparsityPattern object does - // not hold the values of the - // matrix, it only stores the - // places where entries are. The - // entries themselves are stored in - // objects of type SparseMatrix, of - // which our variable system_matrix - // is one. - // - // The distinction between sparsity pattern - // and matrix was made to allow several - // matrices to use the same sparsity - // pattern. This may not seem relevant - // here, but when you consider the size - // which matrices can have, and that it may - // take some time to build the sparsity - // pattern, this becomes important in - // large-scale problems if you have to - // store several matrices in your program. + // Note that the + // SparsityPattern object does + // not hold the values of the + // matrix, it only stores the + // places where entries are. The + // entries themselves are stored in + // objects of type SparseMatrix, of + // which our variable system_matrix + // is one. + // + // The distinction between sparsity pattern + // and matrix was made to allow several + // matrices to use the same sparsity + // pattern. This may not seem relevant + // here, but when you consider the size + // which matrices can have, and that it may + // take some time to build the sparsity + // pattern, this becomes important in + // large-scale problems if you have to + // store several matrices in your program. system_matrix.reinit (sparsity_pattern); - // The last thing to do in this - // function is to set the sizes of - // the right hand side vector and - // the solution vector to the right - // values: + // The last thing to do in this + // function is to set the sizes of + // the right hand side vector and + // the solution vector to the right + // values: solution.reinit (dof_handler.n_dofs()); system_rhs.reinit (dof_handler.n_dofs()); } - // @sect4{Step3::assemble_system} - - - // The next step is to compute the entries of - // the matrix and right hand side that form - // the linear system from which we compute - // the solution. This is the central function - // of each finite element program and we have - // discussed the primary steps in the - // introduction already. - // - // The general approach to assemble matrices - // and vectors is to loop over all cells, and - // on each cell compute the contribution of - // that cell to the global matrix and right - // hand side by quadrature. The point to - // realize now is that we need the values of - // the shape functions at the locations of - // quadrature points on the real - // cell. However, both the finite element - // shape functions as well as the quadrature - // points are only defined on the reference - // cell. They are therefore of little help to - // us, and we will in fact hardly ever query - // information about finite element shape - // functions or quadrature points from these - // objects directly. - // - // Rather, what is required is a way to map - // this data from the reference cell to the - // real cell. Classes that can do that are - // derived from the Mapping class, though one - // again often does not have to deal with - // them directly: many functions in the - // library can take a mapping object as - // argument, but when it is omitted they - // simply resort to the standard bilinear Q1 - // mapping. We will go this route, and not - // bother with it for the moment (we come - // back to this in step-10, step-11, and - // step-12). - // - // So what we now have is a collection of - // three classes to deal with: finite - // element, quadrature, and mapping - // objects. That's too much, so there is one - // type of class that orchestrates - // information exchange between these three: - // the FEValues class. If given one instance - // of each three of these objects (or two, - // and an implicit linear mapping), it will - // be able to provide you with information - // about values and gradients of shape - // functions at quadrature points on a real - // cell. - // - // Using all this, we will assemble the - // linear system for this problem in the - // following function: +// @sect4{Step3::assemble_system} + + +// The next step is to compute the entries of +// the matrix and right hand side that form +// the linear system from which we compute +// the solution. This is the central function +// of each finite element program and we have +// discussed the primary steps in the +// introduction already. +// +// The general approach to assemble matrices +// and vectors is to loop over all cells, and +// on each cell compute the contribution of +// that cell to the global matrix and right +// hand side by quadrature. The point to +// realize now is that we need the values of +// the shape functions at the locations of +// quadrature points on the real +// cell. However, both the finite element +// shape functions as well as the quadrature +// points are only defined on the reference +// cell. They are therefore of little help to +// us, and we will in fact hardly ever query +// information about finite element shape +// functions or quadrature points from these +// objects directly. +// +// Rather, what is required is a way to map +// this data from the reference cell to the +// real cell. Classes that can do that are +// derived from the Mapping class, though one +// again often does not have to deal with +// them directly: many functions in the +// library can take a mapping object as +// argument, but when it is omitted they +// simply resort to the standard bilinear Q1 +// mapping. We will go this route, and not +// bother with it for the moment (we come +// back to this in step-10, step-11, and +// step-12). +// +// So what we now have is a collection of +// three classes to deal with: finite +// element, quadrature, and mapping +// objects. That's too much, so there is one +// type of class that orchestrates +// information exchange between these three: +// the FEValues class. If given one instance +// of each three of these objects (or two, +// and an implicit linear mapping), it will +// be able to provide you with information +// about values and gradients of shape +// functions at quadrature points on a real +// cell. +// +// Using all this, we will assemble the +// linear system for this problem in the +// following function: void Step3::assemble_system () { - // Ok, let's start: we need a quadrature - // formula for the evaluation of the - // integrals on each cell. Let's take a - // Gauss formula with two quadrature points - // in each direction, i.e. a total of four - // points since we are in 2D. This - // quadrature formula integrates - // polynomials of degrees up to three - // exactly (in 1D). It is easy to check - // that this is sufficient for the present - // problem: + // Ok, let's start: we need a quadrature + // formula for the evaluation of the + // integrals on each cell. Let's take a + // Gauss formula with two quadrature points + // in each direction, i.e. a total of four + // points since we are in 2D. This + // quadrature formula integrates + // polynomials of degrees up to three + // exactly (in 1D). It is easy to check + // that this is sufficient for the present + // problem: QGauss<2> quadrature_formula(2); - // And we initialize the object which we - // have briefly talked about above. It - // needs to be told which finite element we - // want to use, and the quadrature points - // and their weights (jointly described by - // a Quadrature object). As mentioned, we - // use the implied Q1 mapping, rather than - // specifying one ourselves - // explicitly. Finally, we have to tell it - // what we want it to compute on each cell: - // we need the values of the shape - // functions at the quadrature points (for - // the right hand side $(\varphi,f)$), their - // gradients (for the matrix entries $(\nabla - // \varphi_i, \nabla \varphi_j)$), and also the - // weights of the quadrature points and the - // determinants of the Jacobian - // transformations from the reference cell - // to the real cells. - // - // This list of what kind of information we - // actually need is given as a - // collection of flags as the third - // argument to the constructor of - // FEValues. Since these values have to - // be recomputed, or updated, every time we - // go to a new cell, all of these flags - // start with the prefix update_ and - // then indicate what it actually is that - // we want updated. The flag to give if we - // want the values of the shape functions - // computed is #update_values; for the - // gradients it is - // #update_gradients. The determinants - // of the Jacobians and the quadrature - // weights are always used together, so - // only the products (Jacobians times - // weights, or short JxW) are computed; - // since we need them, we have to list - // #update_JxW_values as well: + // And we initialize the object which we + // have briefly talked about above. It + // needs to be told which finite element we + // want to use, and the quadrature points + // and their weights (jointly described by + // a Quadrature object). As mentioned, we + // use the implied Q1 mapping, rather than + // specifying one ourselves + // explicitly. Finally, we have to tell it + // what we want it to compute on each cell: + // we need the values of the shape + // functions at the quadrature points (for + // the right hand side $(\varphi,f)$), their + // gradients (for the matrix entries $(\nabla + // \varphi_i, \nabla \varphi_j)$), and also the + // weights of the quadrature points and the + // determinants of the Jacobian + // transformations from the reference cell + // to the real cells. + // + // This list of what kind of information we + // actually need is given as a + // collection of flags as the third + // argument to the constructor of + // FEValues. Since these values have to + // be recomputed, or updated, every time we + // go to a new cell, all of these flags + // start with the prefix update_ and + // then indicate what it actually is that + // we want updated. The flag to give if we + // want the values of the shape functions + // computed is #update_values; for the + // gradients it is + // #update_gradients. The determinants + // of the Jacobians and the quadrature + // weights are always used together, so + // only the products (Jacobians times + // weights, or short JxW) are computed; + // since we need them, we have to list + // #update_JxW_values as well: FEValues<2> fe_values (fe, quadrature_formula, update_values | update_gradients | update_JxW_values); - // The advantage of this approach is that - // we can specify what kind of information - // we actually need on each cell. It is - // easily understandable that this approach - // can significant speed up finite element - // computations, compared to approaches - // where everything, including second - // derivatives, normal vectors to cells, - // etc are computed on each cell, - // regardless whether they are needed or - // not. - - // For use further down below, we define - // two short cuts for values that will be - // used very frequently. First, an - // abbreviation for the number of degrees - // of freedom on each cell (since we are in - // 2D and degrees of freedom are associated - // with vertices only, this number is four, - // but we rather want to write the - // definition of this variable in a way - // that does not preclude us from later - // choosing a different finite element that - // has a different number of degrees of - // freedom per cell, or work in a different - // space dimension). - // - // Secondly, we also define an abbreviation - // for the number of quadrature points - // (here that should be four). In general, - // it is a good idea to use their symbolic - // names instead of hard-coding these - // number even if you know them, since you - // may want to change the quadrature - // formula and/or finite element at some - // time; the program will just work with - // these changes, without the need to - // change anything in this function. - // - // The shortcuts, finally, are only defined - // to make the following loops a bit more - // readable. You will see them in many - // places in larger programs, and - // `dofs_per_cell' and `n_q_points' are - // more or less by convention the standard - // names for these purposes: + // The advantage of this approach is that + // we can specify what kind of information + // we actually need on each cell. It is + // easily understandable that this approach + // can significant speed up finite element + // computations, compared to approaches + // where everything, including second + // derivatives, normal vectors to cells, + // etc are computed on each cell, + // regardless whether they are needed or + // not. + + // For use further down below, we define + // two short cuts for values that will be + // used very frequently. First, an + // abbreviation for the number of degrees + // of freedom on each cell (since we are in + // 2D and degrees of freedom are associated + // with vertices only, this number is four, + // but we rather want to write the + // definition of this variable in a way + // that does not preclude us from later + // choosing a different finite element that + // has a different number of degrees of + // freedom per cell, or work in a different + // space dimension). + // + // Secondly, we also define an abbreviation + // for the number of quadrature points + // (here that should be four). In general, + // it is a good idea to use their symbolic + // names instead of hard-coding these + // number even if you know them, since you + // may want to change the quadrature + // formula and/or finite element at some + // time; the program will just work with + // these changes, without the need to + // change anything in this function. + // + // The shortcuts, finally, are only defined + // to make the following loops a bit more + // readable. You will see them in many + // places in larger programs, and + // `dofs_per_cell' and `n_q_points' are + // more or less by convention the standard + // names for these purposes: const unsigned int dofs_per_cell = fe.dofs_per_cell; const unsigned int n_q_points = quadrature_formula.size(); - // Now, we said that we wanted to assemble - // the global matrix and vector - // cell-by-cell. We could write the results - // directly into the global matrix, but - // this is not very efficient since access - // to the elements of a sparse matrix is - // slow. Rather, we first compute the - // contribution of each cell in a small - // matrix with the degrees of freedom on - // the present cell, and only transfer them - // to the global matrix when the - // computations are finished for this - // cell. We do the same for the right hand - // side vector. So let's first allocate - // these objects (these being local - // objects, all degrees of freedom are - // coupling with all others, and we should - // use a full matrix object rather than a - // sparse one for the local operations; - // everything will be transferred to a - // global sparse matrix later on): + // Now, we said that we wanted to assemble + // the global matrix and vector + // cell-by-cell. We could write the results + // directly into the global matrix, but + // this is not very efficient since access + // to the elements of a sparse matrix is + // slow. Rather, we first compute the + // contribution of each cell in a small + // matrix with the degrees of freedom on + // the present cell, and only transfer them + // to the global matrix when the + // computations are finished for this + // cell. We do the same for the right hand + // side vector. So let's first allocate + // these objects (these being local + // objects, all degrees of freedom are + // coupling with all others, and we should + // use a full matrix object rather than a + // sparse one for the local operations; + // everything will be transferred to a + // global sparse matrix later on): FullMatrix cell_matrix (dofs_per_cell, dofs_per_cell); Vector cell_rhs (dofs_per_cell); - // When assembling the - // contributions of each cell, we - // do this with the local numbering - // of the degrees of freedom - // (i.e. the number running from - // zero through - // dofs_per_cell-1). However, when - // we transfer the result into the - // global matrix, we have to know - // the global numbers of the - // degrees of freedom. When we query - // them, we need a scratch - // (temporary) array for these - // numbers: + // When assembling the + // contributions of each cell, we + // do this with the local numbering + // of the degrees of freedom + // (i.e. the number running from + // zero through + // dofs_per_cell-1). However, when + // we transfer the result into the + // global matrix, we have to know + // the global numbers of the + // degrees of freedom. When we query + // them, we need a scratch + // (temporary) array for these + // numbers: std::vector local_dof_indices (dofs_per_cell); - // Now for the loop over all cells. We have - // seen before how this works, so this - // should be familiar including the - // conventional names for these variables: + // Now for the loop over all cells. We have + // seen before how this works, so this + // should be familiar including the + // conventional names for these variables: DoFHandler<2>::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { - // We are now sitting on one cell, and - // we would like the values and - // gradients of the shape functions be - // computed, as well as the - // determinants of the Jacobian - // matrices of the mapping between - // reference cell and true cell, at the - // quadrature points. Since all these - // values depend on the geometry of the - // cell, we have to have the FEValues - // object re-compute them on each cell: + // We are now sitting on one cell, and + // we would like the values and + // gradients of the shape functions be + // computed, as well as the + // determinants of the Jacobian + // matrices of the mapping between + // reference cell and true cell, at the + // quadrature points. Since all these + // values depend on the geometry of the + // cell, we have to have the FEValues + // object re-compute them on each cell: fe_values.reinit (cell); - // Next, reset the local cell's - // contributions to - // global matrix and global right hand - // side to zero, before we fill them: + // Next, reset the local cell's + // contributions to + // global matrix and global right hand + // side to zero, before we fill them: cell_matrix = 0; cell_rhs = 0; - // Then finally assemble the matrix: - // For the Laplace problem, the matrix - // on each cell is the integral over - // the gradients of shape function i - // and j. Since we do not integrate, - // but rather use quadrature, this is - // the sum over all quadrature points - // of the integrands times the - // determinant of the Jacobian matrix - // at the quadrature point times the - // weight of this quadrature point. You - // can get the gradient of shape - // function $i$ at quadrature point - // q_point by using - // fe_values.shape_grad(i,q_point); - // this gradient is a 2-dimensional - // vector (in fact it is of type - // Tensor@<1,dim@>, with here dim=2) and - // the product of two such vectors is - // the scalar product, i.e. the product - // of the two shape_grad function calls - // is the dot product. This is in turn - // multiplied by the Jacobian - // determinant and the quadrature point - // weight (that one gets together by - // the call to - // FEValues::JxW() ). Finally, this is - // repeated for all shape functions - // $i$ and $j$: + // Then finally assemble the matrix: + // For the Laplace problem, the matrix + // on each cell is the integral over + // the gradients of shape function i + // and j. Since we do not integrate, + // but rather use quadrature, this is + // the sum over all quadrature points + // of the integrands times the + // determinant of the Jacobian matrix + // at the quadrature point times the + // weight of this quadrature point. You + // can get the gradient of shape + // function $i$ at quadrature point + // q_point by using + // fe_values.shape_grad(i,q_point); + // this gradient is a 2-dimensional + // vector (in fact it is of type + // Tensor@<1,dim@>, with here dim=2) and + // the product of two such vectors is + // the scalar product, i.e. the product + // of the two shape_grad function calls + // is the dot product. This is in turn + // multiplied by the Jacobian + // determinant and the quadrature point + // weight (that one gets together by + // the call to + // FEValues::JxW() ). Finally, this is + // repeated for all shape functions + // $i$ and $j$: for (unsigned int i=0; iget_dof_indices (local_dof_indices); - // Then again loop over all - // shape functions i and j and - // transfer the local elements - // to the global matrix. The - // global numbers can be - // obtained using - // local_dof_indices[i]: + // Then again loop over all + // shape functions i and j and + // transfer the local elements + // to the global matrix. The + // global numbers can be + // obtained using + // local_dof_indices[i]: for (unsigned int i=0; istd::map class. + // Now almost everything is set up for the + // solution of the discrete + // system. However, we have not yet taken + // care of boundary values (in fact, + // Laplace's equation without Dirichlet + // boundary values is not even uniquely + // solvable, since you can add an arbitrary + // constant to the discrete solution). We + // therefore have to do something about the + // situation. + // + // For this, we first obtain a list of the + // degrees of freedom on the boundary and + // the value the shape function shall have + // there. For simplicity, we only + // interpolate the boundary value function, + // rather than projecting it onto the + // boundary. There is a function in the + // library which does exactly this: + // VectorTools::interpolate_boundary_values(). Its + // parameters are (omitting parameters for + // which default values exist and that we + // don't care about): the DoFHandler object + // to get the global numbers of the degrees + // of freedom on the boundary; the + // component of the boundary where the + // boundary values shall be interpolated; + // the boundary value function itself; and + // the output object. + // + // The component of the boundary is meant + // as follows: in many cases, you may want + // to impose certain boundary values only + // on parts of the boundary. For example, + // you may have inflow and outflow + // boundaries in fluid dynamics, or clamped + // and free parts of bodies in deformation + // computations of bodies. Then you will + // want to denote these different parts of + // the boundary by different numbers and + // tell the interpolate_boundary_values + // function to only compute the boundary + // values on a certain part of the boundary + // (e.g. the clamped part, or the inflow + // boundary). By default, all boundaries + // have the number `0', and since we have + // not changed that, this is still so; + // therefore, if we give `0' as the desired + // portion of the boundary, this means we + // get the whole boundary. If you have + // boundaries with kinds of boundaries, you + // have to number them differently. The + // function call below will then only + // determine boundary values for parts of + // the boundary. + // + // The function describing the boundary + // values is an object of type Function + // or of a derived class. One of the + // derived classes is ZeroFunction, + // which describes (not unexpectedly) a + // function which is zero everywhere. We + // create such an object in-place and pass + // it to the VectorTools::interpolate_boundary_values() + // function. + // + // Finally, the output object is a + // list of pairs of global degree + // of freedom numbers (i.e. the + // number of the degrees of freedom + // on the boundary) and their + // boundary values (which are zero + // here for all entries). This + // mapping of DoF numbers to + // boundary values is done by the + // std::map class. std::map boundary_values; VectorTools::interpolate_boundary_values (dof_handler, 0, ZeroFunction<2>(), boundary_values); - // Now that we got the list of - // boundary DoFs and their - // respective boundary values, - // let's use them to modify the - // system of equations - // accordingly. This is done by the - // following function call: + // Now that we got the list of + // boundary DoFs and their + // respective boundary values, + // let's use them to modify the + // system of equations + // accordingly. This is done by the + // following function call: MatrixTools::apply_boundary_values (boundary_values, system_matrix, solution, @@ -732,137 +732,137 @@ void Step3::assemble_system () } - // @sect4{Step3::solve} - - // The following function simply - // solves the discretized - // equation. As the system is quite a - // large one for direct solvers such - // as Gauss elimination or LU - // decomposition, we use a Conjugate - // Gradient algorithm. You should - // remember that the number of - // variables here (only 1089) is a - // very small number for finite - // element computations, where - // 100.000 is a more usual number. - // For this number of variables, - // direct methods are no longer - // usable and you are forced to use - // methods like CG. +// @sect4{Step3::solve} + +// The following function simply +// solves the discretized +// equation. As the system is quite a +// large one for direct solvers such +// as Gauss elimination or LU +// decomposition, we use a Conjugate +// Gradient algorithm. You should +// remember that the number of +// variables here (only 1089) is a +// very small number for finite +// element computations, where +// 100.000 is a more usual number. +// For this number of variables, +// direct methods are no longer +// usable and you are forced to use +// methods like CG. void Step3::solve () { - // First, we need to have an object that - // knows how to tell the CG algorithm when - // to stop. This is done by using a - // SolverControl object, and as stopping - // criterion we say: stop after a maximum - // of 1000 iterations (which is far more - // than is needed for 1089 variables; see - // the results section to find out how many - // were really used), and stop if the norm - // of the residual is below $10^{-12}$. In - // practice, the latter criterion will be - // the one which stops the iteration: + // First, we need to have an object that + // knows how to tell the CG algorithm when + // to stop. This is done by using a + // SolverControl object, and as stopping + // criterion we say: stop after a maximum + // of 1000 iterations (which is far more + // than is needed for 1089 variables; see + // the results section to find out how many + // were really used), and stop if the norm + // of the residual is below $10^{-12}$. In + // practice, the latter criterion will be + // the one which stops the iteration: SolverControl solver_control (1000, 1e-12); - // Then we need the solver itself. The - // template parameters to the SolverCG - // class are the matrix type and the type - // of the vectors, but the empty angle - // brackets indicate that we simply take - // the default arguments (which are - // SparseMatrix@ and - // Vector@): + // Then we need the solver itself. The + // template parameters to the SolverCG + // class are the matrix type and the type + // of the vectors, but the empty angle + // brackets indicate that we simply take + // the default arguments (which are + // SparseMatrix@ and + // Vector@): SolverCG<> solver (solver_control); - // Now solve the system of equations. The - // CG solver takes a preconditioner as its - // fourth argument. We don't feel ready to - // delve into this yet, so we tell it to - // use the identity operation as - // preconditioner: + // Now solve the system of equations. The + // CG solver takes a preconditioner as its + // fourth argument. We don't feel ready to + // delve into this yet, so we tell it to + // use the identity operation as + // preconditioner: solver.solve (system_matrix, solution, system_rhs, PreconditionIdentity()); - // Now that the solver has done its - // job, the solution variable - // contains the nodal values of the - // solution function. + // Now that the solver has done its + // job, the solution variable + // contains the nodal values of the + // solution function. } - // @sect4{Step3::output_results} +// @sect4{Step3::output_results} - // The last part of a typical finite - // element program is to output the - // results and maybe do some - // postprocessing (for example - // compute the maximal stress values - // at the boundary, or the average - // flux across the outflow, etc). We - // have no such postprocessing here, - // but we would like to write the - // solution to a file. +// The last part of a typical finite +// element program is to output the +// results and maybe do some +// postprocessing (for example +// compute the maximal stress values +// at the boundary, or the average +// flux across the outflow, etc). We +// have no such postprocessing here, +// but we would like to write the +// solution to a file. void Step3::output_results () const { - // To write the output to a file, - // we need an object which knows - // about output formats and the - // like. This is the DataOut class, - // and we need an object of that - // type: + // To write the output to a file, + // we need an object which knows + // about output formats and the + // like. This is the DataOut class, + // and we need an object of that + // type: DataOut<2> data_out; - // Now we have to tell it where to take the - // values from which it shall write. We - // tell it which DoFHandler object to - // use, and the solution vector (and - // the name by which the solution variable - // shall appear in the output file). If - // we had more than one vector which we - // would like to look at in the output (for - // example right hand sides, errors per - // cell, etc) we would add them as well: + // Now we have to tell it where to take the + // values from which it shall write. We + // tell it which DoFHandler object to + // use, and the solution vector (and + // the name by which the solution variable + // shall appear in the output file). If + // we had more than one vector which we + // would like to look at in the output (for + // example right hand sides, errors per + // cell, etc) we would add them as well: data_out.attach_dof_handler (dof_handler); data_out.add_data_vector (solution, "solution"); - // After the DataOut object knows - // which data it is to work on, we - // have to tell it to process them - // into something the back ends can - // handle. The reason is that we - // have separated the frontend - // (which knows about how to treat - // DoFHandler objects and data - // vectors) from the back end (which - // knows many different output formats) - // and use an intermediate data - // format to transfer data from the - // front- to the backend. The data - // is transformed into this - // intermediate format by the - // following function: + // After the DataOut object knows + // which data it is to work on, we + // have to tell it to process them + // into something the back ends can + // handle. The reason is that we + // have separated the frontend + // (which knows about how to treat + // DoFHandler objects and data + // vectors) from the back end (which + // knows many different output formats) + // and use an intermediate data + // format to transfer data from the + // front- to the backend. The data + // is transformed into this + // intermediate format by the + // following function: data_out.build_patches (); - // Now we have everything in place - // for the actual output. Just open - // a file and write the data into - // it, using GNUPLOT format (there - // are other functions which write - // their data in postscript, AVS, - // GMV, or some other format): + // Now we have everything in place + // for the actual output. Just open + // a file and write the data into + // it, using GNUPLOT format (there + // are other functions which write + // their data in postscript, AVS, + // GMV, or some other format): std::ofstream output ("solution.gpl"); data_out.write_gnuplot (output); } - // @sect4{Step3::run} +// @sect4{Step3::run} - // Finally, the last function of this class - // is the main function which calls all the - // other functions of the Step3 - // class. The order in which this is done - // resembles the order in which most finite - // element programs work. Since the names are - // mostly self-explanatory, there is not much - // to comment about: +// Finally, the last function of this class +// is the main function which calls all the +// other functions of the Step3 +// class. The order in which this is done +// resembles the order in which most finite +// element programs work. Since the names are +// mostly self-explanatory, there is not much +// to comment about: void Step3::run () { make_grid (); @@ -873,17 +873,17 @@ void Step3::run () } - // @sect3{The main function} +// @sect3{The main function} - // This is the main function of the - // program. Since the concept of a - // main function is mostly a remnant - // from the pre-object era in C/C++ - // programming, it often does not - // much more than creating an object - // of the top-level class and calling - // its principle function. This is - // what is done here as well: +// This is the main function of the +// program. Since the concept of a +// main function is mostly a remnant +// from the pre-object era in C/C++ +// programming, it often does not +// much more than creating an object +// of the top-level class and calling +// its principle function. This is +// what is done here as well: int main () { Step3 laplace_problem; diff --git a/deal.II/examples/step-30/step-30.cc b/deal.II/examples/step-30/step-30.cc index 6805f049f0..30b06b08ff 100644 --- a/deal.II/examples/step-30/step-30.cc +++ b/deal.II/examples/step-30/step-30.cc @@ -10,10 +10,10 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // The deal.II include files have already - // been covered in previous examples - // and will thus not be further - // commented on. +// The deal.II include files have already +// been covered in previous examples +// and will thus not be further +// commented on. #include #include #include @@ -36,49 +36,49 @@ #include #include - // And this again is C++: +// And this again is C++: #include #include - // The last step is as in all - // previous programs: +// The last step is as in all +// previous programs: namespace Step30 { using namespace dealii; - // @sect3{Equation data} - // - // The classes describing equation data and the - // actual assembly of individual terms are - // almost entirely copied from step-12. We will - // comment on differences. + // @sect3{Equation data} + // + // The classes describing equation data and the + // actual assembly of individual terms are + // almost entirely copied from step-12. We will + // comment on differences. template class RHS: public Function { - public: - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component=0) const; + public: + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component=0) const; }; template class BoundaryValues: public Function { - public: - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component=0) const; + public: + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component=0) const; }; template class Beta { - public: - Beta () {} - void value_list (const std::vector > &points, - std::vector > &values) const; + public: + Beta () {} + void value_list (const std::vector > &points, + std::vector > &values) const; }; @@ -95,27 +95,27 @@ namespace Step30 } - // The flow field is chosen to be a - // quarter circle with - // counterclockwise flow direction - // and with the origin as midpoint - // for the right half of the domain - // with positive $x$ values, whereas - // the flow simply goes to the left - // in the left part of the domain at - // a velocity that matches the one - // coming in from the right. In the - // circular part the magnitude of the - // flow velocity is proportional to - // the distance from the origin. This - // is a difference to step-12, where - // the magnitude was 1 - // evereywhere. the new definition - // leads to a linear variation of - // $\beta$ along each given face of a - // cell. On the other hand, the - // solution $u(x,y)$ is exactly the - // same as before. + // The flow field is chosen to be a + // quarter circle with + // counterclockwise flow direction + // and with the origin as midpoint + // for the right half of the domain + // with positive $x$ values, whereas + // the flow simply goes to the left + // in the left part of the domain at + // a velocity that matches the one + // coming in from the right. In the + // circular part the magnitude of the + // flow velocity is proportional to + // the distance from the origin. This + // is a difference to step-12, where + // the magnitude was 1 + // evereywhere. the new definition + // leads to a linear variation of + // $\beta$ along each given face of a + // cell. On the other hand, the + // solution $u(x,y)$ is exactly the + // same as before. template void Beta::value_list(const std::vector > &points, std::vector > &values) const @@ -157,67 +157,67 @@ namespace Step30 } - // @sect3{Class: DGTransportEquation} - // - // This declaration of this - // class is utterly unaffected by our - // current changes. The only - // substantial change is that we use - // only the second assembly scheme - // described in step-12. + // @sect3{Class: DGTransportEquation} + // + // This declaration of this + // class is utterly unaffected by our + // current changes. The only + // substantial change is that we use + // only the second assembly scheme + // described in step-12. template class DGTransportEquation { - public: - DGTransportEquation(); - - void assemble_cell_term(const FEValues& fe_v, - FullMatrix &ui_vi_matrix, - Vector &cell_vector) const; - - void assemble_boundary_term(const FEFaceValues& fe_v, - FullMatrix &ui_vi_matrix, - Vector &cell_vector) const; - - void assemble_face_term2(const FEFaceValuesBase& fe_v, - const FEFaceValuesBase& fe_v_neighbor, - FullMatrix &ui_vi_matrix, - FullMatrix &ue_vi_matrix, - FullMatrix &ui_ve_matrix, - FullMatrix &ue_ve_matrix) const; - private: - const Beta beta_function; - const RHS rhs_function; - const BoundaryValues boundary_function; + public: + DGTransportEquation(); + + void assemble_cell_term(const FEValues &fe_v, + FullMatrix &ui_vi_matrix, + Vector &cell_vector) const; + + void assemble_boundary_term(const FEFaceValues &fe_v, + FullMatrix &ui_vi_matrix, + Vector &cell_vector) const; + + void assemble_face_term2(const FEFaceValuesBase &fe_v, + const FEFaceValuesBase &fe_v_neighbor, + FullMatrix &ui_vi_matrix, + FullMatrix &ue_vi_matrix, + FullMatrix &ui_ve_matrix, + FullMatrix &ue_ve_matrix) const; + private: + const Beta beta_function; + const RHS rhs_function; + const BoundaryValues boundary_function; }; - // Likewise, the constructor of the - // class as well as the functions - // assembling the terms corresponding - // to cell interiors and boundary - // faces are unchanged from - // before. The function that - // assembles face terms between cells - // also did not change because all it - // does is operate on two objects of - // type FEFaceValuesBase (which is - // the base class of both - // FEFaceValues and - // FESubfaceValues). Where these - // objects come from, i.e. how they - // are initialized, is of no concern - // to this function: it simply - // assumes that the quadrature points - // on faces or subfaces represented - // by the two objects correspond to - // the same points in physical space. + // Likewise, the constructor of the + // class as well as the functions + // assembling the terms corresponding + // to cell interiors and boundary + // faces are unchanged from + // before. The function that + // assembles face terms between cells + // also did not change because all it + // does is operate on two objects of + // type FEFaceValuesBase (which is + // the base class of both + // FEFaceValues and + // FESubfaceValues). Where these + // objects come from, i.e. how they + // are initialized, is of no concern + // to this function: it simply + // assumes that the quadrature points + // on faces or subfaces represented + // by the two objects correspond to + // the same points in physical space. template DGTransportEquation::DGTransportEquation () - : - beta_function (), - rhs_function (), - boundary_function () + : + beta_function (), + rhs_function (), + boundary_function () {} @@ -250,7 +250,7 @@ namespace Step30 template void DGTransportEquation::assemble_boundary_term( - const FEFaceValues& fe_v, + const FEFaceValues &fe_v, FullMatrix &ui_vi_matrix, Vector &cell_vector) const { @@ -285,8 +285,8 @@ namespace Step30 template void DGTransportEquation::assemble_face_term2( - const FEFaceValuesBase& fe_v, - const FEFaceValuesBase& fe_v_neighbor, + const FEFaceValuesBase &fe_v, + const FEFaceValuesBase &fe_v_neighbor, FullMatrix &ui_vi_matrix, FullMatrix &ue_vi_matrix, FullMatrix &ui_ve_matrix, @@ -338,109 +338,109 @@ namespace Step30 } - // @sect3{Class: DGMethod} - // - // Even the main class of this - // program stays more or less the - // same. We omit one of the assembly - // routines and use only the second, - // more effective one of the two - // presented in step-12. However, we - // introduce a new routine - // (set_anisotropic_flags) and modify - // another one (refine_grid). + // @sect3{Class: DGMethod} + // + // Even the main class of this + // program stays more or less the + // same. We omit one of the assembly + // routines and use only the second, + // more effective one of the two + // presented in step-12. However, we + // introduce a new routine + // (set_anisotropic_flags) and modify + // another one (refine_grid). template class DGMethod { - public: - DGMethod (const bool anisotropic); - ~DGMethod (); - - void run (); - - private: - void setup_system (); - void assemble_system1 (); - void assemble_system2 (); - void solve (Vector &solution); - void refine_grid (); - void set_anisotropic_flags (); - void output_results (const unsigned int cycle) const; - - Triangulation triangulation; - const MappingQ1 mapping; - // Again we want to use DG elements of - // degree 1 (but this is only specified in - // the constructor). If you want to use a - // DG method of a different degree replace - // 1 in the constructor by the new degree. - const unsigned int degree; - FE_DGQ fe; - DoFHandler dof_handler; - - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - // This is new, the threshold value used in - // the evaluation of the anisotropic jump - // indicator explained in the - // introduction. Its value is set to 3.0 in - // the constructor, but it can easily be - // changed to a different value greater - // than 1. - const double anisotropic_threshold_ratio; - // This is a bool flag indicating whether - // anisotropic refinement shall be used or - // not. It is set by the constructor, which - // takes an argument of the same name. - const bool anisotropic; - - const QGauss quadrature; - const QGauss face_quadrature; - - Vector solution2; - Vector right_hand_side; - - const DGTransportEquation dg; + public: + DGMethod (const bool anisotropic); + ~DGMethod (); + + void run (); + + private: + void setup_system (); + void assemble_system1 (); + void assemble_system2 (); + void solve (Vector &solution); + void refine_grid (); + void set_anisotropic_flags (); + void output_results (const unsigned int cycle) const; + + Triangulation triangulation; + const MappingQ1 mapping; + // Again we want to use DG elements of + // degree 1 (but this is only specified in + // the constructor). If you want to use a + // DG method of a different degree replace + // 1 in the constructor by the new degree. + const unsigned int degree; + FE_DGQ fe; + DoFHandler dof_handler; + + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + // This is new, the threshold value used in + // the evaluation of the anisotropic jump + // indicator explained in the + // introduction. Its value is set to 3.0 in + // the constructor, but it can easily be + // changed to a different value greater + // than 1. + const double anisotropic_threshold_ratio; + // This is a bool flag indicating whether + // anisotropic refinement shall be used or + // not. It is set by the constructor, which + // takes an argument of the same name. + const bool anisotropic; + + const QGauss quadrature; + const QGauss face_quadrature; + + Vector solution2; + Vector right_hand_side; + + const DGTransportEquation dg; }; template DGMethod::DGMethod (const bool anisotropic) - : - mapping (), - // Change here for DG - // methods of - // different degrees. - degree(1), - fe (degree), - dof_handler (triangulation), - anisotropic_threshold_ratio(3.), - anisotropic(anisotropic), - // As beta is a - // linear function, - // we can choose the - // degree of the - // quadrature for - // which the - // resulting - // integration is - // correct. Thus, we - // choose to use - // degree+1 - // gauss points, - // which enables us - // to integrate - // exactly - // polynomials of - // degree - // 2*degree+1, - // enough for all the - // integrals we will - // perform in this - // program. - quadrature (degree+1), - face_quadrature (degree+1), - dg () + : + mapping (), + // Change here for DG + // methods of + // different degrees. + degree(1), + fe (degree), + dof_handler (triangulation), + anisotropic_threshold_ratio(3.), + anisotropic(anisotropic), + // As beta is a + // linear function, + // we can choose the + // degree of the + // quadrature for + // which the + // resulting + // integration is + // correct. Thus, we + // choose to use + // degree+1 + // gauss points, + // which enables us + // to integrate + // exactly + // polynomials of + // degree + // 2*degree+1, + // enough for all the + // integrals we will + // perform in this + // program. + quadrature (degree+1), + face_quadrature (degree+1), + dg () {} @@ -471,22 +471,22 @@ namespace Step30 } - // @sect4{Function: assemble_system2} - // - // We proceed with the - // assemble_system2 function that - // implements the DG discretization in its - // second version. This function is very - // similar to the assemble_system2 - // function from step-12, even the four cases - // considered for the neighbor-relations of a - // cell are the same, namely a) cell is at the - // boundary, b) there are finer neighboring - // cells, c) the neighbor is neither coarser - // nor finer and d) the neighbor is coarser. - // However, the way in which we decide upon - // which case we have are modified in the way - // described in the introduction. + // @sect4{Function: assemble_system2} + // + // We proceed with the + // assemble_system2 function that + // implements the DG discretization in its + // second version. This function is very + // similar to the assemble_system2 + // function from step-12, even the four cases + // considered for the neighbor-relations of a + // cell are the same, namely a) cell is at the + // boundary, b) there are finer neighboring + // cells, c) the neighbor is neither coarser + // nor finer and d) the neighbor is coarser. + // However, the way in which we decide upon + // which case we have are modified in the way + // described in the introduction. template void DGMethod::assemble_system2 () { @@ -525,9 +525,9 @@ namespace Step30 Vector cell_vector (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); - for (;cell!=endc; ++cell) + cell = dof_handler.begin_active(), + endc = dof_handler.end(); + for (; cell!=endc; ++cell) { ui_vi_matrix = 0; cell_vector = 0; @@ -545,7 +545,7 @@ namespace Step30 typename DoFHandler::face_iterator face= cell->face(face_no); - // Case a) + // Case a) if (face->at_boundary()) { fe_v_face.reinit (cell, face_no); @@ -560,51 +560,51 @@ namespace Step30 ExcInternalError()); typename DoFHandler::cell_iterator neighbor= cell->neighbor(face_no); - // Case b), we decide that there - // are finer cells as neighbors - // by asking the face, whether it - // has children. if so, then - // there must also be finer cells - // which are children or farther - // offsprings of our neighbor. + // Case b), we decide that there + // are finer cells as neighbors + // by asking the face, whether it + // has children. if so, then + // there must also be finer cells + // which are children or farther + // offsprings of our neighbor. if (face->has_children()) { - // We need to know, which of - // the neighbors faces points - // in the direction of our - // cell. Using the @p - // neighbor_face_no function - // we get this information - // for both coarser and - // non-coarser neighbors. + // We need to know, which of + // the neighbors faces points + // in the direction of our + // cell. Using the @p + // neighbor_face_no function + // we get this information + // for both coarser and + // non-coarser neighbors. const unsigned int neighbor2= cell->neighbor_face_no(face_no); - // Now we loop over all - // subfaces, i.e. the - // children and possibly - // grandchildren of the - // current face. + // Now we loop over all + // subfaces, i.e. the + // children and possibly + // grandchildren of the + // current face. for (unsigned int subface_no=0; subface_nonumber_of_children(); ++subface_no) { - // To get the cell behind - // the current subface we - // can use the @p - // neighbor_child_on_subface - // function. it takes - // care of all the - // complicated situations - // of anisotropic - // refinement and - // non-standard faces. + // To get the cell behind + // the current subface we + // can use the @p + // neighbor_child_on_subface + // function. it takes + // care of all the + // complicated situations + // of anisotropic + // refinement and + // non-standard faces. typename DoFHandler::cell_iterator neighbor_child = cell->neighbor_child_on_subface (face_no, subface_no); Assert (!neighbor_child->has_children(), ExcInternalError()); - // The remaining part of - // this case is - // unchanged. + // The remaining part of + // this case is + // unchanged. ue_vi_matrix = 0; ui_ve_matrix = 0; ue_ve_matrix = 0; @@ -635,42 +635,42 @@ namespace Step30 } else { - // Case c). We simply ask, - // whether the neighbor is - // coarser. If not, then it - // is neither coarser nor - // finer, since any finer - // neighbor would have been - // treated above with case - // b). Of all the cases with - // the same refinement - // situation of our cell and - // the neighbor we want to - // treat only one half, so - // that each face is - // considered only once. Thus - // we have the additional - // condition, that the cell - // with the lower index does - // the work. In the rare case - // that both cells have the - // same index, the cell with - // lower level is selected. + // Case c). We simply ask, + // whether the neighbor is + // coarser. If not, then it + // is neither coarser nor + // finer, since any finer + // neighbor would have been + // treated above with case + // b). Of all the cases with + // the same refinement + // situation of our cell and + // the neighbor we want to + // treat only one half, so + // that each face is + // considered only once. Thus + // we have the additional + // condition, that the cell + // with the lower index does + // the work. In the rare case + // that both cells have the + // same index, the cell with + // lower level is selected. if (!cell->neighbor_is_coarser(face_no) && (neighbor->index() > cell->index() || (neighbor->level() < cell->level() && neighbor->index() == cell->index()))) { - // Here we know, that the - // neigbor is not coarser - // so we can use the - // usual @p - // neighbor_of_neighbor - // function. However, we - // could also use the - // more general @p - // neighbor_face_no - // function. + // Here we know, that the + // neigbor is not coarser + // so we can use the + // usual @p + // neighbor_of_neighbor + // function. However, we + // could also use the + // more general @p + // neighbor_face_no + // function. const unsigned int neighbor2=cell->neighbor_of_neighbor(face_no); ue_vi_matrix = 0; @@ -701,10 +701,10 @@ namespace Step30 } } - // We do not need to consider - // case d), as those faces - // are treated 'from the - // other side within case b). + // We do not need to consider + // case d), as those faces + // are treated 'from the + // other side within case b). } } } @@ -719,12 +719,12 @@ namespace Step30 } - // @sect3{Solver} - // - // For this simple problem we use the simple - // Richardson iteration again. The solver is - // completely unaffected by our anisotropic - // changes. + // @sect3{Solver} + // + // For this simple problem we use the simple + // Richardson iteration again. The solver is + // completely unaffected by our anisotropic + // changes. template void DGMethod::solve (Vector &solution) { @@ -740,68 +740,68 @@ namespace Step30 } - // @sect3{Refinement} - // - // We refine the grid according to the same - // simple refinement criterion used in step-12, - // namely an approximation to the - // gradient of the solution. + // @sect3{Refinement} + // + // We refine the grid according to the same + // simple refinement criterion used in step-12, + // namely an approximation to the + // gradient of the solution. template void DGMethod::refine_grid () { Vector gradient_indicator (triangulation.n_active_cells()); - // We approximate the gradient, + // We approximate the gradient, DerivativeApproximation::approximate_gradient (mapping, dof_handler, solution2, gradient_indicator); - // and scale it to obtain an error indicator. + // and scale it to obtain an error indicator. typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no) gradient_indicator(cell_no)*=std::pow(cell->diameter(), 1+1.0*dim/2); - // Then we use this indicator to flag the 30 - // percent of the cells with highest error - // indicator to be refined. + // Then we use this indicator to flag the 30 + // percent of the cells with highest error + // indicator to be refined. GridRefinement::refine_and_coarsen_fixed_number (triangulation, gradient_indicator, 0.3, 0.1); - // Now the refinement flags are set for those - // cells with a large error indicator. If - // nothing is done to change this, those - // cells will be refined isotropically. If - // the @p anisotropic flag given to this - // function is set, we now call the - // set_anisotropic_flags() function, which - // uses the jump indicator to reset some of - // the refinement flags to anisotropic - // refinement. + // Now the refinement flags are set for those + // cells with a large error indicator. If + // nothing is done to change this, those + // cells will be refined isotropically. If + // the @p anisotropic flag given to this + // function is set, we now call the + // set_anisotropic_flags() function, which + // uses the jump indicator to reset some of + // the refinement flags to anisotropic + // refinement. if (anisotropic) set_anisotropic_flags(); - // Now execute the refinement considering - // anisotropic as well as isotropic - // refinement flags. + // Now execute the refinement considering + // anisotropic as well as isotropic + // refinement flags. triangulation.execute_coarsening_and_refinement (); } - // Once an error indicator has been evaluated - // and the cells with largerst error are - // flagged for refinement we want to loop over - // the flagged cells again to decide whether - // they need isotropic refinemnt or whether - // anisotropic refinement is more - // appropriate. This is the anisotropic jump - // indicator explained in the introduction. + // Once an error indicator has been evaluated + // and the cells with largerst error are + // flagged for refinement we want to loop over + // the flagged cells again to decide whether + // they need isotropic refinemnt or whether + // anisotropic refinement is more + // appropriate. This is the anisotropic jump + // indicator explained in the introduction. template void DGMethod::set_anisotropic_flags () { - // We want to evaluate the jump over faces of - // the flagged cells, so we need some objects - // to evaluate values of the solution on - // faces. + // We want to evaluate the jump over faces of + // the flagged cells, so we need some objects + // to evaluate values of the solution on + // faces. UpdateFlags face_update_flags = UpdateFlags(update_values | update_JxW_values); @@ -809,13 +809,13 @@ namespace Step30 FESubfaceValues fe_v_subface (mapping, fe, face_quadrature, face_update_flags); FEFaceValues fe_v_face_neighbor (mapping, fe, face_quadrature, update_values); - // Now we need to loop over all active cells. + // Now we need to loop over all active cells. typename DoFHandler::active_cell_iterator cell=dof_handler.begin_active(), endc=dof_handler.end(); for (; cell!=endc; ++cell) - // We only need to consider cells which are - // flaged for refinement. + // We only need to consider cells which are + // flaged for refinement. if (cell->refine_flag_set()) { Point jump; @@ -833,92 +833,92 @@ namespace Step30 std::vector u (fe_v_face.n_quadrature_points); std::vector u_neighbor (fe_v_face.n_quadrature_points); - // The four cases of different - // neighbor relations senn in - // the assembly routines are - // repeated much in the same - // way here. + // The four cases of different + // neighbor relations senn in + // the assembly routines are + // repeated much in the same + // way here. if (face->has_children()) { - // The neighbor is refined. - // First we store the - // information, which of - // the neighbor's faces - // points in the direction - // of our current - // cell. This property is - // inherited to the - // children. + // The neighbor is refined. + // First we store the + // information, which of + // the neighbor's faces + // points in the direction + // of our current + // cell. This property is + // inherited to the + // children. unsigned int neighbor2=cell->neighbor_face_no(face_no); - // Now we loop over all subfaces, + // Now we loop over all subfaces, for (unsigned int subface_no=0; subface_nonumber_of_children(); ++subface_no) { - // get an iterator - // pointing to the cell - // behind the present - // subface... + // get an iterator + // pointing to the cell + // behind the present + // subface... typename DoFHandler::cell_iterator neighbor_child = cell->neighbor_child_on_subface(face_no,subface_no); Assert (!neighbor_child->has_children(), ExcInternalError()); - // ... and reinit the - // respective - // FEFaceValues und - // FESubFaceValues - // objects. + // ... and reinit the + // respective + // FEFaceValues und + // FESubFaceValues + // objects. fe_v_subface.reinit (cell, face_no, subface_no); fe_v_face_neighbor.reinit (neighbor_child, neighbor2); - // We obtain the function values + // We obtain the function values fe_v_subface.get_function_values(solution2, u); fe_v_face_neighbor.get_function_values(solution2, u_neighbor); - // as well as the - // quadrature weights, - // multiplied by the - // jacobian determinant. + // as well as the + // quadrature weights, + // multiplied by the + // jacobian determinant. const std::vector &JxW = fe_v_subface.get_JxW_values (); - // Now we loop over all - // quadrature points + // Now we loop over all + // quadrature points for (unsigned int x=0; xdim - // components. + // and integrate + // the absolute + // value of the + // jump of the + // solution, + // i.e. the + // absolute value + // of the + // difference + // between the + // function value + // seen from the + // current cell and + // the neighboring + // cell, + // respectively. We + // know, that the + // first two faces + // are orthogonal + // to the first + // coordinate + // direction on the + // unit cell, the + // second two faces + // are orthogonal + // to the second + // coordinate + // direction and so + // on, so we + // accumulate these + // values ito + // vectors with + // dim + // components. jump[face_no/2]+=std::fabs(u[x]-u_neighbor[x])*JxW[x]; - // We also sum up - // the scaled - // weights to - // obtain the - // measure of the - // face. + // We also sum up + // the scaled + // weights to + // obtain the + // measure of the + // face. area[face_no/2]+=JxW[x]; } } @@ -927,16 +927,16 @@ namespace Step30 { if (!cell->neighbor_is_coarser(face_no)) { - // Our current cell and - // the neighbor have - // the same refinement - // along the face under - // consideration. Apart - // from that, we do - // much the same as - // with one of the - // subcells in the - // above case. + // Our current cell and + // the neighbor have + // the same refinement + // along the face under + // consideration. Apart + // from that, we do + // much the same as + // with one of the + // subcells in the + // above case. unsigned int neighbor2=cell->neighbor_of_neighbor(face_no); fe_v_face.reinit (cell, face_no); @@ -955,26 +955,26 @@ namespace Step30 } else //i.e. neighbor is coarser than cell { - // Now the neighbor is - // actually - // coarser. This case - // is new, in that it - // did not occur in the - // assembly - // routine. Here, we - // have to consider it, - // but this is not - // overly - // complicated. We - // simply use the @p - // neighbor_of_coarser_neighbor - // function, which - // again takes care of - // anisotropic - // refinement and - // non-standard face - // orientation by - // itself. + // Now the neighbor is + // actually + // coarser. This case + // is new, in that it + // did not occur in the + // assembly + // routine. Here, we + // have to consider it, + // but this is not + // overly + // complicated. We + // simply use the @p + // neighbor_of_coarser_neighbor + // function, which + // again takes care of + // anisotropic + // refinement and + // non-standard face + // orientation by + // itself. std::pair neighbor_face_subface = cell->neighbor_of_coarser_neighbor(face_no); Assert (neighbor_face_subface.first::faces_per_cell, ExcInternalError()); @@ -1001,10 +1001,10 @@ namespace Step30 } } } - // Now we analyze the size of the mean - // jumps, which we get dividing the - // jumps by the measure of the - // respective faces. + // Now we analyze the size of the mean + // jumps, which we get dividing the + // jumps by the measure of the + // respective faces. double average_jumps[dim]; double sum_of_average_jumps=0.; for (unsigned int i=0; idim - // coordinate directions of the unit - // cell and compare the average jump - // over the faces orthogional to that - // direction with the average jumnps - // over faces orthogonal to the - // remining direction(s). If the first - // is larger than the latter by a given - // factor, we refine only along hat - // axis. Otherwise we leave the - // refinement flag unchanged, resulting - // in isotropic refinement. + // Now we loop over the dim + // coordinate directions of the unit + // cell and compare the average jump + // over the faces orthogional to that + // direction with the average jumnps + // over faces orthogonal to the + // remining direction(s). If the first + // is larger than the latter by a given + // factor, we refine only along hat + // axis. Otherwise we leave the + // refinement flag unchanged, resulting + // in isotropic refinement. for (unsigned int i=0; i anisotropic_threshold_ratio*(sum_of_average_jumps-average_jumps[i])) cell->set_refine_flag(RefinementCase::cut_axis(i)); } } - // @sect3{The Rest} - // - // The remaining part of the program is again - // unmodified. Only the creation of the - // original triangulation is changed in order - // to reproduce the new domain. + // @sect3{The Rest} + // + // The remaining part of the program is again + // unmodified. Only the creation of the + // original triangulation is changed in order + // to reproduce the new domain. template void DGMethod::output_results (const unsigned int cycle) const { @@ -1095,16 +1095,16 @@ namespace Step30 if (cycle == 0) { - // Create the rectangular domain. + // Create the rectangular domain. Point p1,p2; p1(0)=0; p1(0)=-1; for (unsigned int i=0; i repetitions(dim,1); repetitions[0]=2; GridGenerator::subdivided_hyper_rectangle (triangulation, @@ -1149,14 +1149,14 @@ int main () using namespace dealii; using namespace Step30; - // If you want to run the program in 3D, - // simply change the following line to - // const unsigned int dim = 3;. + // If you want to run the program in 3D, + // simply change the following line to + // const unsigned int dim = 3;. const unsigned int dim = 2; { - // First, we perform a run with - // isotropic refinement. + // First, we perform a run with + // isotropic refinement. std::cout << "Performing a " << dim << "D run with isotropic refinement..." << std::endl << "------------------------------------------------" << std::endl; DGMethod dgmethod_iso(false); @@ -1164,8 +1164,8 @@ int main () } { - // Now we do a second run, this time - // with anisotropic refinement. + // Now we do a second run, this time + // with anisotropic refinement. std::cout << std::endl << "Performing a " << dim << "D run with anisotropic refinement..." << std::endl << "--------------------------------------------------" << std::endl; diff --git a/deal.II/examples/step-31/step-31.cc b/deal.II/examples/step-31/step-31.cc index 1db324114d..033ff91776 100644 --- a/deal.II/examples/step-31/step-31.cc +++ b/deal.II/examples/step-31/step-31.cc @@ -10,12 +10,12 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // The first step, as always, is to include - // the functionality of these well-known - // deal.II library files and some C++ header - // files. +// The first step, as always, is to include +// the functionality of these well-known +// deal.II library files and some C++ header +// files. #include #include #include @@ -47,94 +47,94 @@ #include #include - // Then we need to include some header files - // that provide vector, matrix, and - // preconditioner classes that implement - // interfaces to the respective Trilinos - // classes. In particular, we will need - // interfaces to the matrix and vector - // classes based on Trilinos as well as - // Trilinos preconditioners: +// Then we need to include some header files +// that provide vector, matrix, and +// preconditioner classes that implement +// interfaces to the respective Trilinos +// classes. In particular, we will need +// interfaces to the matrix and vector +// classes based on Trilinos as well as +// Trilinos preconditioners: #include #include #include #include #include - // Finally, here are two C++ headers that - // haven't been included yet by one of the - // aforelisted header files: +// Finally, here are two C++ headers that +// haven't been included yet by one of the +// aforelisted header files: #include #include #include - // At the end of this top-matter, we import - // all deal.II names into the global - // namespace: +// At the end of this top-matter, we import +// all deal.II names into the global +// namespace: namespace Step31 { using namespace dealii; - // @sect3{Equation data} - - // Again, the next stage in the program is - // the definition of the equation data, that - // is, the various boundary conditions, the - // right hand sides and the initial condition - // (remember that we're about to solve a - // time-dependent system). The basic strategy - // for this definition is the same as in - // step-22. Regarding the details, though, - // there are some differences. - - // The first thing is that we don't set any - // non-homogenous boundary conditions on the - // velocity, since as is explained in the - // introduction we will use no-flux - // conditions - // $\mathbf{n}\cdot\mathbf{u}=0$. So what is - // left are dim-1 conditions for - // the tangential part of the normal - // component of the stress tensor, - // $\textbf{n} \cdot [p \textbf{1} - - // \eta\varepsilon(\textbf{u})]$; we assume - // homogenous values for these components, - // i.e. a natural boundary condition that - // requires no specific action (it appears as - // a zero term in the right hand side of the - // weak form). - // - // For the temperature T, we assume no - // thermal energy flux, i.e. $\mathbf{n} - // \cdot \kappa \nabla T=0$. This, again, is - // a boundary condition that does not require - // us to do anything in particular. - // - // Secondly, we have to set initial - // conditions for the temperature (no initial - // conditions are required for the velocity - // and pressure, since the Stokes equations - // for the quasi-stationary case we consider - // here have no time derivatives of the - // velocity or pressure). Here, we choose a - // very simple test case, where the initial - // temperature is zero, and all dynamics are - // driven by the temperature right hand side. - // - // Thirdly, we need to define the right hand - // side of the temperature equation. We - // choose it to be constant within three - // circles (or spheres in 3d) somewhere at - // the bottom of the domain, as explained in - // the introduction, and zero outside. - // - // Finally, or maybe firstly, at the top of - // this namespace, we define the various - // material constants we need ($\eta,\kappa$, - // density $\rho$ and the thermal expansion - // coefficient $\beta$): + // @sect3{Equation data} + + // Again, the next stage in the program is + // the definition of the equation data, that + // is, the various boundary conditions, the + // right hand sides and the initial condition + // (remember that we're about to solve a + // time-dependent system). The basic strategy + // for this definition is the same as in + // step-22. Regarding the details, though, + // there are some differences. + + // The first thing is that we don't set any + // non-homogenous boundary conditions on the + // velocity, since as is explained in the + // introduction we will use no-flux + // conditions + // $\mathbf{n}\cdot\mathbf{u}=0$. So what is + // left are dim-1 conditions for + // the tangential part of the normal + // component of the stress tensor, + // $\textbf{n} \cdot [p \textbf{1} - + // \eta\varepsilon(\textbf{u})]$; we assume + // homogenous values for these components, + // i.e. a natural boundary condition that + // requires no specific action (it appears as + // a zero term in the right hand side of the + // weak form). + // + // For the temperature T, we assume no + // thermal energy flux, i.e. $\mathbf{n} + // \cdot \kappa \nabla T=0$. This, again, is + // a boundary condition that does not require + // us to do anything in particular. + // + // Secondly, we have to set initial + // conditions for the temperature (no initial + // conditions are required for the velocity + // and pressure, since the Stokes equations + // for the quasi-stationary case we consider + // here have no time derivatives of the + // velocity or pressure). Here, we choose a + // very simple test case, where the initial + // temperature is zero, and all dynamics are + // driven by the temperature right hand side. + // + // Thirdly, we need to define the right hand + // side of the temperature equation. We + // choose it to be constant within three + // circles (or spheres in 3d) somewhere at + // the bottom of the domain, as explained in + // the introduction, and zero outside. + // + // Finally, or maybe firstly, at the top of + // this namespace, we define the various + // material constants we need ($\eta,\kappa$, + // density $\rho$ and the thermal expansion + // coefficient $\beta$): namespace EquationData { const double eta = 1; @@ -146,21 +146,21 @@ namespace Step31 template class TemperatureInitialValues : public Function { - public: - TemperatureInitialValues () : Function(1) {} + public: + TemperatureInitialValues () : Function(1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void vector_value (const Point &p, - Vector &value) const; + virtual void vector_value (const Point &p, + Vector &value) const; }; template double - TemperatureInitialValues::value (const Point &, - const unsigned int) const + TemperatureInitialValues::value (const Point &, + const unsigned int) const { return 0; } @@ -169,178 +169,179 @@ namespace Step31 template void TemperatureInitialValues::vector_value (const Point &p, - Vector &values) const + Vector &values) const { for (unsigned int c=0; cn_components; ++c) - values(c) = TemperatureInitialValues::value (p, c); + values(c) = TemperatureInitialValues::value (p, c); } template class TemperatureRightHandSide : public Function { - public: - TemperatureRightHandSide () : Function(1) {} + public: + TemperatureRightHandSide () : Function(1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void vector_value (const Point &p, - Vector &value) const; + virtual void vector_value (const Point &p, + Vector &value) const; }; template double TemperatureRightHandSide::value (const Point &p, - const unsigned int component) const + const unsigned int component) const { Assert (component == 0, - ExcMessage ("Invalid operation for a scalar function.")); + ExcMessage ("Invalid operation for a scalar function.")); Assert ((dim==2) || (dim==3), ExcNotImplemented()); static const Point source_centers[3] - = { (dim == 2 ? Point(.3,.1) : Point(.3,.5,.1)), - (dim == 2 ? Point(.45,.1) : Point(.45,.5,.1)), - (dim == 2 ? Point(.75,.1) : Point(.75,.5,.1)) }; + = { (dim == 2 ? Point(.3,.1) : Point(.3,.5,.1)), + (dim == 2 ? Point(.45,.1) : Point(.45,.5,.1)), + (dim == 2 ? Point(.75,.1) : Point(.75,.5,.1)) + }; static const double source_radius - = (dim == 2 ? 1./32 : 1./8); + = (dim == 2 ? 1./32 : 1./8); return ((source_centers[0].distance (p) < source_radius) - || - (source_centers[1].distance (p) < source_radius) - || - (source_centers[2].distance (p) < source_radius) - ? - 1 - : - 0); + || + (source_centers[1].distance (p) < source_radius) + || + (source_centers[2].distance (p) < source_radius) + ? + 1 + : + 0); } template void TemperatureRightHandSide::vector_value (const Point &p, - Vector &values) const + Vector &values) const { for (unsigned int c=0; cn_components; ++c) - values(c) = TemperatureRightHandSide::value (p, c); + values(c) = TemperatureRightHandSide::value (p, c); } } - // @sect3{Linear solvers and preconditioners} - - // This section introduces some objects - // that are used for the solution of the - // linear equations of the Stokes system - // that we need to solve in each time - // step. Many of the ideas used here are - // the same as in step-20, where Schur - // complement based preconditioners and - // solvers have been introduced, with the - // actual interface taken from step-22 (in - // particular the discussion in the - // "Results" section of step-22, in which - // we introduce alternatives to the direct - // Schur complement approach). Note, - // however, that here we don't use the - // Schur complement to solve the Stokes - // equations, though an approximate Schur - // complement (the mass matrix on the - // pressure space) appears in the - // preconditioner. + // @sect3{Linear solvers and preconditioners} + + // This section introduces some objects + // that are used for the solution of the + // linear equations of the Stokes system + // that we need to solve in each time + // step. Many of the ideas used here are + // the same as in step-20, where Schur + // complement based preconditioners and + // solvers have been introduced, with the + // actual interface taken from step-22 (in + // particular the discussion in the + // "Results" section of step-22, in which + // we introduce alternatives to the direct + // Schur complement approach). Note, + // however, that here we don't use the + // Schur complement to solve the Stokes + // equations, though an approximate Schur + // complement (the mass matrix on the + // pressure space) appears in the + // preconditioner. namespace LinearSolvers { - // @sect4{The InverseMatrix class template} - - // This class is an interface to - // calculate the action of an - // "inverted" matrix on a vector - // (using the vmult - // operation) in the same way as - // the corresponding class in - // step-22: when the product of an - // object of this class is - // requested, we solve a linear - // equation system with that matrix - // using the CG method, accelerated - // by a preconditioner of - // (templated) class - // Preconditioner. - // - // In a minor deviation from the - // implementation of the same class in - // step-22 (and step-20), we make the - // vmult function take any - // kind of vector type (it will yield - // compiler errors, however, if the matrix - // does not allow a matrix-vector product - // with this kind of vector). - // - // Secondly, we catch any exceptions that - // the solver may have thrown. The reason - // is as follows: When debugging a program - // like this one occasionally makes a - // mistake of passing an indefinite or - // non-symmetric matrix or preconditioner - // to the current class. The solver will, - // in that case, not converge and throw a - // run-time exception. If not caught here - // it will propagate up the call stack and - // may end up in main() where - // we output an error message that will say - // that the CG solver failed. The question - // then becomes: Which CG solver? The one - // that inverted the mass matrix? The one - // that inverted the top left block with - // the Laplace operator? Or a CG solver in - // one of the several other nested places - // where we use linear solvers in the - // current code? No indication about this - // is present in a run-time exception - // because it doesn't store the stack of - // calls through which we got to the place - // where the exception was generated. - // - // So rather than letting the exception - // propagate freely up to - // main() we realize that - // there is little that an outer function - // can do if the inner solver fails and - // rather convert the run-time exception - // into an assertion that fails and - // triggers a call to abort(), - // allowing us to trace back in a debugger - // how we got to the current place. + // @sect4{The InverseMatrix class template} + + // This class is an interface to + // calculate the action of an + // "inverted" matrix on a vector + // (using the vmult + // operation) in the same way as + // the corresponding class in + // step-22: when the product of an + // object of this class is + // requested, we solve a linear + // equation system with that matrix + // using the CG method, accelerated + // by a preconditioner of + // (templated) class + // Preconditioner. + // + // In a minor deviation from the + // implementation of the same class in + // step-22 (and step-20), we make the + // vmult function take any + // kind of vector type (it will yield + // compiler errors, however, if the matrix + // does not allow a matrix-vector product + // with this kind of vector). + // + // Secondly, we catch any exceptions that + // the solver may have thrown. The reason + // is as follows: When debugging a program + // like this one occasionally makes a + // mistake of passing an indefinite or + // non-symmetric matrix or preconditioner + // to the current class. The solver will, + // in that case, not converge and throw a + // run-time exception. If not caught here + // it will propagate up the call stack and + // may end up in main() where + // we output an error message that will say + // that the CG solver failed. The question + // then becomes: Which CG solver? The one + // that inverted the mass matrix? The one + // that inverted the top left block with + // the Laplace operator? Or a CG solver in + // one of the several other nested places + // where we use linear solvers in the + // current code? No indication about this + // is present in a run-time exception + // because it doesn't store the stack of + // calls through which we got to the place + // where the exception was generated. + // + // So rather than letting the exception + // propagate freely up to + // main() we realize that + // there is little that an outer function + // can do if the inner solver fails and + // rather convert the run-time exception + // into an assertion that fails and + // triggers a call to abort(), + // allowing us to trace back in a debugger + // how we got to the current place. template class InverseMatrix : public Subscriptor { - public: - InverseMatrix (const Matrix &m, - const Preconditioner &preconditioner); + public: + InverseMatrix (const Matrix &m, + const Preconditioner &preconditioner); - template - void vmult (VectorType &dst, - const VectorType &src) const; + template + void vmult (VectorType &dst, + const VectorType &src) const; - private: - const SmartPointer matrix; - const Preconditioner &preconditioner; + private: + const SmartPointer matrix; + const Preconditioner &preconditioner; }; template InverseMatrix:: InverseMatrix (const Matrix &m, - const Preconditioner &preconditioner) - : - matrix (&m), - preconditioner (preconditioner) + const Preconditioner &preconditioner) + : + matrix (&m), + preconditioner (preconditioner) {} @@ -350,7 +351,7 @@ namespace Step31 void InverseMatrix:: vmult (VectorType &dst, - const VectorType &src) const + const VectorType &src) const { SolverControl solver_control (src.size(), 1e-7*src.l2_norm()); SolverCG cg (solver_control); @@ -358,130 +359,130 @@ namespace Step31 dst = 0; try - { - cg.solve (*matrix, dst, src, preconditioner); - } + { + cg.solve (*matrix, dst, src, preconditioner); + } catch (std::exception &e) - { - Assert (false, ExcMessage(e.what())); - } + { + Assert (false, ExcMessage(e.what())); + } } - // @sect4{Schur complement preconditioner} - - // This is the implementation of the - // Schur complement preconditioner as - // described in detail in the - // introduction. As opposed to step-20 - // and step-22, we solve the block system - // all-at-once using GMRES, and use the - // Schur complement of the block - // structured matrix to build a good - // preconditioner instead. - // - // Let's have a look at the ideal - // preconditioner matrix - // $P=\left(\begin{array}{cc} A & 0 \\ B - // & -S \end{array}\right)$ described in - // the introduction. If we apply this - // matrix in the solution of a linear - // system, convergence of an iterative - // GMRES solver will be governed by the - // matrix - // @f{eqnarray*} - // P^{-1}\left(\begin{array}{cc} A - // & B^T \\ B & 0 - // \end{array}\right) = - // \left(\begin{array}{cc} I & - // A^{-1} B^T \\ 0 & I - // \end{array}\right), - // @f} - // which indeed is very simple. A GMRES - // solver based on exact matrices would - // converge in one iteration, since all - // eigenvalues are equal (any Krylov - // method takes at most as many - // iterations as there are distinct - // eigenvalues). Such a preconditioner - // for the blocked Stokes system has been - // proposed by Silvester and Wathen - // ("Fast iterative solution of - // stabilised Stokes systems part II. - // Using general block preconditioners", - // SIAM J. Numer. Anal., 31 (1994), - // pp. 1352-1367). - // - // Replacing P by $\tilde{P}$ - // keeps that spirit alive: the product - // $P^{-1} A$ will still be close to a - // matrix with eigenvalues 1 with a - // distribution that does not depend on - // the problem size. This lets us hope to - // be able to get a number of GMRES - // iterations that is problem-size - // independent. - // - // The deal.II users who have already - // gone through the step-20 and step-22 - // tutorials can certainly imagine how - // we're going to implement this. We - // replace the exact inverse matrices in - // $P^{-1}$ by some approximate inverses - // built from the InverseMatrix class, - // and the inverse Schur complement will - // be approximated by the pressure mass - // matrix $M_p$ (weighted by $\eta^{-1}$ - // as mentioned in the introduction). As - // pointed out in the results section of - // step-22, we can replace the exact - // inverse of A by just the - // application of a preconditioner, in - // this case on a vector Laplace matrix - // as was explained in the - // introduction. This does increase the - // number of (outer) GMRES iterations, - // but is still significantly cheaper - // than an exact inverse, which would - // require between 20 and 35 CG - // iterations for each outer - // solver step (using the AMG - // preconditioner). - // - // Having the above explanations in mind, - // we define a preconditioner class with - // a vmult functionality, - // which is all we need for the - // interaction with the usual solver - // functions further below in the program - // code. - // - // First the declarations. These are - // similar to the definition of the Schur - // complement in step-20, with the - // difference that we need some more - // preconditioners in the constructor and - // that the matrices we use here are - // built upon Trilinos: + // @sect4{Schur complement preconditioner} + + // This is the implementation of the + // Schur complement preconditioner as + // described in detail in the + // introduction. As opposed to step-20 + // and step-22, we solve the block system + // all-at-once using GMRES, and use the + // Schur complement of the block + // structured matrix to build a good + // preconditioner instead. + // + // Let's have a look at the ideal + // preconditioner matrix + // $P=\left(\begin{array}{cc} A & 0 \\ B + // & -S \end{array}\right)$ described in + // the introduction. If we apply this + // matrix in the solution of a linear + // system, convergence of an iterative + // GMRES solver will be governed by the + // matrix + // @f{eqnarray*} + // P^{-1}\left(\begin{array}{cc} A + // & B^T \\ B & 0 + // \end{array}\right) = + // \left(\begin{array}{cc} I & + // A^{-1} B^T \\ 0 & I + // \end{array}\right), + // @f} + // which indeed is very simple. A GMRES + // solver based on exact matrices would + // converge in one iteration, since all + // eigenvalues are equal (any Krylov + // method takes at most as many + // iterations as there are distinct + // eigenvalues). Such a preconditioner + // for the blocked Stokes system has been + // proposed by Silvester and Wathen + // ("Fast iterative solution of + // stabilised Stokes systems part II. + // Using general block preconditioners", + // SIAM J. Numer. Anal., 31 (1994), + // pp. 1352-1367). + // + // Replacing P by $\tilde{P}$ + // keeps that spirit alive: the product + // $P^{-1} A$ will still be close to a + // matrix with eigenvalues 1 with a + // distribution that does not depend on + // the problem size. This lets us hope to + // be able to get a number of GMRES + // iterations that is problem-size + // independent. + // + // The deal.II users who have already + // gone through the step-20 and step-22 + // tutorials can certainly imagine how + // we're going to implement this. We + // replace the exact inverse matrices in + // $P^{-1}$ by some approximate inverses + // built from the InverseMatrix class, + // and the inverse Schur complement will + // be approximated by the pressure mass + // matrix $M_p$ (weighted by $\eta^{-1}$ + // as mentioned in the introduction). As + // pointed out in the results section of + // step-22, we can replace the exact + // inverse of A by just the + // application of a preconditioner, in + // this case on a vector Laplace matrix + // as was explained in the + // introduction. This does increase the + // number of (outer) GMRES iterations, + // but is still significantly cheaper + // than an exact inverse, which would + // require between 20 and 35 CG + // iterations for each outer + // solver step (using the AMG + // preconditioner). + // + // Having the above explanations in mind, + // we define a preconditioner class with + // a vmult functionality, + // which is all we need for the + // interaction with the usual solver + // functions further below in the program + // code. + // + // First the declarations. These are + // similar to the definition of the Schur + // complement in step-20, with the + // difference that we need some more + // preconditioners in the constructor and + // that the matrices we use here are + // built upon Trilinos: template class BlockSchurPreconditioner : public Subscriptor { - public: - BlockSchurPreconditioner ( - const TrilinosWrappers::BlockSparseMatrix &S, - const InverseMatrix &Mpinv, - const PreconditionerA &Apreconditioner); - - void vmult (TrilinosWrappers::BlockVector &dst, - const TrilinosWrappers::BlockVector &src) const; - - private: - const SmartPointer stokes_matrix; - const SmartPointer > m_inverse; - const PreconditionerA &a_preconditioner; - - mutable TrilinosWrappers::Vector tmp; + public: + BlockSchurPreconditioner ( + const TrilinosWrappers::BlockSparseMatrix &S, + const InverseMatrix &Mpinv, + const PreconditionerA &Apreconditioner); + + void vmult (TrilinosWrappers::BlockVector &dst, + const TrilinosWrappers::BlockVector &src) const; + + private: + const SmartPointer stokes_matrix; + const SmartPointer > m_inverse; + const PreconditionerA &a_preconditioner; + + mutable TrilinosWrappers::Vector tmp; }; @@ -489,46 +490,46 @@ namespace Step31 template BlockSchurPreconditioner:: BlockSchurPreconditioner(const TrilinosWrappers::BlockSparseMatrix &S, - const InverseMatrix &Mpinv, - const PreconditionerA &Apreconditioner) - : - stokes_matrix (&S), - m_inverse (&Mpinv), - a_preconditioner (Apreconditioner), - tmp (stokes_matrix->block(1,1).m()) + const InverseMatrix &Mpinv, + const PreconditionerA &Apreconditioner) + : + stokes_matrix (&S), + m_inverse (&Mpinv), + a_preconditioner (Apreconditioner), + tmp (stokes_matrix->block(1,1).m()) {} - // Next is the vmult - // function. We implement the action of - // $P^{-1}$ as described above in three - // successive steps. In formulas, we want - // to compute $Y=P^{-1}X$ where $X,Y$ are - // both vectors with two block components. - // - // The first step multiplies the velocity - // part of the vector by a preconditioner - // of the matrix A, i.e. we compute - // $Y_0={\tilde A}^{-1}X_0$. The resulting - // velocity vector is then multiplied by - // $B$ and subtracted from the pressure, - // i.e. we want to compute $X_1-BY_0$. - // This second step only acts on the - // pressure vector and is accomplished by - // the residual function of our matrix - // classes, except that the sign is - // wrong. Consequently, we change the sign - // in the temporary pressure vector and - // finally multiply by the inverse pressure - // mass matrix to get the final pressure - // vector, completing our work on the - // Stokes preconditioner: + // Next is the vmult + // function. We implement the action of + // $P^{-1}$ as described above in three + // successive steps. In formulas, we want + // to compute $Y=P^{-1}X$ where $X,Y$ are + // both vectors with two block components. + // + // The first step multiplies the velocity + // part of the vector by a preconditioner + // of the matrix A, i.e. we compute + // $Y_0={\tilde A}^{-1}X_0$. The resulting + // velocity vector is then multiplied by + // $B$ and subtracted from the pressure, + // i.e. we want to compute $X_1-BY_0$. + // This second step only acts on the + // pressure vector and is accomplished by + // the residual function of our matrix + // classes, except that the sign is + // wrong. Consequently, we change the sign + // in the temporary pressure vector and + // finally multiply by the inverse pressure + // mass matrix to get the final pressure + // vector, completing our work on the + // Stokes preconditioner: template void BlockSchurPreconditioner:: vmult (TrilinosWrappers::BlockVector &dst, - const TrilinosWrappers::BlockVector &src) const + const TrilinosWrappers::BlockVector &src) const { a_preconditioner.vmult (dst.block(0), src.block(0)); stokes_matrix->block(1,0).residual(tmp, dst.block(0), src.block(1)); @@ -539,248 +540,248 @@ namespace Step31 - // @sect3{The BoussinesqFlowProblem class template} - - // The definition of the class that defines - // the top-level logic of solving the - // time-dependent Boussinesq problem is - // mainly based on the step-22 tutorial - // program. The main differences are that now - // we also have to solve for the temperature - // equation, which forces us to have a second - // DoFHandler object for the temperature - // variable as well as matrices, right hand - // sides, and solution vectors for the - // current and previous time steps. As - // mentioned in the introduction, all linear - // algebra objects are going to use wrappers - // of the corresponding Trilinos - // functionality. - // - // The member functions of this class are - // reminiscent of step-21, where we also used - // a staggered scheme that first solve the - // flow equations (here the Stokes equations, - // in step-21 Darcy flow) and then update - // the advected quantity (here the - // temperature, there the saturation). The - // functions that are new are mainly - // concerned with determining the time step, - // as well as the proper size of the - // artificial viscosity stabilization. - // - // The last three variables indicate whether - // the various matrices or preconditioners - // need to be rebuilt the next time the - // corresponding build functions are - // called. This allows us to move the - // corresponding if into the - // respective function and thereby keeping - // our main run() function clean - // and easy to read. + // @sect3{The BoussinesqFlowProblem class template} + + // The definition of the class that defines + // the top-level logic of solving the + // time-dependent Boussinesq problem is + // mainly based on the step-22 tutorial + // program. The main differences are that now + // we also have to solve for the temperature + // equation, which forces us to have a second + // DoFHandler object for the temperature + // variable as well as matrices, right hand + // sides, and solution vectors for the + // current and previous time steps. As + // mentioned in the introduction, all linear + // algebra objects are going to use wrappers + // of the corresponding Trilinos + // functionality. + // + // The member functions of this class are + // reminiscent of step-21, where we also used + // a staggered scheme that first solve the + // flow equations (here the Stokes equations, + // in step-21 Darcy flow) and then update + // the advected quantity (here the + // temperature, there the saturation). The + // functions that are new are mainly + // concerned with determining the time step, + // as well as the proper size of the + // artificial viscosity stabilization. + // + // The last three variables indicate whether + // the various matrices or preconditioners + // need to be rebuilt the next time the + // corresponding build functions are + // called. This allows us to move the + // corresponding if into the + // respective function and thereby keeping + // our main run() function clean + // and easy to read. template class BoussinesqFlowProblem { - public: - BoussinesqFlowProblem (); - void run (); + public: + BoussinesqFlowProblem (); + void run (); + + private: + void setup_dofs (); + void assemble_stokes_preconditioner (); + void build_stokes_preconditioner (); + void assemble_stokes_system (); + void assemble_temperature_system (const double maximal_velocity); + void assemble_temperature_matrix (); + double get_maximal_velocity () const; + std::pair get_extrapolated_temperature_range () const; + void solve (); + void output_results () const; + void refine_mesh (const unsigned int max_grid_level); - private: - void setup_dofs (); - void assemble_stokes_preconditioner (); - void build_stokes_preconditioner (); - void assemble_stokes_system (); - void assemble_temperature_system (const double maximal_velocity); - void assemble_temperature_matrix (); - double get_maximal_velocity () const; - std::pair get_extrapolated_temperature_range () const; - void solve (); - void output_results () const; - void refine_mesh (const unsigned int max_grid_level); - - double - compute_viscosity(const std::vector &old_temperature, - const std::vector &old_old_temperature, - const std::vector > &old_temperature_grads, - const std::vector > &old_old_temperature_grads, - const std::vector &old_temperature_laplacians, - const std::vector &old_old_temperature_laplacians, - const std::vector > &old_velocity_values, - const std::vector > &old_old_velocity_values, - const std::vector &gamma_values, - const double global_u_infty, - const double global_T_variation, - const double cell_diameter) const; - - - Triangulation triangulation; - double global_Omega_diameter; - - const unsigned int stokes_degree; - FESystem stokes_fe; - DoFHandler stokes_dof_handler; - ConstraintMatrix stokes_constraints; - - std::vector stokes_block_sizes; - TrilinosWrappers::BlockSparseMatrix stokes_matrix; - TrilinosWrappers::BlockSparseMatrix stokes_preconditioner_matrix; - - TrilinosWrappers::BlockVector stokes_solution; - TrilinosWrappers::BlockVector old_stokes_solution; - TrilinosWrappers::BlockVector stokes_rhs; - - - const unsigned int temperature_degree; - FE_Q temperature_fe; - DoFHandler temperature_dof_handler; - ConstraintMatrix temperature_constraints; - - TrilinosWrappers::SparseMatrix temperature_mass_matrix; - TrilinosWrappers::SparseMatrix temperature_stiffness_matrix; - TrilinosWrappers::SparseMatrix temperature_matrix; - - TrilinosWrappers::Vector temperature_solution; - TrilinosWrappers::Vector old_temperature_solution; - TrilinosWrappers::Vector old_old_temperature_solution; - TrilinosWrappers::Vector temperature_rhs; - - - double time_step; - double old_time_step; - unsigned int timestep_number; - - std_cxx1x::shared_ptr Amg_preconditioner; - std_cxx1x::shared_ptr Mp_preconditioner; - - bool rebuild_stokes_matrix; - bool rebuild_temperature_matrices; - bool rebuild_stokes_preconditioner; + double + compute_viscosity(const std::vector &old_temperature, + const std::vector &old_old_temperature, + const std::vector > &old_temperature_grads, + const std::vector > &old_old_temperature_grads, + const std::vector &old_temperature_laplacians, + const std::vector &old_old_temperature_laplacians, + const std::vector > &old_velocity_values, + const std::vector > &old_old_velocity_values, + const std::vector &gamma_values, + const double global_u_infty, + const double global_T_variation, + const double cell_diameter) const; + + + Triangulation triangulation; + double global_Omega_diameter; + + const unsigned int stokes_degree; + FESystem stokes_fe; + DoFHandler stokes_dof_handler; + ConstraintMatrix stokes_constraints; + + std::vector stokes_block_sizes; + TrilinosWrappers::BlockSparseMatrix stokes_matrix; + TrilinosWrappers::BlockSparseMatrix stokes_preconditioner_matrix; + + TrilinosWrappers::BlockVector stokes_solution; + TrilinosWrappers::BlockVector old_stokes_solution; + TrilinosWrappers::BlockVector stokes_rhs; + + + const unsigned int temperature_degree; + FE_Q temperature_fe; + DoFHandler temperature_dof_handler; + ConstraintMatrix temperature_constraints; + + TrilinosWrappers::SparseMatrix temperature_mass_matrix; + TrilinosWrappers::SparseMatrix temperature_stiffness_matrix; + TrilinosWrappers::SparseMatrix temperature_matrix; + + TrilinosWrappers::Vector temperature_solution; + TrilinosWrappers::Vector old_temperature_solution; + TrilinosWrappers::Vector old_old_temperature_solution; + TrilinosWrappers::Vector temperature_rhs; + + + double time_step; + double old_time_step; + unsigned int timestep_number; + + std_cxx1x::shared_ptr Amg_preconditioner; + std_cxx1x::shared_ptr Mp_preconditioner; + + bool rebuild_stokes_matrix; + bool rebuild_temperature_matrices; + bool rebuild_stokes_preconditioner; }; - // @sect3{BoussinesqFlowProblem class implementation} - - // @sect4{BoussinesqFlowProblem::BoussinesqFlowProblem} - // - // The constructor of this class is an - // extension of the constructor in - // step-22. We need to add the various - // variables that concern the temperature. As - // discussed in the introduction, we are - // going to use $Q_2\times Q_1$ (Taylor-Hood) - // elements again for the Stokes part, and - // $Q_2$ elements for the - // temperature. However, by using variables - // that store the polynomial degree of the - // Stokes and temperature finite elements, it - // is easy to consistently modify the degree - // of the elements as well as all quadrature - // formulas used on them - // downstream. Moreover, we initialize the - // time stepping as well as the options for - // matrix assembly and preconditioning: + // @sect3{BoussinesqFlowProblem class implementation} + + // @sect4{BoussinesqFlowProblem::BoussinesqFlowProblem} + // + // The constructor of this class is an + // extension of the constructor in + // step-22. We need to add the various + // variables that concern the temperature. As + // discussed in the introduction, we are + // going to use $Q_2\times Q_1$ (Taylor-Hood) + // elements again for the Stokes part, and + // $Q_2$ elements for the + // temperature. However, by using variables + // that store the polynomial degree of the + // Stokes and temperature finite elements, it + // is easy to consistently modify the degree + // of the elements as well as all quadrature + // formulas used on them + // downstream. Moreover, we initialize the + // time stepping as well as the options for + // matrix assembly and preconditioning: template BoussinesqFlowProblem::BoussinesqFlowProblem () - : - triangulation (Triangulation::maximum_smoothing), - - stokes_degree (1), - stokes_fe (FE_Q(stokes_degree+1), dim, - FE_Q(stokes_degree), 1), - stokes_dof_handler (triangulation), - - temperature_degree (2), - temperature_fe (temperature_degree), - temperature_dof_handler (triangulation), - - time_step (0), - old_time_step (0), - timestep_number (0), - rebuild_stokes_matrix (true), - rebuild_temperature_matrices (true), - rebuild_stokes_preconditioner (true) + : + triangulation (Triangulation::maximum_smoothing), + + stokes_degree (1), + stokes_fe (FE_Q(stokes_degree+1), dim, + FE_Q(stokes_degree), 1), + stokes_dof_handler (triangulation), + + temperature_degree (2), + temperature_fe (temperature_degree), + temperature_dof_handler (triangulation), + + time_step (0), + old_time_step (0), + timestep_number (0), + rebuild_stokes_matrix (true), + rebuild_temperature_matrices (true), + rebuild_stokes_preconditioner (true) {} - // @sect4{BoussinesqFlowProblem::get_maximal_velocity} - - // Starting the real functionality of this - // class is a helper function that determines - // the maximum ($L_\infty$) velocity in the - // domain (at the quadrature points, in - // fact). How it works should be relatively - // obvious to all who have gotten to this - // point of the tutorial. Note that since we - // are only interested in the velocity, - // rather than using - // stokes_fe_values.get_function_values - // to get the values of the entire Stokes - // solution (velocities and pressures) we use - // stokes_fe_values[velocities].get_function_values - // to extract only the velocities part. This - // has the additional benefit that we get it - // as a Tensor<1,dim>, rather than some - // components in a Vector, allowing - // us to process it right away using the - // norm() function to get the - // magnitude of the velocity. - // - // The only point worth thinking about a bit - // is how to choose the quadrature points we - // use here. Since the goal of this function - // is to find the maximal velocity over a - // domain by looking at quadrature points on - // each cell. So we should ask how we should - // best choose these quadrature points on - // each cell. To this end, recall that if we - // had a single $Q_1$ field (rather than the - // vector-valued field of higher order) then - // the maximum would be attained at a vertex - // of the mesh. In other words, we should use - // the QTrapez class that has quadrature - // points only at the vertices of cells. - // - // For higher order shape functions, the - // situation is more complicated: the maxima - // and minima may be attained at points - // between the support points of shape - // functions (for the usual $Q_p$ elements - // the support points are the equidistant - // Lagrange interpolation points); - // furthermore, since we are looking for the - // maximum magnitude of a vector-valued - // quantity, we can even less say with - // certainty where the set of potential - // maximal points are. Nevertheless, - // intuitively if not provably, the Lagrange - // interpolation points appear to be a better - // choice than the Gauss points. - // - // There are now different methods to produce - // a quadrature formula with quadrature - // points equal to the interpolation points - // of the finite element. One option would be - // to use the - // FiniteElement::get_unit_support_points() - // function, reduce the output to a unique - // set of points to avoid duplicate function - // evaluations, and create a Quadrature - // object using these points. Another option, - // chosen here, is to use the QTrapez class - // and combine it with the QIterated class - // that repeats the QTrapez formula on a - // number of sub-cells in each coordinate - // direction. To cover all support points, we - // need to iterate it - // stokes_degree+1 times since - // this is the polynomial degree of the - // Stokes element in use: + // @sect4{BoussinesqFlowProblem::get_maximal_velocity} + + // Starting the real functionality of this + // class is a helper function that determines + // the maximum ($L_\infty$) velocity in the + // domain (at the quadrature points, in + // fact). How it works should be relatively + // obvious to all who have gotten to this + // point of the tutorial. Note that since we + // are only interested in the velocity, + // rather than using + // stokes_fe_values.get_function_values + // to get the values of the entire Stokes + // solution (velocities and pressures) we use + // stokes_fe_values[velocities].get_function_values + // to extract only the velocities part. This + // has the additional benefit that we get it + // as a Tensor<1,dim>, rather than some + // components in a Vector, allowing + // us to process it right away using the + // norm() function to get the + // magnitude of the velocity. + // + // The only point worth thinking about a bit + // is how to choose the quadrature points we + // use here. Since the goal of this function + // is to find the maximal velocity over a + // domain by looking at quadrature points on + // each cell. So we should ask how we should + // best choose these quadrature points on + // each cell. To this end, recall that if we + // had a single $Q_1$ field (rather than the + // vector-valued field of higher order) then + // the maximum would be attained at a vertex + // of the mesh. In other words, we should use + // the QTrapez class that has quadrature + // points only at the vertices of cells. + // + // For higher order shape functions, the + // situation is more complicated: the maxima + // and minima may be attained at points + // between the support points of shape + // functions (for the usual $Q_p$ elements + // the support points are the equidistant + // Lagrange interpolation points); + // furthermore, since we are looking for the + // maximum magnitude of a vector-valued + // quantity, we can even less say with + // certainty where the set of potential + // maximal points are. Nevertheless, + // intuitively if not provably, the Lagrange + // interpolation points appear to be a better + // choice than the Gauss points. + // + // There are now different methods to produce + // a quadrature formula with quadrature + // points equal to the interpolation points + // of the finite element. One option would be + // to use the + // FiniteElement::get_unit_support_points() + // function, reduce the output to a unique + // set of points to avoid duplicate function + // evaluations, and create a Quadrature + // object using these points. Another option, + // chosen here, is to use the QTrapez class + // and combine it with the QIterated class + // that repeats the QTrapez formula on a + // number of sub-cells in each coordinate + // direction. To cover all support points, we + // need to iterate it + // stokes_degree+1 times since + // this is the polynomial degree of the + // Stokes element in use: template double BoussinesqFlowProblem::get_maximal_velocity () const { const QIterated quadrature_formula (QTrapez<1>(), - stokes_degree+1); + stokes_degree+1); const unsigned int n_q_points = quadrature_formula.size(); FEValues fe_values (stokes_fe, quadrature_formula, update_values); @@ -790,16 +791,16 @@ namespace Step31 const FEValuesExtractors::Vector velocities (0); typename DoFHandler::active_cell_iterator - cell = stokes_dof_handler.begin_active(), - endc = stokes_dof_handler.end(); + cell = stokes_dof_handler.begin_active(), + endc = stokes_dof_handler.end(); for (; cell!=endc; ++cell) { - fe_values.reinit (cell); - fe_values[velocities].get_function_values (stokes_solution, - velocity_values); + fe_values.reinit (cell); + fe_values[velocities].get_function_values (stokes_solution, + velocity_values); - for (unsigned int q=0; q std::pair BoussinesqFlowProblem::get_extrapolated_temperature_range () const { const QIterated quadrature_formula (QTrapez<1>(), - temperature_degree); + temperature_degree); const unsigned int n_q_points = quadrature_formula.size(); FEValues fe_values (temperature_fe, quadrature_formula, - update_values); + update_values); std::vector old_temperature_values(n_q_points); std::vector old_old_temperature_values(n_q_points); if (timestep_number != 0) { - double min_temperature = std::numeric_limits::max(), - max_temperature = -std::numeric_limits::max(); - - typename DoFHandler::active_cell_iterator - cell = temperature_dof_handler.begin_active(), - endc = temperature_dof_handler.end(); - for (; cell!=endc; ++cell) - { - fe_values.reinit (cell); - fe_values.get_function_values (old_temperature_solution, - old_temperature_values); - fe_values.get_function_values (old_old_temperature_solution, - old_old_temperature_values); - - for (unsigned int q=0; q::max(), + max_temperature = -std::numeric_limits::max(); + + typename DoFHandler::active_cell_iterator + cell = temperature_dof_handler.begin_active(), + endc = temperature_dof_handler.end(); + for (; cell!=endc; ++cell) + { + fe_values.reinit (cell); + fe_values.get_function_values (old_temperature_solution, + old_temperature_values); + fe_values.get_function_values (old_old_temperature_solution, + old_old_temperature_values); + + for (unsigned int q=0; q::max(), - max_temperature = -std::numeric_limits::max(); - - typename DoFHandler::active_cell_iterator - cell = temperature_dof_handler.begin_active(), - endc = temperature_dof_handler.end(); - for (; cell!=endc; ++cell) - { - fe_values.reinit (cell); - fe_values.get_function_values (old_temperature_solution, - old_temperature_values); - - for (unsigned int q=0; q::max(), + max_temperature = -std::numeric_limits::max(); + + typename DoFHandler::active_cell_iterator + cell = temperature_dof_handler.begin_active(), + endc = temperature_dof_handler.end(); + for (; cell!=endc; ++cell) + { + fe_values.reinit (cell); + fe_values.get_function_values (old_temperature_solution, + old_temperature_values); + + for (unsigned int q=0; q double BoussinesqFlowProblem:: compute_viscosity (const std::vector &old_temperature, - const std::vector &old_old_temperature, - const std::vector > &old_temperature_grads, - const std::vector > &old_old_temperature_grads, - const std::vector &old_temperature_laplacians, - const std::vector &old_old_temperature_laplacians, - const std::vector > &old_velocity_values, - const std::vector > &old_old_velocity_values, - const std::vector &gamma_values, - const double global_u_infty, - const double global_T_variation, - const double cell_diameter) const + const std::vector &old_old_temperature, + const std::vector > &old_temperature_grads, + const std::vector > &old_old_temperature_grads, + const std::vector &old_temperature_laplacians, + const std::vector &old_old_temperature_laplacians, + const std::vector > &old_velocity_values, + const std::vector > &old_old_velocity_values, + const std::vector &gamma_values, + const double global_u_infty, + const double global_T_variation, + const double cell_diameter) const { const double beta = 0.015 * dim; const double alpha = 1; @@ -986,92 +987,92 @@ namespace Step31 for (unsigned int q=0; q < n_q_points; ++q) { - const Tensor<1,dim> u = (old_velocity_values[q] + - old_old_velocity_values[q]) / 2; + const Tensor<1,dim> u = (old_velocity_values[q] + + old_old_velocity_values[q]) / 2; - const double dT_dt = (old_temperature[q] - old_old_temperature[q]) - / old_time_step; - const double u_grad_T = u * (old_temperature_grads[q] + - old_old_temperature_grads[q]) / 2; + const double dT_dt = (old_temperature[q] - old_old_temperature[q]) + / old_time_step; + const double u_grad_T = u * (old_temperature_grads[q] + + old_old_temperature_grads[q]) / 2; - const double kappa_Delta_T = EquationData::kappa - * (old_temperature_laplacians[q] + - old_old_temperature_laplacians[q]) / 2; + const double kappa_Delta_T = EquationData::kappa + * (old_temperature_laplacians[q] + + old_old_temperature_laplacians[q]) / 2; - const double residual - = std::abs((dT_dt + u_grad_T - kappa_Delta_T - gamma_values[q]) * - std::pow((old_temperature[q]+old_old_temperature[q]) / 2, - alpha-1.)); + const double residual + = std::abs((dT_dt + u_grad_T - kappa_Delta_T - gamma_values[q]) * + std::pow((old_temperature[q]+old_old_temperature[q]) / 2, + alpha-1.)); - max_residual = std::max (residual, max_residual); - max_velocity = std::max (std::sqrt (u*u), max_velocity); + max_residual = std::max (residual, max_residual); + max_velocity = std::max (std::sqrt (u*u), max_velocity); } const double c_R = std::pow (2., (4.-2*alpha)/dim); const double global_scaling = c_R * global_u_infty * global_T_variation * - std::pow(global_Omega_diameter, alpha - 2.); + std::pow(global_Omega_diameter, alpha - 2.); return (beta * - max_velocity * - std::min (cell_diameter, - std::pow(cell_diameter,alpha) * - max_residual / global_scaling)); + max_velocity * + std::min (cell_diameter, + std::pow(cell_diameter,alpha) * + max_residual / global_scaling)); } - // @sect4{BoussinesqFlowProblem::setup_dofs} - // - // This is the function that sets up the - // DoFHandler objects we have here (one for - // the Stokes part and one for the - // temperature part) as well as set to the - // right sizes the various objects required - // for the linear algebra in this - // program. Its basic operations are similar - // to what we do in step-22. - // - // The body of the function first - // enumerates all degrees of freedom for - // the Stokes and temperature systems. For - // the Stokes part, degrees of freedom are - // then sorted to ensure that velocities - // precede pressure DoFs so that we can - // partition the Stokes matrix into a - // $2\times 2$ matrix. As a difference to - // step-22, we do not perform any - // additional DoF renumbering. In that - // program, it paid off since our solver - // was heavily dependent on ILU's, whereas - // we use AMG here which is not sensitive - // to the DoF numbering. The IC - // preconditioner for the inversion of the - // pressure mass matrix would of course - // take advantage of a Cuthill-McKee like - // renumbering, but its costs are low - // compared to the velocity portion, so the - // additional work does not pay off. - // - // We then proceed with the generation of the - // hanging node constraints that arise from - // adaptive grid refinement for both - // DoFHandler objects. For the velocity, we - // impose no-flux boundary conditions - // $\mathbf{u}\cdot \mathbf{n}=0$ by adding - // constraints to the object that already - // stores the hanging node constraints - // matrix. The second parameter in the - // function describes the first of the - // velocity components in the total dof - // vector, which is zero here. The variable - // no_normal_flux_boundaries - // denotes the boundary indicators for which - // to set the no flux boundary conditions; - // here, this is boundary indicator zero. - // - // After having done so, we count the number - // of degrees of freedom in the various - // blocks: + // @sect4{BoussinesqFlowProblem::setup_dofs} + // + // This is the function that sets up the + // DoFHandler objects we have here (one for + // the Stokes part and one for the + // temperature part) as well as set to the + // right sizes the various objects required + // for the linear algebra in this + // program. Its basic operations are similar + // to what we do in step-22. + // + // The body of the function first + // enumerates all degrees of freedom for + // the Stokes and temperature systems. For + // the Stokes part, degrees of freedom are + // then sorted to ensure that velocities + // precede pressure DoFs so that we can + // partition the Stokes matrix into a + // $2\times 2$ matrix. As a difference to + // step-22, we do not perform any + // additional DoF renumbering. In that + // program, it paid off since our solver + // was heavily dependent on ILU's, whereas + // we use AMG here which is not sensitive + // to the DoF numbering. The IC + // preconditioner for the inversion of the + // pressure mass matrix would of course + // take advantage of a Cuthill-McKee like + // renumbering, but its costs are low + // compared to the velocity portion, so the + // additional work does not pay off. + // + // We then proceed with the generation of the + // hanging node constraints that arise from + // adaptive grid refinement for both + // DoFHandler objects. For the velocity, we + // impose no-flux boundary conditions + // $\mathbf{u}\cdot \mathbf{n}=0$ by adding + // constraints to the object that already + // stores the hanging node constraints + // matrix. The second parameter in the + // function describes the first of the + // velocity components in the total dof + // vector, which is zero here. The variable + // no_normal_flux_boundaries + // denotes the boundary indicators for which + // to set the no flux boundary conditions; + // here, this is boundary indicator zero. + // + // After having done so, we count the number + // of degrees of freedom in the various + // blocks: template void BoussinesqFlowProblem::setup_dofs () { @@ -1121,78 +1122,78 @@ namespace Step31 << std::endl << std::endl; - // The next step is to create the sparsity - // pattern for the Stokes and temperature - // system matrices as well as the - // preconditioner matrix from which we - // build the Stokes preconditioner. As in - // step-22, we choose to create the pattern - // not as in the first few tutorial - // programs, but by using the blocked - // version of CompressedSimpleSparsityPattern. - // The reason for doing this is mainly - // memory, that is, the SparsityPattern - // class would consume too much memory when - // used in three spatial dimensions as we - // intend to do for this program. - // - // So, we first release the memory stored - // in the matrices, then set up an object - // of type - // BlockCompressedSimpleSparsityPattern - // consisting of $2\times 2$ blocks (for - // the Stokes system matrix and - // preconditioner) or - // CompressedSimpleSparsityPattern (for - // the temperature part). We then fill - // these objects with the nonzero - // pattern, taking into account that for - // the Stokes system matrix, there are no - // entries in the pressure-pressure block - // (but all velocity vector components - // couple with each other and with the - // pressure). Similarly, in the Stokes - // preconditioner matrix, only the - // diagonal blocks are nonzero, since we - // use the vector Laplacian as discussed - // in the introduction. This operator - // only couples each vector component of - // the Laplacian with itself, but not - // with the other vector - // components. (Application of the - // constraints resulting from the no-flux - // boundary conditions will couple vector - // components at the boundary again, - // however.) - // - // When generating the sparsity pattern, - // we directly apply the constraints from - // hanging nodes and no-flux boundary - // conditions. This approach was already - // used in step-27, but is different from - // the one in early tutorial programs - // where we first built the original - // sparsity pattern and only then added - // the entries resulting from - // constraints. The reason for doing so - // is that later during assembly we are - // going to distribute the constraints - // immediately when transferring local to - // global dofs. Consequently, there will - // be no data written at positions of - // constrained degrees of freedom, so we - // can let the - // DoFTools::make_sparsity_pattern - // function omit these entries by setting - // the last boolean flag to - // false. Once the sparsity - // pattern is ready, we can use it to - // initialize the Trilinos - // matrices. Since the Trilinos matrices - // store the sparsity pattern internally, - // there is no need to keep the sparsity - // pattern around after the - // initialization of the matrix. + // The next step is to create the sparsity + // pattern for the Stokes and temperature + // system matrices as well as the + // preconditioner matrix from which we + // build the Stokes preconditioner. As in + // step-22, we choose to create the pattern + // not as in the first few tutorial + // programs, but by using the blocked + // version of CompressedSimpleSparsityPattern. + // The reason for doing this is mainly + // memory, that is, the SparsityPattern + // class would consume too much memory when + // used in three spatial dimensions as we + // intend to do for this program. + // + // So, we first release the memory stored + // in the matrices, then set up an object + // of type + // BlockCompressedSimpleSparsityPattern + // consisting of $2\times 2$ blocks (for + // the Stokes system matrix and + // preconditioner) or + // CompressedSimpleSparsityPattern (for + // the temperature part). We then fill + // these objects with the nonzero + // pattern, taking into account that for + // the Stokes system matrix, there are no + // entries in the pressure-pressure block + // (but all velocity vector components + // couple with each other and with the + // pressure). Similarly, in the Stokes + // preconditioner matrix, only the + // diagonal blocks are nonzero, since we + // use the vector Laplacian as discussed + // in the introduction. This operator + // only couples each vector component of + // the Laplacian with itself, but not + // with the other vector + // components. (Application of the + // constraints resulting from the no-flux + // boundary conditions will couple vector + // components at the boundary again, + // however.) + // + // When generating the sparsity pattern, + // we directly apply the constraints from + // hanging nodes and no-flux boundary + // conditions. This approach was already + // used in step-27, but is different from + // the one in early tutorial programs + // where we first built the original + // sparsity pattern and only then added + // the entries resulting from + // constraints. The reason for doing so + // is that later during assembly we are + // going to distribute the constraints + // immediately when transferring local to + // global dofs. Consequently, there will + // be no data written at positions of + // constrained degrees of freedom, so we + // can let the + // DoFTools::make_sparsity_pattern + // function omit these entries by setting + // the last boolean flag to + // false. Once the sparsity + // pattern is ready, we can use it to + // initialize the Trilinos + // matrices. Since the Trilinos matrices + // store the sparsity pattern internally, + // there is no need to keep the sparsity + // pattern around after the + // initialization of the matrix. stokes_block_sizes.resize (2); stokes_block_sizes[0] = n_u; stokes_block_sizes[1] = n_p; @@ -1251,28 +1252,28 @@ namespace Step31 stokes_preconditioner_matrix.reinit (csp); } - // The creation of the temperature matrix - // (or, rather, matrices, since we - // provide a temperature mass matrix and - // a temperature stiffness matrix, that - // will be added together for time - // discretization) follows the generation - // of the Stokes matrix – except - // that it is much easier here since we - // do not need to take care of any blocks - // or coupling between components. Note - // how we initialize the three - // temperature matrices: We only use the - // sparsity pattern for reinitialization - // of the first matrix, whereas we use - // the previously generated matrix for - // the two remaining reinits. The reason - // for doing so is that reinitialization - // from an already generated matrix - // allows Trilinos to reuse the sparsity - // pattern instead of generating a new - // one for each copy. This saves both - // some time and memory. + // The creation of the temperature matrix + // (or, rather, matrices, since we + // provide a temperature mass matrix and + // a temperature stiffness matrix, that + // will be added together for time + // discretization) follows the generation + // of the Stokes matrix – except + // that it is much easier here since we + // do not need to take care of any blocks + // or coupling between components. Note + // how we initialize the three + // temperature matrices: We only use the + // sparsity pattern for reinitialization + // of the first matrix, whereas we use + // the previously generated matrix for + // the two remaining reinits. The reason + // for doing so is that reinitialization + // from an already generated matrix + // allows Trilinos to reuse the sparsity + // pattern instead of generating a new + // one for each copy. This saves both + // some time and memory. { temperature_mass_matrix.clear (); temperature_stiffness_matrix.clear (); @@ -1287,14 +1288,14 @@ namespace Step31 temperature_stiffness_matrix.reinit (temperature_matrix); } - // Lastly, we set the vectors for the - // Stokes solutions $\mathbf u^{n-1}$ and - // $\mathbf u^{n-2}$, as well as for the - // temperatures $T^{n}$, $T^{n-1}$ and - // $T^{n-2}$ (required for time stepping) - // and all the system right hand sides to - // their correct sizes and block - // structure: + // Lastly, we set the vectors for the + // Stokes solutions $\mathbf u^{n-1}$ and + // $\mathbf u^{n-2}$, as well as for the + // temperatures $T^{n}$, $T^{n-1}$ and + // $T^{n-2}$ (required for time stepping) + // and all the system right hand sides to + // their correct sizes and block + // structure: stokes_solution.reinit (stokes_block_sizes); old_stokes_solution.reinit (stokes_block_sizes); stokes_rhs.reinit (stokes_block_sizes); @@ -1308,29 +1309,29 @@ namespace Step31 - // @sect4{BoussinesqFlowProblem::assemble_stokes_preconditioner} - // - // This function assembles the matrix we use - // for preconditioning the Stokes - // system. What we need are a vector Laplace - // matrix on the velocity components and a - // mass matrix weighted by $\eta^{-1}$ on the - // pressure component. We start by generating - // a quadrature object of appropriate order, - // the FEValues object that can give values - // and gradients at the quadrature points - // (together with quadrature weights). Next - // we create data structures for the cell - // matrix and the relation between local and - // global DoFs. The vectors - // grad_phi_u and - // phi_p are going to hold the - // values of the basis functions in order to - // faster build up the local matrices, as was - // already done in step-22. Before we start - // the loop over all active cells, we have to - // specify which components are pressure and - // which are velocity. + // @sect4{BoussinesqFlowProblem::assemble_stokes_preconditioner} + // + // This function assembles the matrix we use + // for preconditioning the Stokes + // system. What we need are a vector Laplace + // matrix on the velocity components and a + // mass matrix weighted by $\eta^{-1}$ on the + // pressure component. We start by generating + // a quadrature object of appropriate order, + // the FEValues object that can give values + // and gradients at the quadrature points + // (together with quadrature weights). Next + // we create data structures for the cell + // matrix and the relation between local and + // global DoFs. The vectors + // grad_phi_u and + // phi_p are going to hold the + // values of the basis functions in order to + // faster build up the local matrices, as was + // already done in step-22. Before we start + // the loop over all active cells, we have to + // specify which components are pressure and + // which are velocity. template void BoussinesqFlowProblem::assemble_stokes_preconditioner () @@ -1356,33 +1357,33 @@ namespace Step31 const FEValuesExtractors::Scalar pressure (dim); typename DoFHandler::active_cell_iterator - cell = stokes_dof_handler.begin_active(), - endc = stokes_dof_handler.end(); + cell = stokes_dof_handler.begin_active(), + endc = stokes_dof_handler.end(); for (; cell!=endc; ++cell) { stokes_fe_values.reinit (cell); local_matrix = 0; - // The creation of the local matrix is - // rather simple. There are only a - // Laplace term (on the velocity) and a - // mass matrix weighted by $\eta^{-1}$ - // to be generated, so the creation of - // the local matrix is done in two - // lines. Once the local matrix is - // ready (loop over rows and columns in - // the local matrix on each quadrature - // point), we get the local DoF indices - // and write the local information into - // the global matrix. We do this as in - // step-27, i.e. we directly apply the - // constraints from hanging nodes - // locally. By doing so, we don't have - // to do that afterwards, and we don't - // also write into entries of the - // matrix that will actually be set to - // zero again later when eliminating - // constraints. + // The creation of the local matrix is + // rather simple. There are only a + // Laplace term (on the velocity) and a + // mass matrix weighted by $\eta^{-1}$ + // to be generated, so the creation of + // the local matrix is done in two + // lines. Once the local matrix is + // ready (loop over rows and columns in + // the local matrix on each quadrature + // point), we get the local DoF indices + // and write the local information into + // the global matrix. We do this as in + // step-27, i.e. we directly apply the + // constraints from hanging nodes + // locally. By doing so, we don't have + // to do that afterwards, and we don't + // also write into entries of the + // matrix that will actually be set to + // zero again later when eliminating + // constraints. for (unsigned int q=0; qrebuild_stokes_preconditioner - // has the value - // false). Otherwise its first - // task is to call - // assemble_stokes_preconditioner - // to generate the preconditioner matrices. - // - // Next, we set up the preconditioner for - // the velocity-velocity matrix - // A. As explained in the - // introduction, we are going to use an - // AMG preconditioner based on a vector - // Laplace matrix $\hat{A}$ (which is - // spectrally close to the Stokes matrix - // A). Usually, the - // TrilinosWrappers::PreconditionAMG - // class can be seen as a good black-box - // preconditioner which does not need any - // special knowledge. In this case, - // however, we have to be careful: since - // we build an AMG for a vector problem, - // we have to tell the preconditioner - // setup which dofs belong to which - // vector component. We do this using the - // function - // DoFTools::extract_constant_modes, a - // function that generates a set of - // dim vectors, where each one - // has ones in the respective component - // of the vector problem and zeros - // elsewhere. Hence, these are the - // constant modes on each component, - // which explains the name of the - // variable. + // @sect4{BoussinesqFlowProblem::build_stokes_preconditioner} + // + // This function generates the inner + // preconditioners that are going to be used + // for the Schur complement block + // preconditioner. Since the preconditioners + // need only to be regenerated when the + // matrices change, this function does not + // have to do anything in case the matrices + // have not changed (i.e., the flag + // rebuild_stokes_preconditioner + // has the value + // false). Otherwise its first + // task is to call + // assemble_stokes_preconditioner + // to generate the preconditioner matrices. + // + // Next, we set up the preconditioner for + // the velocity-velocity matrix + // A. As explained in the + // introduction, we are going to use an + // AMG preconditioner based on a vector + // Laplace matrix $\hat{A}$ (which is + // spectrally close to the Stokes matrix + // A). Usually, the + // TrilinosWrappers::PreconditionAMG + // class can be seen as a good black-box + // preconditioner which does not need any + // special knowledge. In this case, + // however, we have to be careful: since + // we build an AMG for a vector problem, + // we have to tell the preconditioner + // setup which dofs belong to which + // vector component. We do this using the + // function + // DoFTools::extract_constant_modes, a + // function that generates a set of + // dim vectors, where each one + // has ones in the respective component + // of the vector problem and zeros + // elsewhere. Hence, these are the + // constant modes on each component, + // which explains the name of the + // variable. template void BoussinesqFlowProblem::build_stokes_preconditioner () @@ -1471,65 +1472,65 @@ namespace Step31 std::vector > constant_modes; FEValuesExtractors::Vector velocity_components(0); DoFTools::extract_constant_modes (stokes_dof_handler, - stokes_fe.component_mask(velocity_components), + stokes_fe.component_mask(velocity_components), constant_modes); TrilinosWrappers::PreconditionAMG::AdditionalData amg_data; amg_data.constant_modes = constant_modes; - // Next, we set some more options of the - // AMG preconditioner. In particular, we - // need to tell the AMG setup that we use - // quadratic basis functions for the - // velocity matrix (this implies more - // nonzero elements in the matrix, so - // that a more rubust algorithm needs to - // be chosen internally). Moreover, we - // want to be able to control how the - // coarsening structure is build up. The - // way the Trilinos smoothed aggregation - // AMG does this is to look which matrix - // entries are of similar size as the - // diagonal entry in order to - // algebraically build a coarse-grid - // structure. By setting the parameter - // aggregation_threshold to - // 0.02, we specify that all entries that - // are more than two precent of size of - // some diagonal pivots in that row - // should form one coarse grid - // point. This parameter is rather - // ad-hoc, and some fine-tuning of it can - // influence the performance of the - // preconditioner. As a rule of thumb, - // larger values of - // aggregation_threshold - // will decrease the number of - // iterations, but increase the costs per - // iteration. A look at the Trilinos - // documentation will provide more - // information on these parameters. With - // this data set, we then initialize the - // preconditioner with the matrix we want - // it to apply to. - // - // Finally, we also initialize the - // preconditioner for the inversion of - // the pressure mass matrix. This matrix - // is symmetric and well-behaved, so we - // can chose a simple preconditioner. We - // stick with an incomple Cholesky (IC) - // factorization preconditioner, which is - // designed for symmetric matrices. We - // could have also chosen an SSOR - // preconditioner with relaxation factor - // around 1.2, but IC is cheaper for our - // example. We wrap the preconditioners - // into a std_cxx1x::shared_ptr - // pointer, which makes it easier to - // recreate the preconditioner next time - // around since we do not have to care - // about destroying the previously used - // object. + // Next, we set some more options of the + // AMG preconditioner. In particular, we + // need to tell the AMG setup that we use + // quadratic basis functions for the + // velocity matrix (this implies more + // nonzero elements in the matrix, so + // that a more rubust algorithm needs to + // be chosen internally). Moreover, we + // want to be able to control how the + // coarsening structure is build up. The + // way the Trilinos smoothed aggregation + // AMG does this is to look which matrix + // entries are of similar size as the + // diagonal entry in order to + // algebraically build a coarse-grid + // structure. By setting the parameter + // aggregation_threshold to + // 0.02, we specify that all entries that + // are more than two precent of size of + // some diagonal pivots in that row + // should form one coarse grid + // point. This parameter is rather + // ad-hoc, and some fine-tuning of it can + // influence the performance of the + // preconditioner. As a rule of thumb, + // larger values of + // aggregation_threshold + // will decrease the number of + // iterations, but increase the costs per + // iteration. A look at the Trilinos + // documentation will provide more + // information on these parameters. With + // this data set, we then initialize the + // preconditioner with the matrix we want + // it to apply to. + // + // Finally, we also initialize the + // preconditioner for the inversion of + // the pressure mass matrix. This matrix + // is symmetric and well-behaved, so we + // can chose a simple preconditioner. We + // stick with an incomple Cholesky (IC) + // factorization preconditioner, which is + // designed for symmetric matrices. We + // could have also chosen an SSOR + // preconditioner with relaxation factor + // around 1.2, but IC is cheaper for our + // example. We wrap the preconditioners + // into a std_cxx1x::shared_ptr + // pointer, which makes it easier to + // recreate the preconditioner next time + // around since we do not have to care + // about destroying the previously used + // object. amg_data.elliptic = true; amg_data.higher_order_elements = true; amg_data.smoother_sweeps = 2; @@ -1548,67 +1549,67 @@ namespace Step31 - // @sect4{BoussinesqFlowProblem::assemble_stokes_system} - // - // The time lag scheme we use for advancing - // the coupled Stokes-temperature system - // forces us to split up the assembly (and - // the solution of linear systems) into two - // step. The first one is to create the - // Stokes system matrix and right hand - // side, and the second is to create matrix - // and right hand sides for the temperature - // dofs, which depends on the result of the - // linear system for the velocity. - // - // This function is called at the beginning - // of each time step. In the first time step - // or if the mesh has changed, indicated by - // the rebuild_stokes_matrix, we - // need to assemble the Stokes matrix; on the - // other hand, if the mesh hasn't changed and - // the matrix is already available, this is - // not necessary and all we need to do is - // assemble the right hand side vector which - // changes in each time step. - // - // Regarding the technical details of - // implementation, not much has changed from - // step-22. We reset matrix and vector, - // create a quadrature formula on the cells, - // and then create the respective FEValues - // object. For the update flags, we require - // basis function derivatives only in case of - // a full assembly, since they are not needed - // for the right hand side; as always, - // choosing the minimal set of flags - // depending on what is currently needed - // makes the call to FEValues::reinit further - // down in the program more efficient. - // - // There is one thing that needs to be - // commented – since we have a separate - // finite element and DoFHandler for the - // temperature, we need to generate a second - // FEValues object for the proper evaluation - // of the temperature solution. This isn't - // too complicated to realize here: just use - // the temperature structures and set an - // update flag for the basis function values - // which we need for evaluation of the - // temperature solution. The only important - // part to remember here is that the same - // quadrature formula is used for both - // FEValues objects to ensure that we get - // matching information when we loop over the - // quadrature points of the two objects. - // - // The declarations proceed with some - // shortcuts for array sizes, the creation - // of the local matrix and right hand side - // as well as the vector for the indices of - // the local dofs compared to the global - // system. + // @sect4{BoussinesqFlowProblem::assemble_stokes_system} + // + // The time lag scheme we use for advancing + // the coupled Stokes-temperature system + // forces us to split up the assembly (and + // the solution of linear systems) into two + // step. The first one is to create the + // Stokes system matrix and right hand + // side, and the second is to create matrix + // and right hand sides for the temperature + // dofs, which depends on the result of the + // linear system for the velocity. + // + // This function is called at the beginning + // of each time step. In the first time step + // or if the mesh has changed, indicated by + // the rebuild_stokes_matrix, we + // need to assemble the Stokes matrix; on the + // other hand, if the mesh hasn't changed and + // the matrix is already available, this is + // not necessary and all we need to do is + // assemble the right hand side vector which + // changes in each time step. + // + // Regarding the technical details of + // implementation, not much has changed from + // step-22. We reset matrix and vector, + // create a quadrature formula on the cells, + // and then create the respective FEValues + // object. For the update flags, we require + // basis function derivatives only in case of + // a full assembly, since they are not needed + // for the right hand side; as always, + // choosing the minimal set of flags + // depending on what is currently needed + // makes the call to FEValues::reinit further + // down in the program more efficient. + // + // There is one thing that needs to be + // commented – since we have a separate + // finite element and DoFHandler for the + // temperature, we need to generate a second + // FEValues object for the proper evaluation + // of the temperature solution. This isn't + // too complicated to realize here: just use + // the temperature structures and set an + // update flag for the basis function values + // which we need for evaluation of the + // temperature solution. The only important + // part to remember here is that the same + // quadrature formula is used for both + // FEValues objects to ensure that we get + // matching information when we loop over the + // quadrature points of the two objects. + // + // The declarations proceed with some + // shortcuts for array sizes, the creation + // of the local matrix and right hand side + // as well as the vector for the indices of + // the local dofs compared to the global + // system. template void BoussinesqFlowProblem::assemble_stokes_system () { @@ -1641,29 +1642,29 @@ namespace Step31 std::vector local_dof_indices (dofs_per_cell); - // Next we need a vector that will contain - // the values of the temperature solution - // at the previous time level at the - // quadrature points to assemble the source - // term in the right hand side of the - // momentum equation. Let's call this vector - // old_solution_values. - // - // The set of vectors we create next hold - // the evaluations of the basis functions - // as well as their gradients and - // symmetrized gradients that will be used - // for creating the matrices. Putting these - // into their own arrays rather than asking - // the FEValues object for this information - // each time it is needed is an - // optimization to accelerate the assembly - // process, see step-22 for details. - // - // The last two declarations are used to - // extract the individual blocks - // (velocity, pressure, temperature) from - // the total FE system. + // Next we need a vector that will contain + // the values of the temperature solution + // at the previous time level at the + // quadrature points to assemble the source + // term in the right hand side of the + // momentum equation. Let's call this vector + // old_solution_values. + // + // The set of vectors we create next hold + // the evaluations of the basis functions + // as well as their gradients and + // symmetrized gradients that will be used + // for creating the matrices. Putting these + // into their own arrays rather than asking + // the FEValues object for this information + // each time it is needed is an + // optimization to accelerate the assembly + // process, see step-22 for details. + // + // The last two declarations are used to + // extract the individual blocks + // (velocity, pressure, temperature) from + // the total FE system. std::vector old_temperature_values(n_q_points); std::vector > phi_u (dofs_per_cell); @@ -1674,30 +1675,30 @@ namespace Step31 const FEValuesExtractors::Vector velocities (0); const FEValuesExtractors::Scalar pressure (dim); - // Now start the loop over all cells in - // the problem. We are working on two - // different DoFHandlers for this - // assembly routine, so we must have two - // different cell iterators for the two - // objects in use. This might seem a bit - // peculiar, since both the Stokes system - // and the temperature system use the - // same grid, but that's the only way to - // keep degrees of freedom in sync. The - // first statements within the loop are - // again all very familiar, doing the - // update of the finite element data as - // specified by the update flags, zeroing - // out the local arrays and getting the - // values of the old solution at the - // quadrature points. Then we are ready to - // loop over the quadrature points on the - // cell. + // Now start the loop over all cells in + // the problem. We are working on two + // different DoFHandlers for this + // assembly routine, so we must have two + // different cell iterators for the two + // objects in use. This might seem a bit + // peculiar, since both the Stokes system + // and the temperature system use the + // same grid, but that's the only way to + // keep degrees of freedom in sync. The + // first statements within the loop are + // again all very familiar, doing the + // update of the finite element data as + // specified by the update flags, zeroing + // out the local arrays and getting the + // values of the old solution at the + // quadrature points. Then we are ready to + // loop over the quadrature points on the + // cell. typename DoFHandler::active_cell_iterator - cell = stokes_dof_handler.begin_active(), - endc = stokes_dof_handler.end(); + cell = stokes_dof_handler.begin_active(), + endc = stokes_dof_handler.end(); typename DoFHandler::active_cell_iterator - temperature_cell = temperature_dof_handler.begin_active(); + temperature_cell = temperature_dof_handler.begin_active(); for (; cell!=endc; ++cell, ++temperature_cell) { @@ -1714,29 +1715,29 @@ namespace Step31 { const double old_temperature = old_temperature_values[q]; - // Next we extract the values and - // gradients of basis functions - // relevant to the terms in the - // inner products. As shown in - // step-22 this helps accelerate - // assembly. - // - // Once this is done, we start the - // loop over the rows and columns - // of the local matrix and feed the - // matrix with the relevant - // products. The right hand side is - // filled with the forcing term - // driven by temperature in - // direction of gravity (which is - // vertical in our example). Note - // that the right hand side term is - // always generated, whereas the - // matrix contributions are only - // updated when it is requested by - // the - // rebuild_matrices - // flag. + // Next we extract the values and + // gradients of basis functions + // relevant to the terms in the + // inner products. As shown in + // step-22 this helps accelerate + // assembly. + // + // Once this is done, we start the + // loop over the rows and columns + // of the local matrix and feed the + // matrix with the relevant + // products. The right hand side is + // filled with the forcing term + // driven by temperature in + // direction of gravity (which is + // vertical in our example). Note + // that the right hand side term is + // always generated, whereas the + // matrix contributions are only + // updated when it is requested by + // the + // rebuild_matrices + // flag. for (unsigned int k=0; klocal_dof_indices. - // Again, we let the ConstraintMatrix - // class do the insertion of the cell - // matrix elements to the global - // matrix, which already condenses the - // hanging node constraints. + // The last step in the loop over all + // cells is to enter the local + // contributions into the global matrix + // and vector structures to the + // positions specified in + // local_dof_indices. + // Again, we let the ConstraintMatrix + // class do the insertion of the cell + // matrix elements to the global + // matrix, which already condenses the + // hanging node constraints. cell->get_dof_indices (local_dof_indices); if (rebuild_stokes_matrix == true) @@ -1799,40 +1800,40 @@ namespace Step31 - // @sect4{BoussinesqFlowProblem::assemble_temperature_matrix} - // - // This function assembles the matrix in - // the temperature equation. The - // temperature matrix consists of two - // parts, a mass matrix and the time step - // size times a stiffness matrix given by - // a Laplace term times the amount of - // diffusion. Since the matrix depends on - // the time step size (which varies from - // one step to another), the temperature - // matrix needs to be updated every time - // step. We could simply regenerate the - // matrices in every time step, but this - // is not really efficient since mass and - // Laplace matrix do only change when we - // change the mesh. Hence, we do this - // more efficiently by generating two - // separate matrices in this function, - // one for the mass matrix and one for - // the stiffness (diffusion) matrix. We - // will then sum up the matrix plus the - // stiffness matrix times the time step - // size once we know the actual time step. - // - // So the details for this first step are - // very simple. In case we need to - // rebuild the matrix (i.e., the mesh has - // changed), we zero the data structures, - // get a quadrature formula and a - // FEValues object, and create local - // matrices, local dof indices and - // evaluation structures for the basis - // functions. + // @sect4{BoussinesqFlowProblem::assemble_temperature_matrix} + // + // This function assembles the matrix in + // the temperature equation. The + // temperature matrix consists of two + // parts, a mass matrix and the time step + // size times a stiffness matrix given by + // a Laplace term times the amount of + // diffusion. Since the matrix depends on + // the time step size (which varies from + // one step to another), the temperature + // matrix needs to be updated every time + // step. We could simply regenerate the + // matrices in every time step, but this + // is not really efficient since mass and + // Laplace matrix do only change when we + // change the mesh. Hence, we do this + // more efficiently by generating two + // separate matrices in this function, + // one for the mass matrix and one for + // the stiffness (diffusion) matrix. We + // will then sum up the matrix plus the + // stiffness matrix times the time step + // size once we know the actual time step. + // + // So the details for this first step are + // very simple. In case we need to + // rebuild the matrix (i.e., the mesh has + // changed), we zero the data structures, + // get a quadrature formula and a + // FEValues object, and create local + // matrices, local dof indices and + // evaluation structures for the basis + // functions. template void BoussinesqFlowProblem::assemble_temperature_matrix () { @@ -1858,23 +1859,23 @@ namespace Step31 std::vector phi_T (dofs_per_cell); std::vector > grad_phi_T (dofs_per_cell); - // Now, let's start the loop over all cells - // in the triangulation. We need to zero - // out the local matrices, update the - // finite element evaluations, and then - // loop over the rows and columns of the - // matrices on each quadrature point, where - // we then create the mass matrix and the - // stiffness matrix (Laplace terms times - // the diffusion - // EquationData::kappa. Finally, - // we let the constraints object insert - // these values into the global matrix, and - // directly condense the constraints into - // the matrix. + // Now, let's start the loop over all cells + // in the triangulation. We need to zero + // out the local matrices, update the + // finite element evaluations, and then + // loop over the rows and columns of the + // matrices on each quadrature point, where + // we then create the mass matrix and the + // stiffness matrix (Laplace terms times + // the diffusion + // EquationData::kappa. Finally, + // we let the constraints object insert + // these values into the global matrix, and + // directly condense the constraints into + // the matrix. typename DoFHandler::active_cell_iterator - cell = temperature_dof_handler.begin_active(), - endc = temperature_dof_handler.end(); + cell = temperature_dof_handler.begin_active(), + endc = temperature_dof_handler.end(); for (; cell!=endc; ++cell) { local_mass_matrix = 0; @@ -1894,13 +1895,13 @@ namespace Step31 for (unsigned int j=0; jupdate_hessians. - // - // The temperature equation is coupled to the - // Stokes system by means of the fluid - // velocity. These two parts of the solution - // are associated with different DoFHandlers, - // so we again need to create a second - // FEValues object for the evaluation of the - // velocity at the quadrature points. + // @sect4{BoussinesqFlowProblem::assemble_temperature_system} + // + // This function does the second part of + // the assembly work on the temperature + // matrix, the actual addition of + // pressure mass and stiffness matrix + // (where the time step size comes into + // play), as well as the creation of the + // velocity-dependent right hand + // side. The declarations for the right + // hand side assembly in this function + // are pretty much the same as the ones + // used in the other assembly routines, + // except that we restrict ourselves to + // vectors this time. We are going to + // calculate residuals on the temperature + // system, which means that we have to + // evaluate second derivatives, specified + // by the update flag + // update_hessians. + // + // The temperature equation is coupled to the + // Stokes system by means of the fluid + // velocity. These two parts of the solution + // are associated with different DoFHandlers, + // so we again need to create a second + // FEValues object for the evaluation of the + // velocity at the quadrature points. template void BoussinesqFlowProblem:: assemble_temperature_system (const double maximal_velocity) @@ -1985,24 +1986,24 @@ namespace Step31 std::vector local_dof_indices (dofs_per_cell); - // Next comes the declaration of vectors - // to hold the old and older solution - // values (as a notation for time levels - // n-1 and n-2, - // respectively) and gradients at - // quadrature points of the current - // cell. We also declarate an object to - // hold the temperature right hande side - // values (gamma_values), - // and we again use shortcuts for the - // temperature basis - // functions. Eventually, we need to find - // the temperature extrema and the - // diameter of the computational domain - // which will be used for the definition - // of the stabilization parameter (we got - // the maximal velocity as an input to - // this function). + // Next comes the declaration of vectors + // to hold the old and older solution + // values (as a notation for time levels + // n-1 and n-2, + // respectively) and gradients at + // quadrature points of the current + // cell. We also declarate an object to + // hold the temperature right hande side + // values (gamma_values), + // and we again use shortcuts for the + // temperature basis + // functions. Eventually, we need to find + // the temperature extrema and the + // diameter of the computational domain + // which will be used for the definition + // of the stabilization parameter (we got + // the maximal velocity as an input to + // this function). std::vector > old_velocity_values (n_q_points); std::vector > old_old_velocity_values (n_q_points); std::vector old_temperature_values (n_q_points); @@ -2019,38 +2020,38 @@ namespace Step31 std::vector > grad_phi_T (dofs_per_cell); const std::pair - global_T_range = get_extrapolated_temperature_range(); + global_T_range = get_extrapolated_temperature_range(); const FEValuesExtractors::Vector velocities (0); - // Now, let's start the loop over all cells - // in the triangulation. Again, we need two - // cell iterators that walk in parallel - // through the cells of the two involved - // DoFHandler objects for the Stokes and - // temperature part. Within the loop, we - // first set the local rhs to zero, and - // then get the values and derivatives of - // the old solution functions at the - // quadrature points, since they are going - // to be needed for the definition of the - // stabilization parameters and as - // coefficients in the equation, - // respectively. Note that since the - // temperature has its own DoFHandler and - // FEValues object we get the entire - // solution at the quadrature point (which - // is the scalar temperature field only - // anyway) whereas for the Stokes part we - // restrict ourselves to extracting the - // velocity part (and ignoring the pressure - // part) by using - // stokes_fe_values[velocities].get_function_values. + // Now, let's start the loop over all cells + // in the triangulation. Again, we need two + // cell iterators that walk in parallel + // through the cells of the two involved + // DoFHandler objects for the Stokes and + // temperature part. Within the loop, we + // first set the local rhs to zero, and + // then get the values and derivatives of + // the old solution functions at the + // quadrature points, since they are going + // to be needed for the definition of the + // stabilization parameters and as + // coefficients in the equation, + // respectively. Note that since the + // temperature has its own DoFHandler and + // FEValues object we get the entire + // solution at the quadrature point (which + // is the scalar temperature field only + // anyway) whereas for the Stokes part we + // restrict ourselves to extracting the + // velocity part (and ignoring the pressure + // part) by using + // stokes_fe_values[velocities].get_function_values. typename DoFHandler::active_cell_iterator - cell = temperature_dof_handler.begin_active(), - endc = temperature_dof_handler.end(); + cell = temperature_dof_handler.begin_active(), + endc = temperature_dof_handler.end(); typename DoFHandler::active_cell_iterator - stokes_cell = stokes_dof_handler.begin_active(); + stokes_cell = stokes_dof_handler.begin_active(); for (; cell!=endc; ++cell, ++stokes_cell) { @@ -2082,28 +2083,28 @@ namespace Step31 stokes_fe_values[velocities].get_function_values (old_stokes_solution, old_old_velocity_values); - // Next, we calculate the artificial - // viscosity for stabilization - // according to the discussion in the - // introduction using the dedicated - // function. With that at hand, we - // can get into the loop over - // quadrature points and local rhs - // vector components. The terms here - // are quite lenghty, but their - // definition follows the - // time-discrete system developed in - // the introduction of this - // program. The BDF-2 scheme needs - // one more term from the old time - // step (and involves more - // complicated factors) than the - // backward Euler scheme that is used - // for the first time step. When all - // this is done, we distribute the - // local vector into the global one - // (including hanging node - // constraints). + // Next, we calculate the artificial + // viscosity for stabilization + // according to the discussion in the + // introduction using the dedicated + // function. With that at hand, we + // can get into the loop over + // quadrature points and local rhs + // vector components. The terms here + // are quite lenghty, but their + // definition follows the + // time-discrete system developed in + // the introduction of this + // program. The BDF-2 scheme needs + // one more term from the old time + // step (and involves more + // complicated factors) than the + // backward Euler scheme that is used + // for the first time step. When all + // this is done, we distribute the + // local vector into the global one + // (including hanging node + // constraints). const double nu = compute_viscosity (old_temperature_values, old_old_temperature_values, @@ -2182,58 +2183,58 @@ namespace Step31 - // @sect4{BoussinesqFlowProblem::solve} - // - // This function solves the linear systems - // of equations. Following the - // introduction, we start with the Stokes - // system, where we need to generate our - // block Schur preconditioner. Since all - // the relevant actions are implemented in - // the class - // BlockSchurPreconditioner, - // all we have to do is to initialize the - // class appropriately. What we need to - // pass down is an - // InverseMatrix object for - // the pressure mass matrix, which we set - // up using the respective class together - // with the IC preconditioner we already - // generated, and the AMG preconditioner - // for the velocity-velocity matrix. Note - // that both Mp_preconditioner - // and Amg_preconditioner are - // only pointers, so we use * - // to pass down the actual preconditioner - // objects. - // - // Once the preconditioner is ready, we - // create a GMRES solver for the block - // system. Since we are working with - // Trilinos data structures, we have to set - // the respective template argument in the - // solver. GMRES needs to internally store - // temporary vectors for each iteration - // (see the discussion in the results - // section of step-22) – the more - // vectors it can use, the better it will - // generally perform. To keep memory - // demands in check, we set the number of - // vectors to 100. This means that up to - // 100 solver iterations, every temporary - // vector can be stored. If the solver - // needs to iterate more often to get the - // specified tolerance, it will work on a - // reduced set of vectors by restarting at - // every 100 iterations. - // - // With this all set up, we solve the system - // and distribute the constraints in the - // Stokes system, i.e. hanging nodes and - // no-flux boundary condition, in order to - // have the appropriate solution values even - // at constrained dofs. Finally, we write the - // number of iterations to the screen. + // @sect4{BoussinesqFlowProblem::solve} + // + // This function solves the linear systems + // of equations. Following the + // introduction, we start with the Stokes + // system, where we need to generate our + // block Schur preconditioner. Since all + // the relevant actions are implemented in + // the class + // BlockSchurPreconditioner, + // all we have to do is to initialize the + // class appropriately. What we need to + // pass down is an + // InverseMatrix object for + // the pressure mass matrix, which we set + // up using the respective class together + // with the IC preconditioner we already + // generated, and the AMG preconditioner + // for the velocity-velocity matrix. Note + // that both Mp_preconditioner + // and Amg_preconditioner are + // only pointers, so we use * + // to pass down the actual preconditioner + // objects. + // + // Once the preconditioner is ready, we + // create a GMRES solver for the block + // system. Since we are working with + // Trilinos data structures, we have to set + // the respective template argument in the + // solver. GMRES needs to internally store + // temporary vectors for each iteration + // (see the discussion in the results + // section of step-22) – the more + // vectors it can use, the better it will + // generally perform. To keep memory + // demands in check, we set the number of + // vectors to 100. This means that up to + // 100 solver iterations, every temporary + // vector can be stored. If the solver + // needs to iterate more often to get the + // specified tolerance, it will work on a + // reduced set of vectors by restarting at + // every 100 iterations. + // + // With this all set up, we solve the system + // and distribute the constraints in the + // Stokes system, i.e. hanging nodes and + // no-flux boundary condition, in order to + // have the appropriate solution values even + // at constrained dofs. Finally, we write the + // number of iterations to the screen. template void BoussinesqFlowProblem::solve () { @@ -2241,19 +2242,19 @@ namespace Step31 { const LinearSolvers::InverseMatrix - mp_inverse (stokes_preconditioner_matrix.block(1,1), *Mp_preconditioner); + TrilinosWrappers::PreconditionIC> + mp_inverse (stokes_preconditioner_matrix.block(1,1), *Mp_preconditioner); const LinearSolvers::BlockSchurPreconditioner - preconditioner (stokes_matrix, mp_inverse, *Amg_preconditioner); + TrilinosWrappers::PreconditionIC> + preconditioner (stokes_matrix, mp_inverse, *Amg_preconditioner); SolverControl solver_control (stokes_matrix.m(), 1e-6*stokes_rhs.l2_norm()); SolverGMRES - gmres (solver_control, - SolverGMRES::AdditionalData(100)); + gmres (solver_control, + SolverGMRES::AdditionalData(100)); for (unsigned int i=0; iassemble_temperature_system(). - // Knowing the matrix and right hand side - // of the temperature equation, we set up - // a preconditioner and a solver. The - // temperature matrix is a mass matrix - // (with eigenvalues around one) plus a - // Laplace matrix (with eigenvalues - // between zero and $ch^{-2}$) times a - // small number proportional to the time - // step $k_n$. Hence, the resulting - // symmetric and positive definite matrix - // has eigenvalues in the range - // $[1,1+k_nh^{-2}]$ (up to - // constants). This matrix is only - // moderately ill conditioned even for - // small mesh sizes and we get a - // reasonably good preconditioner by - // simple means, for example with an - // incomplete Cholesky decomposition - // preconditioner (IC) as we also use for - // preconditioning the pressure mass - // matrix solver. As a solver, we choose - // the conjugate gradient method CG. As - // before, we tell the solver to use - // Trilinos vectors via the template - // argument - // TrilinosWrappers::Vector. - // Finally, we solve, distribute the - // hanging node constraints and write out - // the number of iterations. + // Next we set up the temperature system + // and the right hand side using the + // function + // assemble_temperature_system(). + // Knowing the matrix and right hand side + // of the temperature equation, we set up + // a preconditioner and a solver. The + // temperature matrix is a mass matrix + // (with eigenvalues around one) plus a + // Laplace matrix (with eigenvalues + // between zero and $ch^{-2}$) times a + // small number proportional to the time + // step $k_n$. Hence, the resulting + // symmetric and positive definite matrix + // has eigenvalues in the range + // $[1,1+k_nh^{-2}]$ (up to + // constants). This matrix is only + // moderately ill conditioned even for + // small mesh sizes and we get a + // reasonably good preconditioner by + // simple means, for example with an + // incomplete Cholesky decomposition + // preconditioner (IC) as we also use for + // preconditioning the pressure mass + // matrix solver. As a solver, we choose + // the conjugate gradient method CG. As + // before, we tell the solver to use + // Trilinos vectors via the template + // argument + // TrilinosWrappers::Vector. + // Finally, we solve, distribute the + // hanging node constraints and write out + // the number of iterations. assemble_temperature_system (maximal_velocity); { @@ -2374,14 +2375,14 @@ namespace Step31 << " CG iterations for temperature." << std::endl; - // At the end of this function, we step - // through the vector and read out the - // maximum and minimum temperature value, - // which we also want to output. This - // will come in handy when determining - // the correct constant in the choice of - // time step as discuss in the results - // section of this program. + // At the end of this function, we step + // through the vector and read out the + // maximum and minimum temperature value, + // which we also want to output. This + // will come in handy when determining + // the correct constant in the choice of + // time step as discuss in the results + // section of this program. double min_temperature = temperature_solution(0), max_temperature = temperature_solution(0); for (unsigned int i=0; i void BoussinesqFlowProblem::output_results () const { @@ -2452,62 +2453,62 @@ namespace Step31 Vector joint_solution (joint_dof_handler.n_dofs()); - // Unfortunately, there is no - // straight-forward relation that tells - // us how to sort Stokes and temperature - // vector into the joint vector. The way - // we can get around this trouble is to - // rely on the information collected in - // the FESystem. For each dof in a cell, - // the joint finite element knows to - // which equation component (velocity - // component, pressure, or temperature) - // it belongs – that's the - // information we need! So we step - // through all cells (with iterators into - // all three DoFHandlers moving in - // synch), and for each joint cell dof, - // we read out that component using the - // FiniteElement::system_to_base_index - // function (see there for a description - // of what the various parts of its - // return value contain). We also need to - // keep track whether we're on a Stokes - // dof or a temperature dof, which is - // contained in - // joint_fe.system_to_base_index(i).first.first. - // Eventually, the dof_indices data - // structures on either of the three - // systems tell us how the relation - // between global vector and local dofs - // looks like on the present cell, which - // concludes this tedious work. - // - // There's one thing worth remembering - // when looking at the output: In our - // algorithm, we first solve for the - // Stokes system at time level n-1 - // in each time step and then for the - // temperature at time level n - // using the previously computed - // velocity. These are the two components - // we join for output, so these two parts - // of the output file are actually - // misaligned by one time step. Since we - // consider graphical output as only a - // qualititative means to understand a - // solution, we ignore this - // $\mathcal{O}(h)$ error. + // Unfortunately, there is no + // straight-forward relation that tells + // us how to sort Stokes and temperature + // vector into the joint vector. The way + // we can get around this trouble is to + // rely on the information collected in + // the FESystem. For each dof in a cell, + // the joint finite element knows to + // which equation component (velocity + // component, pressure, or temperature) + // it belongs – that's the + // information we need! So we step + // through all cells (with iterators into + // all three DoFHandlers moving in + // synch), and for each joint cell dof, + // we read out that component using the + // FiniteElement::system_to_base_index + // function (see there for a description + // of what the various parts of its + // return value contain). We also need to + // keep track whether we're on a Stokes + // dof or a temperature dof, which is + // contained in + // joint_fe.system_to_base_index(i).first.first. + // Eventually, the dof_indices data + // structures on either of the three + // systems tell us how the relation + // between global vector and local dofs + // looks like on the present cell, which + // concludes this tedious work. + // + // There's one thing worth remembering + // when looking at the output: In our + // algorithm, we first solve for the + // Stokes system at time level n-1 + // in each time step and then for the + // temperature at time level n + // using the previously computed + // velocity. These are the two components + // we join for output, so these two parts + // of the output file are actually + // misaligned by one time step. Since we + // consider graphical output as only a + // qualititative means to understand a + // solution, we ignore this + // $\mathcal{O}(h)$ error. { std::vector local_joint_dof_indices (joint_fe.dofs_per_cell); std::vector local_stokes_dof_indices (stokes_fe.dofs_per_cell); std::vector local_temperature_dof_indices (temperature_fe.dofs_per_cell); typename DoFHandler::active_cell_iterator - joint_cell = joint_dof_handler.begin_active(), - joint_endc = joint_dof_handler.end(), - stokes_cell = stokes_dof_handler.begin_active(), - temperature_cell = temperature_dof_handler.begin_active(); + joint_cell = joint_dof_handler.begin_active(), + joint_endc = joint_dof_handler.end(), + stokes_cell = stokes_dof_handler.begin_active(), + temperature_cell = temperature_dof_handler.begin_active(); for (; joint_cell!=joint_endc; ++joint_cell, ++stokes_cell, ++temperature_cell) { joint_cell->get_dof_indices (local_joint_dof_indices); @@ -2538,28 +2539,28 @@ namespace Step31 } } - // Next, we proceed as we've done in - // step-22. We create solution names - // (that are going to appear in the - // visualization program for the - // individual components), and attach the - // joint dof handler to a DataOut - // object. The first dim - // components are the vector velocity, - // and then we have pressure and - // temperature. This information is read - // out using the - // DataComponentInterpretation helper - // class. Next, we attach the solution - // values together with the names of its - // components to the output object, and - // build patches according to the degree - // of freedom, which are (sub-) elements - // that describe the data for - // visualization programs. Finally, we - // set a file name (that includes the - // time step number) and write the vtk - // file. + // Next, we proceed as we've done in + // step-22. We create solution names + // (that are going to appear in the + // visualization program for the + // individual components), and attach the + // joint dof handler to a DataOut + // object. The first dim + // components are the vector velocity, + // and then we have pressure and + // temperature. This information is read + // out using the + // DataComponentInterpretation helper + // class. Next, we attach the solution + // values together with the names of its + // components to the output object, and + // build patches according to the degree + // of freedom, which are (sub-) elements + // that describe the data for + // visualization programs. Finally, we + // set a file name (that includes the + // time step number) and write the vtk + // file. std::vector joint_solution_names (dim, "velocity"); joint_solution_names.push_back ("p"); joint_solution_names.push_back ("T"); @@ -2569,8 +2570,8 @@ namespace Step31 data_out.attach_dof_handler (joint_dof_handler); std::vector - data_component_interpretation - (dim+2, DataComponentInterpretation::component_is_scalar); + data_component_interpretation + (dim+2, DataComponentInterpretation::component_is_scalar); for (unsigned int i=0; irefine_and_coarsen_fixed_fraction - // that refines those cells with the largest - // estimated error that together make up 80 - // per cent of the error, and coarsens those - // cells with the smallest error that make up - // for a combined 10 per cent of the - // error. - // - // If implemented like this, we would get a - // program that will not make much progress: - // Remember that we expect temperature fields - // that are nearly discontinuous (the - // diffusivity $\kappa$ is very small after - // all) and consequently we can expect that a - // freely adapted mesh will refine further - // and further into the areas of large - // gradients. This decrease in mesh size will - // then be accompanied by a decrease in time - // step, requiring an exceedingly large - // number of time steps to solve to a given - // final time. It will also lead to meshes - // that are much better at resolving - // discontinuities after several mesh - // refinement cycles than in the beginning. - // - // In particular to prevent the decrease in - // time step size and the correspondingly - // large number of time steps, we limit the - // maximal refinement depth of the mesh. To - // this end, after the refinement indicator - // has been applied to the cells, we simply - // loop over all cells on the finest level - // and unselect them from refinement if they - // would result in too high a mesh level. + // @sect4{BoussinesqFlowProblem::refine_mesh} + // + // This function takes care of the adaptive + // mesh refinement. The three tasks this + // function performs is to first find out + // which cells to refine/coarsen, then to + // actually do the refinement and eventually + // transfer the solution vectors between the + // two different grids. The first task is + // simply achieved by using the + // well-established Kelly error estimator on + // the temperature (it is the temperature + // we're mainly interested in for this + // program, and we need to be accurate in + // regions of high temperature gradients, + // also to not have too much numerical + // diffusion). The second task is to actually + // do the remeshing. That involves only basic + // functions as well, such as the + // refine_and_coarsen_fixed_fraction + // that refines those cells with the largest + // estimated error that together make up 80 + // per cent of the error, and coarsens those + // cells with the smallest error that make up + // for a combined 10 per cent of the + // error. + // + // If implemented like this, we would get a + // program that will not make much progress: + // Remember that we expect temperature fields + // that are nearly discontinuous (the + // diffusivity $\kappa$ is very small after + // all) and consequently we can expect that a + // freely adapted mesh will refine further + // and further into the areas of large + // gradients. This decrease in mesh size will + // then be accompanied by a decrease in time + // step, requiring an exceedingly large + // number of time steps to solve to a given + // final time. It will also lead to meshes + // that are much better at resolving + // discontinuities after several mesh + // refinement cycles than in the beginning. + // + // In particular to prevent the decrease in + // time step size and the correspondingly + // large number of time steps, we limit the + // maximal refinement depth of the mesh. To + // this end, after the refinement indicator + // has been applied to the cells, we simply + // loop over all cells on the finest level + // and unselect them from refinement if they + // would result in too high a mesh level. template void BoussinesqFlowProblem::refine_mesh (const unsigned int max_grid_level) { @@ -2658,80 +2659,80 @@ namespace Step31 0.8, 0.1); if (triangulation.n_levels() > max_grid_level) for (typename Triangulation::active_cell_iterator - cell = triangulation.begin_active(max_grid_level); + cell = triangulation.begin_active(max_grid_level); cell != triangulation.end(); ++cell) cell->clear_refine_flag (); - // As part of mesh refinement we need to - // transfer the solution vectors from the - // old mesh to the new one. To this end - // we use the SolutionTransfer class and - // we have to prepare the solution - // vectors that should be transferred to - // the new grid (we will lose the old - // grid once we have done the refinement - // so the transfer has to happen - // concurrently with refinement). What we - // definetely need are the current and - // the old temperature (BDF-2 time - // stepping requires two old - // solutions). Since the SolutionTransfer - // objects only support to transfer one - // object per dof handler, we need to - // collect the two temperature solutions - // in one data structure. Moreover, we - // choose to transfer the Stokes - // solution, too, since we need the - // velocity at two previous time steps, - // of which only one is calculated on the - // fly. - // - // Consequently, we initialize two - // SolutionTransfer objects for the - // Stokes and temperature DoFHandler - // objects, by attaching them to the old - // dof handlers. With this at place, we - // can prepare the triangulation and the - // data vectors for refinement (in this - // order). + // As part of mesh refinement we need to + // transfer the solution vectors from the + // old mesh to the new one. To this end + // we use the SolutionTransfer class and + // we have to prepare the solution + // vectors that should be transferred to + // the new grid (we will lose the old + // grid once we have done the refinement + // so the transfer has to happen + // concurrently with refinement). What we + // definetely need are the current and + // the old temperature (BDF-2 time + // stepping requires two old + // solutions). Since the SolutionTransfer + // objects only support to transfer one + // object per dof handler, we need to + // collect the two temperature solutions + // in one data structure. Moreover, we + // choose to transfer the Stokes + // solution, too, since we need the + // velocity at two previous time steps, + // of which only one is calculated on the + // fly. + // + // Consequently, we initialize two + // SolutionTransfer objects for the + // Stokes and temperature DoFHandler + // objects, by attaching them to the old + // dof handlers. With this at place, we + // can prepare the triangulation and the + // data vectors for refinement (in this + // order). std::vector x_temperature (2); x_temperature[0] = temperature_solution; x_temperature[1] = old_temperature_solution; TrilinosWrappers::BlockVector x_stokes = stokes_solution; SolutionTransfer - temperature_trans(temperature_dof_handler); + temperature_trans(temperature_dof_handler); SolutionTransfer - stokes_trans(stokes_dof_handler); + stokes_trans(stokes_dof_handler); triangulation.prepare_coarsening_and_refinement(); temperature_trans.prepare_for_coarsening_and_refinement(x_temperature); stokes_trans.prepare_for_coarsening_and_refinement(x_stokes); - // Now everything is ready, so do the - // refinement and recreate the dof - // structure on the new grid, and - // initialize the matrix structures and - // the new vectors in the - // setup_dofs - // function. Next, we actually perform - // the interpolation of the solutions - // between the grids. We create another - // copy of temporary vectors for - // temperature (now corresponding to the - // new grid), and let the interpolate - // function do the job. Then, the - // resulting array of vectors is written - // into the respective vector member - // variables. For the Stokes vector, - // everything is just the same – - // except that we do not need another - // temporary vector since we just - // interpolate a single vector. In the - // end, we have to tell the program that - // the matrices and preconditioners need - // to be regenerated, since the mesh has - // changed. + // Now everything is ready, so do the + // refinement and recreate the dof + // structure on the new grid, and + // initialize the matrix structures and + // the new vectors in the + // setup_dofs + // function. Next, we actually perform + // the interpolation of the solutions + // between the grids. We create another + // copy of temporary vectors for + // temperature (now corresponding to the + // new grid), and let the interpolate + // function do the job. Then, the + // resulting array of vectors is written + // into the respective vector member + // variables. For the Stokes vector, + // everything is just the same – + // except that we do not need another + // temporary vector since we just + // interpolate a single vector. In the + // end, we have to tell the program that + // the matrices and preconditioners need + // to be regenerated, since the mesh has + // changed. triangulation.execute_coarsening_and_refinement (); setup_dofs (); @@ -2752,41 +2753,41 @@ namespace Step31 - // @sect4{BoussinesqFlowProblem::run} - // - // This function performs all the - // essential steps in the Boussinesq - // program. It starts by setting up a - // grid (depending on the spatial - // dimension, we choose some - // different level of initial - // refinement and additional adaptive - // refinement steps, and then create - // a cube in dim - // dimensions and set up the dofs for - // the first time. Since we want to - // start the time stepping already - // with an adaptively refined grid, - // we perform some pre-refinement - // steps, consisting of all assembly, - // solution and refinement, but - // without actually advancing in - // time. Rather, we use the vilified - // goto statement to - // jump out of the time loop right - // after mesh refinement to start all - // over again on the new mesh - // beginning at the - // start_time_iteration - // label. - // - // Before we start, we project the - // initial values to the grid and - // obtain the first data for the - // old_temperature_solution - // vector. Then, we initialize time - // step number and time step and - // start the time loop. + // @sect4{BoussinesqFlowProblem::run} + // + // This function performs all the + // essential steps in the Boussinesq + // program. It starts by setting up a + // grid (depending on the spatial + // dimension, we choose some + // different level of initial + // refinement and additional adaptive + // refinement steps, and then create + // a cube in dim + // dimensions and set up the dofs for + // the first time. Since we want to + // start the time stepping already + // with an adaptively refined grid, + // we perform some pre-refinement + // steps, consisting of all assembly, + // solution and refinement, but + // without actually advancing in + // time. Rather, we use the vilified + // goto statement to + // jump out of the time loop right + // after mesh refinement to start all + // over again on the new mesh + // beginning at the + // start_time_iteration + // label. + // + // Before we start, we project the + // initial values to the grid and + // obtain the first data for the + // old_temperature_solution + // vector. Then, we initialize time + // step number and time step and + // start the time loop. template void BoussinesqFlowProblem::run () { @@ -2803,7 +2804,7 @@ namespace Step31 unsigned int pre_refinement_step = 0; - start_time_iteration: +start_time_iteration: VectorTools::project (temperature_dof_handler, temperature_constraints, @@ -2822,26 +2823,26 @@ namespace Step31 << ": t=" << time << std::endl; - // The first steps in the time loop - // are all obvious – we - // assemble the Stokes system, the - // preconditioner, the temperature - // matrix (matrices and - // preconditioner do actually only - // change in case we've remeshed - // before), and then do the - // solve. Before going on - // with the next time step, we have - // to check whether we should first - // finish the pre-refinement steps or - // if we should remesh (every fifth - // time step), refining up to a level - // that is consistent with initial - // refinement and pre-refinement - // steps. Last in the loop is to - // advance the solutions, i.e. to - // copy the solutions to the next - // "older" time level. + // The first steps in the time loop + // are all obvious – we + // assemble the Stokes system, the + // preconditioner, the temperature + // matrix (matrices and + // preconditioner do actually only + // change in case we've remeshed + // before), and then do the + // solve. Before going on + // with the next time step, we have + // to check whether we should first + // finish the pre-refinement steps or + // if we should remesh (every fifth + // time step), refining up to a level + // that is consistent with initial + // refinement and pre-refinement + // steps. Last in the loop is to + // advance the solutions, i.e. to + // copy the solutions to the next + // "older" time level. assemble_stokes_system (); build_stokes_preconditioner (); assemble_temperature_matrix (); @@ -2859,9 +2860,8 @@ namespace Step31 ++pre_refinement_step; goto start_time_iteration; } - else - if ((timestep_number > 0) && (timestep_number % 5 == 0)) - refine_mesh (initial_refinement + n_pre_refinement_steps); + else if ((timestep_number > 0) && (timestep_number % 5 == 0)) + refine_mesh (initial_refinement + n_pre_refinement_steps); time += time_step; ++timestep_number; @@ -2870,36 +2870,36 @@ namespace Step31 old_old_temperature_solution = old_temperature_solution; old_temperature_solution = temperature_solution; } - // Do all the above until we arrive at - // time 100. + // Do all the above until we arrive at + // time 100. while (time <= 100); } } - // @sect3{The main function} - // - // The main function looks almost the same - // as in all other programs. - // - // There is one difference we have to be - // careful about. This program uses Trilinos - // and, typically, Trilinos is configured so - // that it can run in %parallel using - // MPI. This doesn't mean that it has - // to run in %parallel, and in fact this - // program (unlike step-32) makes no attempt - // at all to do anything in %parallel using - // MPI. Nevertheless, Trilinos wants the MPI - // system to be initialized. We do that be - // creating an object of type - // Utilities::MPI::MPI_InitFinalize that - // initializes MPI (if available) using the - // arguments given to main() (i.e., - // argc and argv) - // and de-initializes it again when the - // object goes out of scope. +// @sect3{The main function} +// +// The main function looks almost the same +// as in all other programs. +// +// There is one difference we have to be +// careful about. This program uses Trilinos +// and, typically, Trilinos is configured so +// that it can run in %parallel using +// MPI. This doesn't mean that it has +// to run in %parallel, and in fact this +// program (unlike step-32) makes no attempt +// at all to do anything in %parallel using +// MPI. Nevertheless, Trilinos wants the MPI +// system to be initialized. We do that be +// creating an object of type +// Utilities::MPI::MPI_InitFinalize that +// initializes MPI (if available) using the +// arguments given to main() (i.e., +// argc and argv) +// and de-initializes it again when the +// object goes out of scope. int main (int argc, char *argv[]) { try diff --git a/deal.II/examples/step-32/step-32.cc b/deal.II/examples/step-32/step-32.cc index f75422b4d8..b81ffa873d 100644 --- a/deal.II/examples/step-32/step-32.cc +++ b/deal.II/examples/step-32/step-32.cc @@ -9,12 +9,12 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - //The first task as usual is to - // include the functionality of these - // well-known deal.II library files - // and some C++ header files. +//The first task as usual is to +// include the functionality of these +// well-known deal.II library files +// and some C++ header files. #include #include #include @@ -70,52 +70,52 @@ #include #include - // This is the only include file that - // is new: It introduces the - // parallel::distributed::SolutionTransfer - // equivalent of the - // dealii::SolutionTransfer class to - // take a solution from on mesh to - // the next one upon mesh refinement, - // but in the case of parallel - // distributed triangulations: +// This is the only include file that +// is new: It introduces the +// parallel::distributed::SolutionTransfer +// equivalent of the +// dealii::SolutionTransfer class to +// take a solution from on mesh to +// the next one upon mesh refinement, +// but in the case of parallel +// distributed triangulations: #include - // The following classes are used in - // parallel distributed computations - // and have all already been - // introduced in step-40: +// The following classes are used in +// parallel distributed computations +// and have all already been +// introduced in step-40: #include #include #include - // The next step is like in all - // previous tutorial programs: We put - // everything into a namespace of its - // own and then import the deal.II - // classes and functions into it: +// The next step is like in all +// previous tutorial programs: We put +// everything into a namespace of its +// own and then import the deal.II +// classes and functions into it: namespace Step32 { using namespace dealii; - // @sect3{Equation data} - - // In the following namespace, we - // define the various pieces of - // equation data that describe the - // problem. This corresponds to the - // various aspects of making the - // problem at least slightly - // realistc and that were - // exhaustively discussed in the - // description of the testcase in - // the introduction. - // - // We start with a few coefficients - // that have constant values (the - // comment after the value - // indicates its physical units): + // @sect3{Equation data} + + // In the following namespace, we + // define the various pieces of + // equation data that describe the + // problem. This corresponds to the + // various aspects of making the + // problem at least slightly + // realistc and that were + // exhaustively discussed in the + // description of the testcase in + // the introduction. + // + // We start with a few coefficients + // that have constant values (the + // comment after the value + // indicates its physical units): namespace EquationData { const double eta = 1e21; /* Pa s */ @@ -134,20 +134,20 @@ namespace Step32 const double T1 = 700+273; /* K */ - // The next set of definitions - // are for functions that encode - // the density as a function of - // temperature, the gravity - // vector, and the initial values - // for the temperature. Again, - // all of these (along with the - // values they compute) are - // discussed in the introduction: + // The next set of definitions + // are for functions that encode + // the density as a function of + // temperature, the gravity + // vector, and the initial values + // for the temperature. Again, + // all of these (along with the + // values they compute) are + // discussed in the introduction: double density (const double temperature) { return (reference_density * - (1 - expansion_coefficient * (temperature - - reference_temperature))); + (1 - expansion_coefficient * (temperature - + reference_temperature))); } @@ -163,14 +163,14 @@ namespace Step32 template class TemperatureInitialValues : public Function { - public: - TemperatureInitialValues () : Function(1) {} + public: + TemperatureInitialValues () : Function(1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void vector_value (const Point &p, - Vector &value) const; + virtual void vector_value (const Point &p, + Vector &value) const; }; @@ -178,7 +178,7 @@ namespace Step32 template double TemperatureInitialValues::value (const Point &p, - const unsigned int) const + const unsigned int) const { const double r = p.norm(); const double h = R1-R0; @@ -187,8 +187,8 @@ namespace Step32 const double q = (dim==3)?std::max(0.0,cos(numbers::PI*abs(p(2)/R1))):1.0; const double phi = std::atan2(p(0),p(1)); const double tau = s - + - 0.2 * s * (1-s) * std::sin(6*phi) * q; + + + 0.2 * s * (1-s) * std::sin(6*phi) * q; return T0*(1.0-tau) + T1*tau; } @@ -197,230 +197,230 @@ namespace Step32 template void TemperatureInitialValues::vector_value (const Point &p, - Vector &values) const + Vector &values) const { for (unsigned int c=0; cn_components; ++c) - values(c) = TemperatureInitialValues::value (p, c); + values(c) = TemperatureInitialValues::value (p, c); } - // As mentioned in the - // introduction we need to - // rescale the pressure to avoid - // the relative ill-conditioning - // of the momentum and mass - // conservation equations. The - // scaling factor is - // $\frac{\eta}{L}$ where $L$ was - // a typical length scale. By - // experimenting it turns out - // that a good length scale is - // the diameter of plumes, which - // is around 10 km: + // As mentioned in the + // introduction we need to + // rescale the pressure to avoid + // the relative ill-conditioning + // of the momentum and mass + // conservation equations. The + // scaling factor is + // $\frac{\eta}{L}$ where $L$ was + // a typical length scale. By + // experimenting it turns out + // that a good length scale is + // the diameter of plumes, which + // is around 10 km: const double pressure_scaling = eta / 10000; - // The final number in this - // namespace is a constant that - // denotes the number of seconds - // per (average, tropical) - // year. We use this only when - // generating screen output: - // internally, all computations - // of this program happen in SI - // units (kilogram, meter, - // seconds) but writing - // geological times in seconds - // yields numbers that one can't - // relate to reality, and so we - // convert to years using the - // factor defined here: + // The final number in this + // namespace is a constant that + // denotes the number of seconds + // per (average, tropical) + // year. We use this only when + // generating screen output: + // internally, all computations + // of this program happen in SI + // units (kilogram, meter, + // seconds) but writing + // geological times in seconds + // yields numbers that one can't + // relate to reality, and so we + // convert to years using the + // factor defined here: const double year_in_seconds = 60*60*24*365.2425; } - // @sect3{Preconditioning the Stokes system} - - // This namespace implements the - // preconditioner. As discussed in the - // introduction, this preconditioner - // differs in a number of key portions from - // the one used in step-31. Specifically, - // it is a right preconditioner, - // implementing the matrix - // @f{align*}\left(\begin{array}{cc}A^{-1} - // & B^T \\ 0 & S^{-1}\end{array}\right)@f} - // where the two inverse matrix operations - // are approximated by linear solvers or, - // if the right flag is given to the - // constructor of this class, by a single - // AMG V-cycle for the velocity block. The - // three code blocks of the - // vmult function implement - // the multiplications with the three - // blocks of this preconditioner matrix and - // should be self explanatory if you have - // read through step-31 or the discussion - // of compositing solvers in step-20. + // @sect3{Preconditioning the Stokes system} + + // This namespace implements the + // preconditioner. As discussed in the + // introduction, this preconditioner + // differs in a number of key portions from + // the one used in step-31. Specifically, + // it is a right preconditioner, + // implementing the matrix + // @f{align*}\left(\begin{array}{cc}A^{-1} + // & B^T \\ 0 & S^{-1}\end{array}\right)@f} + // where the two inverse matrix operations + // are approximated by linear solvers or, + // if the right flag is given to the + // constructor of this class, by a single + // AMG V-cycle for the velocity block. The + // three code blocks of the + // vmult function implement + // the multiplications with the three + // blocks of this preconditioner matrix and + // should be self explanatory if you have + // read through step-31 or the discussion + // of compositing solvers in step-20. namespace LinearSolvers { template class BlockSchurPreconditioner : public Subscriptor { - public: - BlockSchurPreconditioner (const TrilinosWrappers::BlockSparseMatrix &S, - const TrilinosWrappers::BlockSparseMatrix &Spre, - const PreconditionerMp &Mppreconditioner, - const PreconditionerA &Apreconditioner, - const bool do_solve_A) - : - stokes_matrix (&S), - stokes_preconditioner_matrix (&Spre), - mp_preconditioner (Mppreconditioner), - a_preconditioner (Apreconditioner), - do_solve_A (do_solve_A) - {} - - void vmult (TrilinosWrappers::MPI::BlockVector &dst, - const TrilinosWrappers::MPI::BlockVector &src) const - { - TrilinosWrappers::MPI::Vector utmp(src.block(0)); - - { - SolverControl solver_control(5000, 1e-6 * src.block(1).l2_norm()); - - SolverCG solver(solver_control); - - solver.solve(stokes_preconditioner_matrix->block(1,1), - dst.block(1), src.block(1), - mp_preconditioner); - - dst.block(1) *= -1.0; - } - - { - stokes_matrix->block(0,1).vmult(utmp, dst.block(1)); - utmp*=-1.0; - utmp.add(src.block(0)); - } - - if (do_solve_A == true) - { - SolverControl solver_control(5000, utmp.l2_norm()*1e-2); - TrilinosWrappers::SolverCG solver(solver_control); - solver.solve(stokes_matrix->block(0,0), dst.block(0), utmp, - a_preconditioner); - } - else - a_preconditioner.vmult (dst.block(0), utmp); - } - - private: - const SmartPointer stokes_matrix; - const SmartPointer stokes_preconditioner_matrix; - const PreconditionerMp &mp_preconditioner; - const PreconditionerA &a_preconditioner; - const bool do_solve_A; + public: + BlockSchurPreconditioner (const TrilinosWrappers::BlockSparseMatrix &S, + const TrilinosWrappers::BlockSparseMatrix &Spre, + const PreconditionerMp &Mppreconditioner, + const PreconditionerA &Apreconditioner, + const bool do_solve_A) + : + stokes_matrix (&S), + stokes_preconditioner_matrix (&Spre), + mp_preconditioner (Mppreconditioner), + a_preconditioner (Apreconditioner), + do_solve_A (do_solve_A) + {} + + void vmult (TrilinosWrappers::MPI::BlockVector &dst, + const TrilinosWrappers::MPI::BlockVector &src) const + { + TrilinosWrappers::MPI::Vector utmp(src.block(0)); + + { + SolverControl solver_control(5000, 1e-6 * src.block(1).l2_norm()); + + SolverCG solver(solver_control); + + solver.solve(stokes_preconditioner_matrix->block(1,1), + dst.block(1), src.block(1), + mp_preconditioner); + + dst.block(1) *= -1.0; + } + + { + stokes_matrix->block(0,1).vmult(utmp, dst.block(1)); + utmp*=-1.0; + utmp.add(src.block(0)); + } + + if (do_solve_A == true) + { + SolverControl solver_control(5000, utmp.l2_norm()*1e-2); + TrilinosWrappers::SolverCG solver(solver_control); + solver.solve(stokes_matrix->block(0,0), dst.block(0), utmp, + a_preconditioner); + } + else + a_preconditioner.vmult (dst.block(0), utmp); + } + + private: + const SmartPointer stokes_matrix; + const SmartPointer stokes_preconditioner_matrix; + const PreconditionerMp &mp_preconditioner; + const PreconditionerA &a_preconditioner; + const bool do_solve_A; }; } - // @sect3{Definition of assembly data structures} - // - // As described in the - // introduction, we will use the - // WorkStream mechanism discussed - // in the @ref threads module to - // parallelize operations among the - // processors of a single - // machine. The WorkStream class - // requires that data is passed - // around in two kinds of data - // structures, one for scratch data - // and one to pass data from the - // assembly function to the - // function that copies local - // contributions into global - // objects. - // - // The following namespace (and the - // two sub-namespaces) contains a - // collection of data structures - // that serve this purpose, one - // pair for each of the four - // operations discussed in the - // introduction that we will want - // to parallelize. Each assembly - // routine gets two sets of data: a - // Scratch array that collects all - // the classes and arrays that are - // used for the calculation of the - // cell contribution, and a - // CopyData array that keeps local - // matrices and vectors which will - // be written into the global - // matrix. Whereas CopyData is a - // container for the final data - // that is written into the global - // matrices and vector (and, thus, - // absolutely necessary), the - // Scratch arrays are merely there - // for performance reasons — - // it would be much more expensive - // to set up a FEValues object on - // each cell, than creating it only - // once and updating some - // derivative data. - // - // Step-31 had four assembly - // routines: One for the - // preconditioner matrix of the - // Stokes system, one for the - // Stokes matrix and right hand - // side, one for the temperature - // matrices and one for the right - // hand side of the temperature - // equation. We here organize the - // scratch arrays and CopyData - // objects for each of those four - // assembly components using a - // struct environment - // (since we consider these as - // temporary objects we pass - // around, rather than classes that - // implement functionality of their - // own, though this is a more - // subjective point of view to - // distinguish between - // structs and - // classes). - // - // Regarding the Scratch objects, - // each struct is equipped with a - // constructor that creates an - // FEValues object for a @ref - // FiniteElement "finite element", - // a @ref Quadrature "quadrature formula", - // the @ref Mapping "mapping" that - // describes the - // interpolation of curved - // boundaries, and some @ref - // UpdateFlags "update flags". - // Moreover, we manually implement - // a copy constructor (since the - // FEValues class is not copyable - // by itself), and provide some - // additional vector fields that - // are used to hold intermediate - // data during the computation of - // local contributions. - // - // Let us start with the scratch - // arrays and, specifically, the - // one used for assembly of the - // Stokes preconditioner: + // @sect3{Definition of assembly data structures} + // + // As described in the + // introduction, we will use the + // WorkStream mechanism discussed + // in the @ref threads module to + // parallelize operations among the + // processors of a single + // machine. The WorkStream class + // requires that data is passed + // around in two kinds of data + // structures, one for scratch data + // and one to pass data from the + // assembly function to the + // function that copies local + // contributions into global + // objects. + // + // The following namespace (and the + // two sub-namespaces) contains a + // collection of data structures + // that serve this purpose, one + // pair for each of the four + // operations discussed in the + // introduction that we will want + // to parallelize. Each assembly + // routine gets two sets of data: a + // Scratch array that collects all + // the classes and arrays that are + // used for the calculation of the + // cell contribution, and a + // CopyData array that keeps local + // matrices and vectors which will + // be written into the global + // matrix. Whereas CopyData is a + // container for the final data + // that is written into the global + // matrices and vector (and, thus, + // absolutely necessary), the + // Scratch arrays are merely there + // for performance reasons — + // it would be much more expensive + // to set up a FEValues object on + // each cell, than creating it only + // once and updating some + // derivative data. + // + // Step-31 had four assembly + // routines: One for the + // preconditioner matrix of the + // Stokes system, one for the + // Stokes matrix and right hand + // side, one for the temperature + // matrices and one for the right + // hand side of the temperature + // equation. We here organize the + // scratch arrays and CopyData + // objects for each of those four + // assembly components using a + // struct environment + // (since we consider these as + // temporary objects we pass + // around, rather than classes that + // implement functionality of their + // own, though this is a more + // subjective point of view to + // distinguish between + // structs and + // classes). + // + // Regarding the Scratch objects, + // each struct is equipped with a + // constructor that creates an + // FEValues object for a @ref + // FiniteElement "finite element", + // a @ref Quadrature "quadrature formula", + // the @ref Mapping "mapping" that + // describes the + // interpolation of curved + // boundaries, and some @ref + // UpdateFlags "update flags". + // Moreover, we manually implement + // a copy constructor (since the + // FEValues class is not copyable + // by itself), and provide some + // additional vector fields that + // are used to hold intermediate + // data during the computation of + // local contributions. + // + // Let us start with the scratch + // arrays and, specifically, the + // one used for assembly of the + // Stokes preconditioner: namespace Assembly { namespace Scratch @@ -428,31 +428,31 @@ namespace Step32 template struct StokesPreconditioner { - StokesPreconditioner (const FiniteElement &stokes_fe, - const Quadrature &stokes_quadrature, - const Mapping &mapping, - const UpdateFlags update_flags); + StokesPreconditioner (const FiniteElement &stokes_fe, + const Quadrature &stokes_quadrature, + const Mapping &mapping, + const UpdateFlags update_flags); - StokesPreconditioner (const StokesPreconditioner &data); + StokesPreconditioner (const StokesPreconditioner &data); - FEValues stokes_fe_values; + FEValues stokes_fe_values; - std::vector > grad_phi_u; - std::vector phi_p; + std::vector > grad_phi_u; + std::vector phi_p; }; template StokesPreconditioner:: StokesPreconditioner (const FiniteElement &stokes_fe, - const Quadrature &stokes_quadrature, - const Mapping &mapping, - const UpdateFlags update_flags) - : - stokes_fe_values (mapping, stokes_fe, stokes_quadrature, - update_flags), - grad_phi_u (stokes_fe.dofs_per_cell), - phi_p (stokes_fe.dofs_per_cell) + const Quadrature &stokes_quadrature, + const Mapping &mapping, + const UpdateFlags update_flags) + : + stokes_fe_values (mapping, stokes_fe, stokes_quadrature, + update_flags), + grad_phi_u (stokes_fe.dofs_per_cell), + phi_p (stokes_fe.dofs_per_cell) {} @@ -460,308 +460,308 @@ namespace Step32 template StokesPreconditioner:: StokesPreconditioner (const StokesPreconditioner &scratch) - : - stokes_fe_values (scratch.stokes_fe_values.get_mapping(), - scratch.stokes_fe_values.get_fe(), - scratch.stokes_fe_values.get_quadrature(), - scratch.stokes_fe_values.get_update_flags()), - grad_phi_u (scratch.grad_phi_u), - phi_p (scratch.phi_p) + : + stokes_fe_values (scratch.stokes_fe_values.get_mapping(), + scratch.stokes_fe_values.get_fe(), + scratch.stokes_fe_values.get_quadrature(), + scratch.stokes_fe_values.get_update_flags()), + grad_phi_u (scratch.grad_phi_u), + phi_p (scratch.phi_p) {} - // The next one is the scratch object - // used for the assembly of the full - // Stokes system. Observe that we - // derive the StokesSystem scratch - // class from the StokesPreconditioner - // class above. We do this because all the - // objects that are necessary for the - // assembly of the preconditioner are - // also needed for the actual matrix - // system and right hand side, plus - // some extra data. This makes the - // program more compact. Note also that - // the assembly of the Stokes system - // and the temperature right hand side - // further down requires data from - // temperature and velocity, - // respectively, so we actually need - // two FEValues objects for those two - // cases. + // The next one is the scratch object + // used for the assembly of the full + // Stokes system. Observe that we + // derive the StokesSystem scratch + // class from the StokesPreconditioner + // class above. We do this because all the + // objects that are necessary for the + // assembly of the preconditioner are + // also needed for the actual matrix + // system and right hand side, plus + // some extra data. This makes the + // program more compact. Note also that + // the assembly of the Stokes system + // and the temperature right hand side + // further down requires data from + // temperature and velocity, + // respectively, so we actually need + // two FEValues objects for those two + // cases. template struct StokesSystem : public StokesPreconditioner { - StokesSystem (const FiniteElement &stokes_fe, - const Mapping &mapping, - const Quadrature &stokes_quadrature, - const UpdateFlags stokes_update_flags, - const FiniteElement &temperature_fe, - const UpdateFlags temperature_update_flags); + StokesSystem (const FiniteElement &stokes_fe, + const Mapping &mapping, + const Quadrature &stokes_quadrature, + const UpdateFlags stokes_update_flags, + const FiniteElement &temperature_fe, + const UpdateFlags temperature_update_flags); - StokesSystem (const StokesSystem &data); + StokesSystem (const StokesSystem &data); - FEValues temperature_fe_values; + FEValues temperature_fe_values; - std::vector > phi_u; - std::vector > grads_phi_u; - std::vector div_phi_u; + std::vector > phi_u; + std::vector > grads_phi_u; + std::vector div_phi_u; - std::vector old_temperature_values; + std::vector old_temperature_values; }; template StokesSystem:: StokesSystem (const FiniteElement &stokes_fe, - const Mapping &mapping, - const Quadrature &stokes_quadrature, - const UpdateFlags stokes_update_flags, - const FiniteElement &temperature_fe, - const UpdateFlags temperature_update_flags) - : - StokesPreconditioner (stokes_fe, stokes_quadrature, - mapping, - stokes_update_flags), - temperature_fe_values (mapping, temperature_fe, stokes_quadrature, - temperature_update_flags), - phi_u (stokes_fe.dofs_per_cell), - grads_phi_u (stokes_fe.dofs_per_cell), - div_phi_u (stokes_fe.dofs_per_cell), - old_temperature_values (stokes_quadrature.size()) + const Mapping &mapping, + const Quadrature &stokes_quadrature, + const UpdateFlags stokes_update_flags, + const FiniteElement &temperature_fe, + const UpdateFlags temperature_update_flags) + : + StokesPreconditioner (stokes_fe, stokes_quadrature, + mapping, + stokes_update_flags), + temperature_fe_values (mapping, temperature_fe, stokes_quadrature, + temperature_update_flags), + phi_u (stokes_fe.dofs_per_cell), + grads_phi_u (stokes_fe.dofs_per_cell), + div_phi_u (stokes_fe.dofs_per_cell), + old_temperature_values (stokes_quadrature.size()) {} template StokesSystem:: StokesSystem (const StokesSystem &scratch) - : - StokesPreconditioner (scratch), - temperature_fe_values (scratch.temperature_fe_values.get_mapping(), - scratch.temperature_fe_values.get_fe(), - scratch.temperature_fe_values.get_quadrature(), - scratch.temperature_fe_values.get_update_flags()), - phi_u (scratch.phi_u), - grads_phi_u (scratch.grads_phi_u), - div_phi_u (scratch.div_phi_u), - old_temperature_values (scratch.old_temperature_values) + : + StokesPreconditioner (scratch), + temperature_fe_values (scratch.temperature_fe_values.get_mapping(), + scratch.temperature_fe_values.get_fe(), + scratch.temperature_fe_values.get_quadrature(), + scratch.temperature_fe_values.get_update_flags()), + phi_u (scratch.phi_u), + grads_phi_u (scratch.grads_phi_u), + div_phi_u (scratch.div_phi_u), + old_temperature_values (scratch.old_temperature_values) {} - // After defining the objects used in - // the assembly of the Stokes system, - // we do the same for the assembly of - // the matrices necessary for the - // temperature system. The general - // structure is very similar: + // After defining the objects used in + // the assembly of the Stokes system, + // we do the same for the assembly of + // the matrices necessary for the + // temperature system. The general + // structure is very similar: template struct TemperatureMatrix { - TemperatureMatrix (const FiniteElement &temperature_fe, - const Mapping &mapping, - const Quadrature &temperature_quadrature); + TemperatureMatrix (const FiniteElement &temperature_fe, + const Mapping &mapping, + const Quadrature &temperature_quadrature); - TemperatureMatrix (const TemperatureMatrix &data); + TemperatureMatrix (const TemperatureMatrix &data); - FEValues temperature_fe_values; + FEValues temperature_fe_values; - std::vector phi_T; - std::vector > grad_phi_T; + std::vector phi_T; + std::vector > grad_phi_T; }; template TemperatureMatrix:: TemperatureMatrix (const FiniteElement &temperature_fe, - const Mapping &mapping, - const Quadrature &temperature_quadrature) - : - temperature_fe_values (mapping, - temperature_fe, temperature_quadrature, - update_values | update_gradients | - update_JxW_values), - phi_T (temperature_fe.dofs_per_cell), - grad_phi_T (temperature_fe.dofs_per_cell) + const Mapping &mapping, + const Quadrature &temperature_quadrature) + : + temperature_fe_values (mapping, + temperature_fe, temperature_quadrature, + update_values | update_gradients | + update_JxW_values), + phi_T (temperature_fe.dofs_per_cell), + grad_phi_T (temperature_fe.dofs_per_cell) {} template TemperatureMatrix:: TemperatureMatrix (const TemperatureMatrix &scratch) - : - temperature_fe_values (scratch.temperature_fe_values.get_mapping(), - scratch.temperature_fe_values.get_fe(), - scratch.temperature_fe_values.get_quadrature(), - scratch.temperature_fe_values.get_update_flags()), - phi_T (scratch.phi_T), - grad_phi_T (scratch.grad_phi_T) + : + temperature_fe_values (scratch.temperature_fe_values.get_mapping(), + scratch.temperature_fe_values.get_fe(), + scratch.temperature_fe_values.get_quadrature(), + scratch.temperature_fe_values.get_update_flags()), + phi_T (scratch.phi_T), + grad_phi_T (scratch.grad_phi_T) {} - // The final scratch object is used in - // the assembly of the right hand side - // of the temperature system. This - // object is significantly larger than - // the ones above because a lot more - // quantities enter the computation of - // the right hand side of the - // temperature equation. In particular, - // the temperature values and gradients - // of the previous two time steps need - // to be evaluated at the quadrature - // points, as well as the velocities - // and the strain rates (i.e. the - // symmetric gradients of the velocity) - // that enter the right hand side as - // friction heating terms. Despite the - // number of terms, the following - // should be rather self explanatory: + // The final scratch object is used in + // the assembly of the right hand side + // of the temperature system. This + // object is significantly larger than + // the ones above because a lot more + // quantities enter the computation of + // the right hand side of the + // temperature equation. In particular, + // the temperature values and gradients + // of the previous two time steps need + // to be evaluated at the quadrature + // points, as well as the velocities + // and the strain rates (i.e. the + // symmetric gradients of the velocity) + // that enter the right hand side as + // friction heating terms. Despite the + // number of terms, the following + // should be rather self explanatory: template struct TemperatureRHS { - TemperatureRHS (const FiniteElement &temperature_fe, - const FiniteElement &stokes_fe, - const Mapping &mapping, - const Quadrature &quadrature); + TemperatureRHS (const FiniteElement &temperature_fe, + const FiniteElement &stokes_fe, + const Mapping &mapping, + const Quadrature &quadrature); - TemperatureRHS (const TemperatureRHS &data); + TemperatureRHS (const TemperatureRHS &data); - FEValues temperature_fe_values; - FEValues stokes_fe_values; + FEValues temperature_fe_values; + FEValues stokes_fe_values; - std::vector phi_T; - std::vector > grad_phi_T; + std::vector phi_T; + std::vector > grad_phi_T; - std::vector > old_velocity_values; - std::vector > old_old_velocity_values; + std::vector > old_velocity_values; + std::vector > old_old_velocity_values; - std::vector > old_strain_rates; - std::vector > old_old_strain_rates; + std::vector > old_strain_rates; + std::vector > old_old_strain_rates; - std::vector old_temperature_values; - std::vector old_old_temperature_values; - std::vector > old_temperature_grads; - std::vector > old_old_temperature_grads; - std::vector old_temperature_laplacians; - std::vector old_old_temperature_laplacians; + std::vector old_temperature_values; + std::vector old_old_temperature_values; + std::vector > old_temperature_grads; + std::vector > old_old_temperature_grads; + std::vector old_temperature_laplacians; + std::vector old_old_temperature_laplacians; }; template TemperatureRHS:: TemperatureRHS (const FiniteElement &temperature_fe, - const FiniteElement &stokes_fe, - const Mapping &mapping, - const Quadrature &quadrature) - : - temperature_fe_values (mapping, - temperature_fe, quadrature, - update_values | - update_gradients | - update_hessians | - update_quadrature_points | - update_JxW_values), - stokes_fe_values (mapping, - stokes_fe, quadrature, - update_values | update_gradients), - phi_T (temperature_fe.dofs_per_cell), - grad_phi_T (temperature_fe.dofs_per_cell), - - old_velocity_values (quadrature.size()), - old_old_velocity_values (quadrature.size()), - old_strain_rates (quadrature.size()), - old_old_strain_rates (quadrature.size()), - - old_temperature_values (quadrature.size()), - old_old_temperature_values(quadrature.size()), - old_temperature_grads(quadrature.size()), - old_old_temperature_grads(quadrature.size()), - old_temperature_laplacians(quadrature.size()), - old_old_temperature_laplacians(quadrature.size()) + const FiniteElement &stokes_fe, + const Mapping &mapping, + const Quadrature &quadrature) + : + temperature_fe_values (mapping, + temperature_fe, quadrature, + update_values | + update_gradients | + update_hessians | + update_quadrature_points | + update_JxW_values), + stokes_fe_values (mapping, + stokes_fe, quadrature, + update_values | update_gradients), + phi_T (temperature_fe.dofs_per_cell), + grad_phi_T (temperature_fe.dofs_per_cell), + + old_velocity_values (quadrature.size()), + old_old_velocity_values (quadrature.size()), + old_strain_rates (quadrature.size()), + old_old_strain_rates (quadrature.size()), + + old_temperature_values (quadrature.size()), + old_old_temperature_values(quadrature.size()), + old_temperature_grads(quadrature.size()), + old_old_temperature_grads(quadrature.size()), + old_temperature_laplacians(quadrature.size()), + old_old_temperature_laplacians(quadrature.size()) {} template TemperatureRHS:: TemperatureRHS (const TemperatureRHS &scratch) - : - temperature_fe_values (scratch.temperature_fe_values.get_mapping(), - scratch.temperature_fe_values.get_fe(), - scratch.temperature_fe_values.get_quadrature(), - scratch.temperature_fe_values.get_update_flags()), - stokes_fe_values (scratch.stokes_fe_values.get_mapping(), - scratch.stokes_fe_values.get_fe(), - scratch.stokes_fe_values.get_quadrature(), - scratch.stokes_fe_values.get_update_flags()), - phi_T (scratch.phi_T), - grad_phi_T (scratch.grad_phi_T), - - old_velocity_values (scratch.old_velocity_values), - old_old_velocity_values (scratch.old_old_velocity_values), - old_strain_rates (scratch.old_strain_rates), - old_old_strain_rates (scratch.old_old_strain_rates), - - old_temperature_values (scratch.old_temperature_values), - old_old_temperature_values (scratch.old_old_temperature_values), - old_temperature_grads (scratch.old_temperature_grads), - old_old_temperature_grads (scratch.old_old_temperature_grads), - old_temperature_laplacians (scratch.old_temperature_laplacians), - old_old_temperature_laplacians (scratch.old_old_temperature_laplacians) + : + temperature_fe_values (scratch.temperature_fe_values.get_mapping(), + scratch.temperature_fe_values.get_fe(), + scratch.temperature_fe_values.get_quadrature(), + scratch.temperature_fe_values.get_update_flags()), + stokes_fe_values (scratch.stokes_fe_values.get_mapping(), + scratch.stokes_fe_values.get_fe(), + scratch.stokes_fe_values.get_quadrature(), + scratch.stokes_fe_values.get_update_flags()), + phi_T (scratch.phi_T), + grad_phi_T (scratch.grad_phi_T), + + old_velocity_values (scratch.old_velocity_values), + old_old_velocity_values (scratch.old_old_velocity_values), + old_strain_rates (scratch.old_strain_rates), + old_old_strain_rates (scratch.old_old_strain_rates), + + old_temperature_values (scratch.old_temperature_values), + old_old_temperature_values (scratch.old_old_temperature_values), + old_temperature_grads (scratch.old_temperature_grads), + old_old_temperature_grads (scratch.old_old_temperature_grads), + old_temperature_laplacians (scratch.old_temperature_laplacians), + old_old_temperature_laplacians (scratch.old_old_temperature_laplacians) {} } - // The CopyData objects are even - // simpler than the Scratch - // objects as all they have to do - // is to store the results of - // local computations until they - // can be copied into the global - // matrix or vector - // objects. These structures - // therefore only need to provide - // a constructor, a copy - // operation, and some arrays for - // local matrix, local vectors - // and the relation between local - // and global degrees of freedom - // (a.k.a. - // local_dof_indices). Again, - // we have one such structure for - // each of the four operations we - // will parallelize using the - // WorkStream class: + // The CopyData objects are even + // simpler than the Scratch + // objects as all they have to do + // is to store the results of + // local computations until they + // can be copied into the global + // matrix or vector + // objects. These structures + // therefore only need to provide + // a constructor, a copy + // operation, and some arrays for + // local matrix, local vectors + // and the relation between local + // and global degrees of freedom + // (a.k.a. + // local_dof_indices). Again, + // we have one such structure for + // each of the four operations we + // will parallelize using the + // WorkStream class: namespace CopyData { template struct StokesPreconditioner { - StokesPreconditioner (const FiniteElement &stokes_fe); - StokesPreconditioner (const StokesPreconditioner &data); + StokesPreconditioner (const FiniteElement &stokes_fe); + StokesPreconditioner (const StokesPreconditioner &data); - FullMatrix local_matrix; - std::vector local_dof_indices; + FullMatrix local_matrix; + std::vector local_dof_indices; }; template StokesPreconditioner:: StokesPreconditioner (const FiniteElement &stokes_fe) - : - local_matrix (stokes_fe.dofs_per_cell, - stokes_fe.dofs_per_cell), - local_dof_indices (stokes_fe.dofs_per_cell) + : + local_matrix (stokes_fe.dofs_per_cell, + stokes_fe.dofs_per_cell), + local_dof_indices (stokes_fe.dofs_per_cell) {} template StokesPreconditioner:: StokesPreconditioner (const StokesPreconditioner &data) - : - local_matrix (data.local_matrix), - local_dof_indices (data.local_dof_indices) + : + local_matrix (data.local_matrix), + local_dof_indices (data.local_dof_indices) {} @@ -769,26 +769,26 @@ namespace Step32 template struct StokesSystem : public StokesPreconditioner { - StokesSystem (const FiniteElement &stokes_fe); - StokesSystem (const StokesSystem &data); + StokesSystem (const FiniteElement &stokes_fe); + StokesSystem (const StokesSystem &data); - Vector local_rhs; + Vector local_rhs; }; template StokesSystem:: StokesSystem (const FiniteElement &stokes_fe) - : - StokesPreconditioner (stokes_fe), - local_rhs (stokes_fe.dofs_per_cell) + : + StokesPreconditioner (stokes_fe), + local_rhs (stokes_fe.dofs_per_cell) {} template StokesSystem:: StokesSystem (const StokesSystem &data) - : - StokesPreconditioner (data), - local_rhs (data.local_rhs) + : + StokesPreconditioner (data), + local_rhs (data.local_rhs) {} @@ -796,32 +796,32 @@ namespace Step32 template struct TemperatureMatrix { - TemperatureMatrix (const FiniteElement &temperature_fe); - TemperatureMatrix (const TemperatureMatrix &data); + TemperatureMatrix (const FiniteElement &temperature_fe); + TemperatureMatrix (const TemperatureMatrix &data); - FullMatrix local_mass_matrix; - FullMatrix local_stiffness_matrix; - std::vector local_dof_indices; + FullMatrix local_mass_matrix; + FullMatrix local_stiffness_matrix; + std::vector local_dof_indices; }; template TemperatureMatrix:: TemperatureMatrix (const FiniteElement &temperature_fe) - : - local_mass_matrix (temperature_fe.dofs_per_cell, - temperature_fe.dofs_per_cell), - local_stiffness_matrix (temperature_fe.dofs_per_cell, - temperature_fe.dofs_per_cell), - local_dof_indices (temperature_fe.dofs_per_cell) + : + local_mass_matrix (temperature_fe.dofs_per_cell, + temperature_fe.dofs_per_cell), + local_stiffness_matrix (temperature_fe.dofs_per_cell, + temperature_fe.dofs_per_cell), + local_dof_indices (temperature_fe.dofs_per_cell) {} template TemperatureMatrix:: TemperatureMatrix (const TemperatureMatrix &data) - : - local_mass_matrix (data.local_mass_matrix), - local_stiffness_matrix (data.local_stiffness_matrix), - local_dof_indices (data.local_dof_indices) + : + local_mass_matrix (data.local_mass_matrix), + local_stiffness_matrix (data.local_stiffness_matrix), + local_dof_indices (data.local_dof_indices) {} @@ -829,467 +829,467 @@ namespace Step32 template struct TemperatureRHS { - TemperatureRHS (const FiniteElement &temperature_fe); - TemperatureRHS (const TemperatureRHS &data); + TemperatureRHS (const FiniteElement &temperature_fe); + TemperatureRHS (const TemperatureRHS &data); - Vector local_rhs; - std::vector local_dof_indices; - FullMatrix matrix_for_bc; + Vector local_rhs; + std::vector local_dof_indices; + FullMatrix matrix_for_bc; }; template TemperatureRHS:: TemperatureRHS (const FiniteElement &temperature_fe) - : - local_rhs (temperature_fe.dofs_per_cell), - local_dof_indices (temperature_fe.dofs_per_cell), - matrix_for_bc (temperature_fe.dofs_per_cell, - temperature_fe.dofs_per_cell) + : + local_rhs (temperature_fe.dofs_per_cell), + local_dof_indices (temperature_fe.dofs_per_cell), + matrix_for_bc (temperature_fe.dofs_per_cell, + temperature_fe.dofs_per_cell) {} template TemperatureRHS:: TemperatureRHS (const TemperatureRHS &data) - : - local_rhs (data.local_rhs), - local_dof_indices (data.local_dof_indices), - matrix_for_bc (data.matrix_for_bc) + : + local_rhs (data.local_rhs), + local_dof_indices (data.local_dof_indices), + matrix_for_bc (data.matrix_for_bc) {} } } - // @sect3{The BoussinesqFlowProblem class template} - // - // This is the declaration of the - // main class. It is very similar - // to step-31 but there are a - // number differences we will - // comment on below. - // - // The top of the class is - // essentially the same as in - // step-31, listing the public - // methods and a set of private - // functions that do the heavy - // lifting. Compared to step-31 - // there are only two additions to - // this section: the function - // get_cfl_number() - // that computes the maximum CFL - // number over all cells which - // we then compute the global time - // step from, and the function - // get_entropy_variation() - // that is used in the computation - // of the entropy stabilization. It - // is akin to the - // get_extrapolated_temperature_range() - // we have used in step-31 for this - // purpose, but works on the - // entropy instead of the - // temperature instead. + // @sect3{The BoussinesqFlowProblem class template} + // + // This is the declaration of the + // main class. It is very similar + // to step-31 but there are a + // number differences we will + // comment on below. + // + // The top of the class is + // essentially the same as in + // step-31, listing the public + // methods and a set of private + // functions that do the heavy + // lifting. Compared to step-31 + // there are only two additions to + // this section: the function + // get_cfl_number() + // that computes the maximum CFL + // number over all cells which + // we then compute the global time + // step from, and the function + // get_entropy_variation() + // that is used in the computation + // of the entropy stabilization. It + // is akin to the + // get_extrapolated_temperature_range() + // we have used in step-31 for this + // purpose, but works on the + // entropy instead of the + // temperature instead. template class BoussinesqFlowProblem { - public: - struct Parameters; - BoussinesqFlowProblem (Parameters ¶meters); - void run (); + public: + struct Parameters; + BoussinesqFlowProblem (Parameters ¶meters); + void run (); + + private: + void setup_dofs (); + void assemble_stokes_preconditioner (); + void build_stokes_preconditioner (); + void assemble_stokes_system (); + void assemble_temperature_matrix (); + void assemble_temperature_system (const double maximal_velocity); + void project_temperature_field (); + double get_maximal_velocity () const; + double get_cfl_number () const; + double get_entropy_variation (const double average_temperature) const; + std::pair get_extrapolated_temperature_range () const; + void solve (); + void output_results (); + void refine_mesh (const unsigned int max_grid_level); - private: - void setup_dofs (); - void assemble_stokes_preconditioner (); - void build_stokes_preconditioner (); - void assemble_stokes_system (); - void assemble_temperature_matrix (); - void assemble_temperature_system (const double maximal_velocity); - void project_temperature_field (); - double get_maximal_velocity () const; - double get_cfl_number () const; - double get_entropy_variation (const double average_temperature) const; - std::pair get_extrapolated_temperature_range () const; - void solve (); - void output_results (); - void refine_mesh (const unsigned int max_grid_level); - - double - compute_viscosity(const std::vector &old_temperature, - const std::vector &old_old_temperature, - const std::vector > &old_temperature_grads, - const std::vector > &old_old_temperature_grads, - const std::vector &old_temperature_laplacians, - const std::vector &old_old_temperature_laplacians, - const std::vector > &old_velocity_values, - const std::vector > &old_old_velocity_values, - const std::vector > &old_strain_rates, - const std::vector > &old_old_strain_rates, - const double global_u_infty, - const double global_T_variation, - const double average_temperature, - const double global_entropy_variation, - const double cell_diameter) const; + double + compute_viscosity(const std::vector &old_temperature, + const std::vector &old_old_temperature, + const std::vector > &old_temperature_grads, + const std::vector > &old_old_temperature_grads, + const std::vector &old_temperature_laplacians, + const std::vector &old_old_temperature_laplacians, + const std::vector > &old_velocity_values, + const std::vector > &old_old_velocity_values, + const std::vector > &old_strain_rates, + const std::vector > &old_old_strain_rates, + const double global_u_infty, + const double global_T_variation, + const double average_temperature, + const double global_entropy_variation, + const double cell_diameter) const; + + public: + + // The first significant new + // component is the definition + // of a struct for the + // parameters according to the + // discussion in the + // introduction. This structure + // is initialized by reading + // from a parameter file during + // construction of this object. + struct Parameters + { + Parameters (const std::string ¶meter_filename); - public: + static void declare_parameters (ParameterHandler &prm); + void parse_parameters (ParameterHandler &prm); - // The first significant new - // component is the definition - // of a struct for the - // parameters according to the - // discussion in the - // introduction. This structure - // is initialized by reading - // from a parameter file during - // construction of this object. - struct Parameters - { - Parameters (const std::string ¶meter_filename); + double end_time; - static void declare_parameters (ParameterHandler &prm); - void parse_parameters (ParameterHandler &prm); + unsigned int initial_global_refinement; + unsigned int initial_adaptive_refinement; - double end_time; + bool generate_graphical_output; + unsigned int graphical_output_interval; - unsigned int initial_global_refinement; - unsigned int initial_adaptive_refinement; + unsigned int adaptive_refinement_interval; - bool generate_graphical_output; - unsigned int graphical_output_interval; + double stabilization_alpha; + double stabilization_c_R; + double stabilization_beta; - unsigned int adaptive_refinement_interval; + unsigned int stokes_velocity_degree; + bool use_locally_conservative_discretization; - double stabilization_alpha; - double stabilization_c_R; - double stabilization_beta; + unsigned int temperature_degree; + }; - unsigned int stokes_velocity_degree; - bool use_locally_conservative_discretization; + private: + Parameters ¶meters; + + // The pcout (for + // %parallel + // std::cout) + // object is used to simplify + // writing output: each MPI + // process can use this to + // generate output as usual, + // but since each of these + // processes will (hopefully) + // produce the same output it + // will just be replicated many + // times over; with the + // ConditionalOStream class, + // only the output generated by + // one MPI process will + // actually be printed to + // screen, whereas the output + // by all the other threads + // will simply be forgotten. + ConditionalOStream pcout; + + // The following member + // variables will then again be + // similar to those in step-31 + // (and to other tutorial + // programs). As mentioned in + // the introduction, we fully + // distribute computations, so + // we will have to use the + // parallel::distributed::Triangulation + // class (see step-40) but the + // remainder of these variables + // is rather standard with two + // exceptions: + // + // - The mapping + // variable is used to denote a + // higher-order polynomial + // mapping. As mentioned in the + // introduction, we use this + // mapping when forming + // integrals through quadrature + // for all cells that are + // adjacent to either the inner + // or outer boundaries of our + // domain where the boundary is + // curved. + // + // - In a bit of naming + // confusion, you will notice + // below that some of the + // variables from namespace + // TrilinosWrappers are taken + // from namespace + // TrilinosWrappers::MPI (such + // as the right hand side + // vectors) whereas others are + // not (such as the various + // matrices). For the matrices, + // we happen to use the same + // class names for %parallel + // and sequential data + // structures, i.e., all + // matrices will actually be + // considered %parallel + // below. On the other hand, + // for vectors, only those from + // namespace + // TrilinosWrappers::MPI are + // actually distributed. In + // particular, we will + // frequently have to query + // velocities and temperatures + // at arbitrary quadrature + // points; consequently, rather + // than importing ghost + // information of a vector + // whenever we need access to + // degrees of freedom that are + // relevant locally but owned + // by another processor, we + // solve linear systems in + // %parallel but then + // immediately initialize a + // vector including ghost + // entries of the solution for + // further processing. The + // various + // *_solution + // vectors are therefore filled + // immediately after solving + // their respective linear + // system in %parallel and will + // always contain values for + // all @ref + // GlossLocallyRelevantDof + // "locally relevant degrees of freedom"; + // the fully + // distributed vectors that we + // obtain from the solution + // process and that only ever + // contain the @ref + // GlossLocallyOwnedDof + // "locally owned degrees of freedom" + // are destroyed + // immediately after the + // solution process and after + // we have copied the relevant + // values into the member + // variable vectors. + parallel::distributed::Triangulation triangulation; + double global_Omega_diameter; + + const MappingQ mapping; + + const FESystem stokes_fe; + DoFHandler stokes_dof_handler; + ConstraintMatrix stokes_constraints; + + TrilinosWrappers::BlockSparseMatrix stokes_matrix; + TrilinosWrappers::BlockSparseMatrix stokes_preconditioner_matrix; + + TrilinosWrappers::MPI::BlockVector stokes_solution; + TrilinosWrappers::MPI::BlockVector old_stokes_solution; + TrilinosWrappers::MPI::BlockVector stokes_rhs; + + + FE_Q temperature_fe; + DoFHandler temperature_dof_handler; + ConstraintMatrix temperature_constraints; + + TrilinosWrappers::SparseMatrix temperature_mass_matrix; + TrilinosWrappers::SparseMatrix temperature_stiffness_matrix; + TrilinosWrappers::SparseMatrix temperature_matrix; + + TrilinosWrappers::MPI::Vector temperature_solution; + TrilinosWrappers::MPI::Vector old_temperature_solution; + TrilinosWrappers::MPI::Vector old_old_temperature_solution; + TrilinosWrappers::MPI::Vector temperature_rhs; + + + double time_step; + double old_time_step; + unsigned int timestep_number; + + std_cxx1x::shared_ptr Amg_preconditioner; + std_cxx1x::shared_ptr Mp_preconditioner; + std_cxx1x::shared_ptr T_preconditioner; + + bool rebuild_stokes_matrix; + bool rebuild_stokes_preconditioner; + bool rebuild_temperature_matrices; + bool rebuild_temperature_preconditioner; + + // The next member variable, + // computing_timer + // is used to conveniently + // account for compute time + // spent in certain "sections" + // of the code that are + // repeatedly entered. For + // example, we will enter (and + // leave) sections for Stokes + // matrix assembly and would + // like to accumulate the run + // time spent in this section + // over all time steps. Every + // so many time steps as well + // as at the end of the program + // (through the destructor of + // the TimerOutput class) we + // will then produce a nice + // summary of the times spent + // in the different sections + // into which we categorize the + // run-time of this program. + TimerOutput computing_timer; + + // After these member variables + // we have a number of + // auxiliary functions that + // have been broken out of the + // ones listed + // above. Specifically, there + // are first three functions + // that we call from + // setup_dofs and + // then the ones that do the + // assembling of linear + // systems: + void setup_stokes_matrix (const std::vector &stokes_partitioning); + void setup_stokes_preconditioner (const std::vector &stokes_partitioning); + void setup_temperature_matrices (const IndexSet &temperature_partitioning); + + + // Following the @ref + // MTWorkStream + // "task-based parallelization" + // paradigm, + // we split all the assembly + // routines into two parts: a + // first part that can do all + // the calculations on a + // certain cell without taking + // care of other threads, and a + // second part (which is + // writing the local data into + // the global matrices and + // vectors) which can be + // entered by only one thread + // at a time. In order to + // implement that, we provide + // functions for each of those + // two steps for all the four + // assembly routines that we + // use in this program. The + // following eight functions do + // exactly this: + void + local_assemble_stokes_preconditioner (const typename DoFHandler::active_cell_iterator &cell, + Assembly::Scratch::StokesPreconditioner &scratch, + Assembly::CopyData::StokesPreconditioner &data); - unsigned int temperature_degree; - }; + void + copy_local_to_global_stokes_preconditioner (const Assembly::CopyData::StokesPreconditioner &data); - private: - Parameters ¶meters; - - // The pcout (for - // %parallel - // std::cout) - // object is used to simplify - // writing output: each MPI - // process can use this to - // generate output as usual, - // but since each of these - // processes will (hopefully) - // produce the same output it - // will just be replicated many - // times over; with the - // ConditionalOStream class, - // only the output generated by - // one MPI process will - // actually be printed to - // screen, whereas the output - // by all the other threads - // will simply be forgotten. - ConditionalOStream pcout; - - // The following member - // variables will then again be - // similar to those in step-31 - // (and to other tutorial - // programs). As mentioned in - // the introduction, we fully - // distribute computations, so - // we will have to use the - // parallel::distributed::Triangulation - // class (see step-40) but the - // remainder of these variables - // is rather standard with two - // exceptions: - // - // - The mapping - // variable is used to denote a - // higher-order polynomial - // mapping. As mentioned in the - // introduction, we use this - // mapping when forming - // integrals through quadrature - // for all cells that are - // adjacent to either the inner - // or outer boundaries of our - // domain where the boundary is - // curved. - // - // - In a bit of naming - // confusion, you will notice - // below that some of the - // variables from namespace - // TrilinosWrappers are taken - // from namespace - // TrilinosWrappers::MPI (such - // as the right hand side - // vectors) whereas others are - // not (such as the various - // matrices). For the matrices, - // we happen to use the same - // class names for %parallel - // and sequential data - // structures, i.e., all - // matrices will actually be - // considered %parallel - // below. On the other hand, - // for vectors, only those from - // namespace - // TrilinosWrappers::MPI are - // actually distributed. In - // particular, we will - // frequently have to query - // velocities and temperatures - // at arbitrary quadrature - // points; consequently, rather - // than importing ghost - // information of a vector - // whenever we need access to - // degrees of freedom that are - // relevant locally but owned - // by another processor, we - // solve linear systems in - // %parallel but then - // immediately initialize a - // vector including ghost - // entries of the solution for - // further processing. The - // various - // *_solution - // vectors are therefore filled - // immediately after solving - // their respective linear - // system in %parallel and will - // always contain values for - // all @ref - // GlossLocallyRelevantDof - // "locally relevant degrees of freedom"; - // the fully - // distributed vectors that we - // obtain from the solution - // process and that only ever - // contain the @ref - // GlossLocallyOwnedDof - // "locally owned degrees of freedom" - // are destroyed - // immediately after the - // solution process and after - // we have copied the relevant - // values into the member - // variable vectors. - parallel::distributed::Triangulation triangulation; - double global_Omega_diameter; - - const MappingQ mapping; - - const FESystem stokes_fe; - DoFHandler stokes_dof_handler; - ConstraintMatrix stokes_constraints; - - TrilinosWrappers::BlockSparseMatrix stokes_matrix; - TrilinosWrappers::BlockSparseMatrix stokes_preconditioner_matrix; - - TrilinosWrappers::MPI::BlockVector stokes_solution; - TrilinosWrappers::MPI::BlockVector old_stokes_solution; - TrilinosWrappers::MPI::BlockVector stokes_rhs; - - - FE_Q temperature_fe; - DoFHandler temperature_dof_handler; - ConstraintMatrix temperature_constraints; - - TrilinosWrappers::SparseMatrix temperature_mass_matrix; - TrilinosWrappers::SparseMatrix temperature_stiffness_matrix; - TrilinosWrappers::SparseMatrix temperature_matrix; - - TrilinosWrappers::MPI::Vector temperature_solution; - TrilinosWrappers::MPI::Vector old_temperature_solution; - TrilinosWrappers::MPI::Vector old_old_temperature_solution; - TrilinosWrappers::MPI::Vector temperature_rhs; - - - double time_step; - double old_time_step; - unsigned int timestep_number; - - std_cxx1x::shared_ptr Amg_preconditioner; - std_cxx1x::shared_ptr Mp_preconditioner; - std_cxx1x::shared_ptr T_preconditioner; - - bool rebuild_stokes_matrix; - bool rebuild_stokes_preconditioner; - bool rebuild_temperature_matrices; - bool rebuild_temperature_preconditioner; - - // The next member variable, - // computing_timer - // is used to conveniently - // account for compute time - // spent in certain "sections" - // of the code that are - // repeatedly entered. For - // example, we will enter (and - // leave) sections for Stokes - // matrix assembly and would - // like to accumulate the run - // time spent in this section - // over all time steps. Every - // so many time steps as well - // as at the end of the program - // (through the destructor of - // the TimerOutput class) we - // will then produce a nice - // summary of the times spent - // in the different sections - // into which we categorize the - // run-time of this program. - TimerOutput computing_timer; - - // After these member variables - // we have a number of - // auxiliary functions that - // have been broken out of the - // ones listed - // above. Specifically, there - // are first three functions - // that we call from - // setup_dofs and - // then the ones that do the - // assembling of linear - // systems: - void setup_stokes_matrix (const std::vector &stokes_partitioning); - void setup_stokes_preconditioner (const std::vector &stokes_partitioning); - void setup_temperature_matrices (const IndexSet &temperature_partitioning); - - - // Following the @ref - // MTWorkStream - // "task-based parallelization" - // paradigm, - // we split all the assembly - // routines into two parts: a - // first part that can do all - // the calculations on a - // certain cell without taking - // care of other threads, and a - // second part (which is - // writing the local data into - // the global matrices and - // vectors) which can be - // entered by only one thread - // at a time. In order to - // implement that, we provide - // functions for each of those - // two steps for all the four - // assembly routines that we - // use in this program. The - // following eight functions do - // exactly this: - void - local_assemble_stokes_preconditioner (const typename DoFHandler::active_cell_iterator &cell, - Assembly::Scratch::StokesPreconditioner &scratch, - Assembly::CopyData::StokesPreconditioner &data); - - void - copy_local_to_global_stokes_preconditioner (const Assembly::CopyData::StokesPreconditioner &data); - - - void - local_assemble_stokes_system (const typename DoFHandler::active_cell_iterator &cell, - Assembly::Scratch::StokesSystem &scratch, - Assembly::CopyData::StokesSystem &data); - - void - copy_local_to_global_stokes_system (const Assembly::CopyData::StokesSystem &data); - - - void - local_assemble_temperature_matrix (const typename DoFHandler::active_cell_iterator &cell, - Assembly::Scratch::TemperatureMatrix &scratch, - Assembly::CopyData::TemperatureMatrix &data); - - void - copy_local_to_global_temperature_matrix (const Assembly::CopyData::TemperatureMatrix &data); - - - - void - local_assemble_temperature_rhs (const std::pair global_T_range, - const double global_max_velocity, - const double global_entropy_variation, - const typename DoFHandler::active_cell_iterator &cell, - Assembly::Scratch::TemperatureRHS &scratch, - Assembly::CopyData::TemperatureRHS &data); - - void - copy_local_to_global_temperature_rhs (const Assembly::CopyData::TemperatureRHS &data); - - // Finally, we forward declare - // a member class that we will - // define later on and that - // will be used to compute a - // number of quantities from - // our solution vectors that - // we'd like to put into the - // output files for - // visualization. - class Postprocessor; + + void + local_assemble_stokes_system (const typename DoFHandler::active_cell_iterator &cell, + Assembly::Scratch::StokesSystem &scratch, + Assembly::CopyData::StokesSystem &data); + + void + copy_local_to_global_stokes_system (const Assembly::CopyData::StokesSystem &data); + + + void + local_assemble_temperature_matrix (const typename DoFHandler::active_cell_iterator &cell, + Assembly::Scratch::TemperatureMatrix &scratch, + Assembly::CopyData::TemperatureMatrix &data); + + void + copy_local_to_global_temperature_matrix (const Assembly::CopyData::TemperatureMatrix &data); + + + + void + local_assemble_temperature_rhs (const std::pair global_T_range, + const double global_max_velocity, + const double global_entropy_variation, + const typename DoFHandler::active_cell_iterator &cell, + Assembly::Scratch::TemperatureRHS &scratch, + Assembly::CopyData::TemperatureRHS &data); + + void + copy_local_to_global_temperature_rhs (const Assembly::CopyData::TemperatureRHS &data); + + // Finally, we forward declare + // a member class that we will + // define later on and that + // will be used to compute a + // number of quantities from + // our solution vectors that + // we'd like to put into the + // output files for + // visualization. + class Postprocessor; }; - // @sect3{BoussinesqFlowProblem class implementation} - - // @sect4{BoussinesqFlowProblem::Parameters} - // - // Here comes the definition of the - // parameters for the Stokes - // problem. We allow to set the end - // time for the simulation, the - // level of refinements (both - // global and adaptive, which in - // the sum specify what maximum - // level the cells are allowed to - // have), and the interval between - // refinements in the time - // stepping. - // - // Then, we let the user specify - // constants for the stabilization - // parameters (as discussed in the - // introduction), the polynomial - // degree for the Stokes velocity - // space, whether to use the - // locally conservative - // discretization based on FE_DGP - // elements for the pressure or not - // (FE_Q elements for pressure), - // and the polynomial degree for - // the temperature interpolation. - // - // The constructor checks for a - // valid input file (if not, a file - // with default parameters for the - // quantities is written), and - // eventually parses the - // parameters. + // @sect3{BoussinesqFlowProblem class implementation} + + // @sect4{BoussinesqFlowProblem::Parameters} + // + // Here comes the definition of the + // parameters for the Stokes + // problem. We allow to set the end + // time for the simulation, the + // level of refinements (both + // global and adaptive, which in + // the sum specify what maximum + // level the cells are allowed to + // have), and the interval between + // refinements in the time + // stepping. + // + // Then, we let the user specify + // constants for the stabilization + // parameters (as discussed in the + // introduction), the polynomial + // degree for the Stokes velocity + // space, whether to use the + // locally conservative + // discretization based on FE_DGP + // elements for the pressure or not + // (FE_Q elements for pressure), + // and the polynomial degree for + // the temperature interpolation. + // + // The constructor checks for a + // valid input file (if not, a file + // with default parameters for the + // quantities is written), and + // eventually parses the + // parameters. template BoussinesqFlowProblem::Parameters::Parameters (const std::string ¶meter_filename) - : - end_time (1e8), - initial_global_refinement (2), - initial_adaptive_refinement (2), - adaptive_refinement_interval (10), - stabilization_alpha (2), - stabilization_c_R (0.11), - stabilization_beta (0.078), - stokes_velocity_degree (2), - use_locally_conservative_discretization (true), - temperature_degree (2) + : + end_time (1e8), + initial_global_refinement (2), + initial_adaptive_refinement (2), + adaptive_refinement_interval (10), + stabilization_alpha (2), + stabilization_c_R (0.11), + stabilization_beta (0.078), + stokes_velocity_degree (2), + use_locally_conservative_discretization (true), + temperature_degree (2) { ParameterHandler prm; BoussinesqFlowProblem::Parameters::declare_parameters (prm); @@ -1298,20 +1298,20 @@ namespace Step32 if (!parameter_file) { - parameter_file.close (); + parameter_file.close (); - std::ostringstream message; - message << "Input parameter file <" - << parameter_filename << "> not found. Creating a" - << std::endl - << "template file of the same name." - << std::endl; + std::ostringstream message; + message << "Input parameter file <" + << parameter_filename << "> not found. Creating a" + << std::endl + << "template file of the same name." + << std::endl; - std::ofstream parameter_out (parameter_filename.c_str()); - prm.print_parameters (parameter_out, - ParameterHandler::Text); + std::ofstream parameter_out (parameter_filename.c_str()); + prm.print_parameters (parameter_out, + ParameterHandler::Text); - AssertThrow (false, ExcMessage (message.str().c_str())); + AssertThrow (false, ExcMessage (message.str().c_str())); } const bool success = prm.read_input (parameter_file); @@ -1322,90 +1322,90 @@ namespace Step32 - // Next we have a function that - // declares the parameters that we - // expect in the input file, - // together with their data types, - // default values and a - // description: + // Next we have a function that + // declares the parameters that we + // expect in the input file, + // together with their data types, + // default values and a + // description: template void BoussinesqFlowProblem::Parameters:: declare_parameters (ParameterHandler &prm) { prm.declare_entry ("End time", "1e8", - Patterns::Double (0), - "The end time of the simulation in years."); + Patterns::Double (0), + "The end time of the simulation in years."); prm.declare_entry ("Initial global refinement", "2", - Patterns::Integer (0), - "The number of global refinement steps performed on " - "the initial coarse mesh, before the problem is first " - "solved there."); + Patterns::Integer (0), + "The number of global refinement steps performed on " + "the initial coarse mesh, before the problem is first " + "solved there."); prm.declare_entry ("Initial adaptive refinement", "2", - Patterns::Integer (0), - "The number of adaptive refinement steps performed after " - "initial global refinement."); + Patterns::Integer (0), + "The number of adaptive refinement steps performed after " + "initial global refinement."); prm.declare_entry ("Time steps between mesh refinement", "10", - Patterns::Integer (1), - "The number of time steps after which the mesh is to be " - "adapted based on computed error indicators."); + Patterns::Integer (1), + "The number of time steps after which the mesh is to be " + "adapted based on computed error indicators."); prm.declare_entry ("Generate graphical output", "false", - Patterns::Bool (), - "Whether graphical output is to be generated or not. " - "You may not want to get graphical output if the number " - "of processors is large."); + Patterns::Bool (), + "Whether graphical output is to be generated or not. " + "You may not want to get graphical output if the number " + "of processors is large."); prm.declare_entry ("Time steps between graphical output", "50", - Patterns::Integer (1), - "The number of time steps between each generation of " - "graphical output files."); + Patterns::Integer (1), + "The number of time steps between each generation of " + "graphical output files."); prm.enter_subsection ("Stabilization parameters"); { prm.declare_entry ("alpha", "2", - Patterns::Double (1, 2), - "The exponent in the entropy viscosity stabilization."); + Patterns::Double (1, 2), + "The exponent in the entropy viscosity stabilization."); prm.declare_entry ("c_R", "0.11", - Patterns::Double (0), - "The c_R factor in the entropy viscosity " - "stabilization."); + Patterns::Double (0), + "The c_R factor in the entropy viscosity " + "stabilization."); prm.declare_entry ("beta", "0.078", - Patterns::Double (0), - "The beta factor in the artificial viscosity " - "stabilization. An appropriate value for 2d is 0.052 " - "and 0.078 for 3d."); + Patterns::Double (0), + "The beta factor in the artificial viscosity " + "stabilization. An appropriate value for 2d is 0.052 " + "and 0.078 for 3d."); } prm.leave_subsection (); prm.enter_subsection ("Discretization"); { prm.declare_entry ("Stokes velocity polynomial degree", "2", - Patterns::Integer (1), - "The polynomial degree to use for the velocity variables " - "in the Stokes system."); + Patterns::Integer (1), + "The polynomial degree to use for the velocity variables " + "in the Stokes system."); prm.declare_entry ("Temperature polynomial degree", "2", - Patterns::Integer (1), - "The polynomial degree to use for the temperature variable."); + Patterns::Integer (1), + "The polynomial degree to use for the temperature variable."); prm.declare_entry ("Use locally conservative discretization", "true", - Patterns::Bool (), - "Whether to use a Stokes discretization that is locally " - "conservative at the expense of a larger number of degrees " - "of freedom, or to go with a cheaper discretization " - "that does not locally conserve mass (although it is " - "globally conservative."); + Patterns::Bool (), + "Whether to use a Stokes discretization that is locally " + "conservative at the expense of a larger number of degrees " + "of freedom, or to go with a cheaper discretization " + "that does not locally conserve mass (although it is " + "globally conservative."); } prm.leave_subsection (); } - // And then we need a function that - // reads the contents of the - // ParameterHandler object we get - // by reading the input file and - // puts the results into variables - // that store the values of the - // parameters we have previously - // declared: + // And then we need a function that + // reads the contents of the + // ParameterHandler object we get + // by reading the input file and + // puts the results into variables + // that store the values of the + // parameters we have previously + // declared: template void BoussinesqFlowProblem::Parameters:: @@ -1433,7 +1433,7 @@ namespace Step32 stokes_velocity_degree = prm.get_integer ("Stokes velocity polynomial degree"); temperature_degree = prm.get_integer ("Temperature polynomial degree"); use_locally_conservative_discretization - = prm.get_bool ("Use locally conservative discretization"); + = prm.get_bool ("Use locally conservative discretization"); } prm.leave_subsection (); } @@ -1441,180 +1441,180 @@ namespace Step32 - // @sect4{BoussinesqFlowProblem::BoussinesqFlowProblem} - // - // The constructor of the problem - // is very similar to the - // constructor in step-31. What is - // different is the %parallel - // communication: Trilinos uses a - // message passing interface (MPI) - // for data distribution. When - // entering the - // BoussinesqFlowProblem class, we - // have to decide how the - // parallization is to be done. We - // choose a rather simple strategy - // and let all processors that are - // running the program work - // together, specified by the - // communicator - // MPI_COMM_WORLD. Next, - // we create the output stream (as - // we already did in step-18) that - // only generates output on the - // first MPI process and is - // completely forgetful on all - // others. The implementation of - // this idea is to check the - // process number when - // pcout gets a true - // argument, and it uses the - // std::cout stream - // for output. If we are one - // processor five, for instance, - // then we will give a - // false argument to - // pcout, which means - // that the output of that - // processor will not be - // printed. With the exception of - // the mapping object (for which we - // use polynomials of degree 4) all - // but the final member variable - // are exactly the same as in - // step-31. - // - // This final object, the - // TimerOutput object, is then told - // to restrict output to the - // pcout stream - // (processor 0), and then we - // specify that we want to get a - // summary table at the end of the - // program which shows us wallclock - // times (as opposed to CPU - // times). We will manually also - // request intermediate summaries - // every so many time steps in the - // run() function - // below. + // @sect4{BoussinesqFlowProblem::BoussinesqFlowProblem} + // + // The constructor of the problem + // is very similar to the + // constructor in step-31. What is + // different is the %parallel + // communication: Trilinos uses a + // message passing interface (MPI) + // for data distribution. When + // entering the + // BoussinesqFlowProblem class, we + // have to decide how the + // parallization is to be done. We + // choose a rather simple strategy + // and let all processors that are + // running the program work + // together, specified by the + // communicator + // MPI_COMM_WORLD. Next, + // we create the output stream (as + // we already did in step-18) that + // only generates output on the + // first MPI process and is + // completely forgetful on all + // others. The implementation of + // this idea is to check the + // process number when + // pcout gets a true + // argument, and it uses the + // std::cout stream + // for output. If we are one + // processor five, for instance, + // then we will give a + // false argument to + // pcout, which means + // that the output of that + // processor will not be + // printed. With the exception of + // the mapping object (for which we + // use polynomials of degree 4) all + // but the final member variable + // are exactly the same as in + // step-31. + // + // This final object, the + // TimerOutput object, is then told + // to restrict output to the + // pcout stream + // (processor 0), and then we + // specify that we want to get a + // summary table at the end of the + // program which shows us wallclock + // times (as opposed to CPU + // times). We will manually also + // request intermediate summaries + // every so many time steps in the + // run() function + // below. template BoussinesqFlowProblem::BoussinesqFlowProblem (Parameters ¶meters_) - : - parameters (parameters_), - pcout (std::cout, - (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) - == 0)), - - triangulation (MPI_COMM_WORLD, - typename Triangulation::MeshSmoothing - (Triangulation::smoothing_on_refinement | - Triangulation::smoothing_on_coarsening)), - - mapping (4), - - stokes_fe (FE_Q(parameters.stokes_velocity_degree), - dim, - (parameters.use_locally_conservative_discretization - ? - static_cast &> - (FE_DGP(parameters.stokes_velocity_degree-1)) - : - static_cast &> - (FE_Q(parameters.stokes_velocity_degree-1))), - 1), - - stokes_dof_handler (triangulation), - - temperature_fe (parameters.temperature_degree), - temperature_dof_handler (triangulation), - - time_step (0), - old_time_step (0), - timestep_number (0), - rebuild_stokes_matrix (true), - rebuild_stokes_preconditioner (true), - rebuild_temperature_matrices (true), - rebuild_temperature_preconditioner (true), - - computing_timer (pcout, - TimerOutput::summary, - TimerOutput::wall_times) + : + parameters (parameters_), + pcout (std::cout, + (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) + == 0)), + + triangulation (MPI_COMM_WORLD, + typename Triangulation::MeshSmoothing + (Triangulation::smoothing_on_refinement | + Triangulation::smoothing_on_coarsening)), + + mapping (4), + + stokes_fe (FE_Q(parameters.stokes_velocity_degree), + dim, + (parameters.use_locally_conservative_discretization + ? + static_cast &> + (FE_DGP(parameters.stokes_velocity_degree-1)) + : + static_cast &> + (FE_Q(parameters.stokes_velocity_degree-1))), + 1), + + stokes_dof_handler (triangulation), + + temperature_fe (parameters.temperature_degree), + temperature_dof_handler (triangulation), + + time_step (0), + old_time_step (0), + timestep_number (0), + rebuild_stokes_matrix (true), + rebuild_stokes_preconditioner (true), + rebuild_temperature_matrices (true), + rebuild_temperature_preconditioner (true), + + computing_timer (pcout, + TimerOutput::summary, + TimerOutput::wall_times) {} - // @sect4{The BoussinesqFlowProblem helper functions} - // @sect5{BoussinesqFlowProblem::get_maximal_velocity} - // Except for two small details, - // the function to compute the - // global maximum of the velocity - // is the same as in step-31. The - // first detail is actually common - // to all functions that implement - // loops over all cells in the - // triangulation: When operating in - // %parallel, each processor can - // only work on a chunk of cells - // since each processor only has a - // certain part of the entire - // triangulation. This chunk of - // cells that we want to work on is - // identified via a so-called - // subdomain_id, as we - // also did in step-18. All we need - // to change is hence to perform - // the cell-related operations only - // on cells that are owned by the - // current process (as opposed to - // ghost or artificial cells), - // i.e. for which the subdomain id - // equals the number of the process - // ID. Since this is a commonly - // used operation, there is a - // shortcut for this operation: we - // can ask whether the cell is - // owned by the current processor - // using - // cell-@>is_locally_owned(). - // - // The second difference is the way - // we calculate the maximum - // value. Before, we could simply - // have a double - // variable that we checked against - // on each quadrature point for - // each cell. Now, we have to be a - // bit more careful since each - // processor only operates on a - // subset of cells. What we do is - // to first let each processor - // calculate the maximum among its - // cells, and then do a global - // communication operation - // Utilities::MPI::max - // that computes the maximum value - // among all the maximum values of - // the individual processors. MPI - // provides such a call, but it's - // even simpler to use the - // respective function in namespace - // Utilities::MPI using the MPI - // communicator object since that - // will do the right thing even if - // we work without MPI and on a - // single machine only. The call to - // Utilities::MPI::max - // needs two arguments, namely the - // local maximum (input) and the - // MPI communicator, which is - // MPI_COMM_WORLD in this example. + // @sect4{The BoussinesqFlowProblem helper functions} + // @sect5{BoussinesqFlowProblem::get_maximal_velocity} + // Except for two small details, + // the function to compute the + // global maximum of the velocity + // is the same as in step-31. The + // first detail is actually common + // to all functions that implement + // loops over all cells in the + // triangulation: When operating in + // %parallel, each processor can + // only work on a chunk of cells + // since each processor only has a + // certain part of the entire + // triangulation. This chunk of + // cells that we want to work on is + // identified via a so-called + // subdomain_id, as we + // also did in step-18. All we need + // to change is hence to perform + // the cell-related operations only + // on cells that are owned by the + // current process (as opposed to + // ghost or artificial cells), + // i.e. for which the subdomain id + // equals the number of the process + // ID. Since this is a commonly + // used operation, there is a + // shortcut for this operation: we + // can ask whether the cell is + // owned by the current processor + // using + // cell-@>is_locally_owned(). + // + // The second difference is the way + // we calculate the maximum + // value. Before, we could simply + // have a double + // variable that we checked against + // on each quadrature point for + // each cell. Now, we have to be a + // bit more careful since each + // processor only operates on a + // subset of cells. What we do is + // to first let each processor + // calculate the maximum among its + // cells, and then do a global + // communication operation + // Utilities::MPI::max + // that computes the maximum value + // among all the maximum values of + // the individual processors. MPI + // provides such a call, but it's + // even simpler to use the + // respective function in namespace + // Utilities::MPI using the MPI + // communicator object since that + // will do the right thing even if + // we work without MPI and on a + // single machine only. The call to + // Utilities::MPI::max + // needs two arguments, namely the + // local maximum (input) and the + // MPI communicator, which is + // MPI_COMM_WORLD in this example. template double BoussinesqFlowProblem::get_maximal_velocity () const { const QIterated quadrature_formula (QTrapez<1>(), - parameters.stokes_velocity_degree); + parameters.stokes_velocity_degree); const unsigned int n_q_points = quadrature_formula.size(); FEValues fe_values (mapping, stokes_fe, quadrature_formula, update_values); @@ -1625,46 +1625,46 @@ namespace Step32 double max_local_velocity = 0; typename DoFHandler::active_cell_iterator - cell = stokes_dof_handler.begin_active(), - endc = stokes_dof_handler.end(); + cell = stokes_dof_handler.begin_active(), + endc = stokes_dof_handler.end(); for (; cell!=endc; ++cell) if (cell->is_locally_owned()) - { - fe_values.reinit (cell); - fe_values[velocities].get_function_values (stokes_solution, - velocity_values); + { + fe_values.reinit (cell); + fe_values[velocities].get_function_values (stokes_solution, + velocity_values); - for (unsigned int q=0; q double BoussinesqFlowProblem::get_cfl_number () const { const QIterated quadrature_formula (QTrapez<1>(), - parameters.stokes_velocity_degree); + parameters.stokes_velocity_degree); const unsigned int n_q_points = quadrature_formula.size(); FEValues fe_values (mapping, stokes_fe, quadrature_formula, update_values); @@ -1675,63 +1675,63 @@ namespace Step32 double max_local_cfl = 0; typename DoFHandler::active_cell_iterator - cell = stokes_dof_handler.begin_active(), - endc = stokes_dof_handler.end(); + cell = stokes_dof_handler.begin_active(), + endc = stokes_dof_handler.end(); for (; cell!=endc; ++cell) if (cell->is_locally_owned()) - { - fe_values.reinit (cell); - fe_values[velocities].get_function_values (stokes_solution, - velocity_values); - - double max_local_velocity = 1e-10; - for (unsigned int q=0; qdiameter()); - } + { + fe_values.reinit (cell); + fe_values[velocities].get_function_values (stokes_solution, + velocity_values); + + double max_local_velocity = 1e-10; + for (unsigned int q=0; qdiameter()); + } return Utilities::MPI::max (max_local_cfl, MPI_COMM_WORLD); } - // @sect5{BoussinesqFlowProblem::get_entropy_variation} - // Next comes the computation of - // the global entropy variation - // $\|E(T)-\bar{E}(T)\|_\infty$ - // where the entropy $E$ is defined - // as discussed in the - // introduction. This is needed for - // the evaluation of the - // stabilization in the temperature - // equation as explained in the - // introduction. The entropy - // variation is actually only - // needed if we use $\alpha=2$ as a - // power in the residual - // computation. The infinity norm - // is computed by the maxima over - // quadrature points, as usual in - // discrete computations. - // - // In order to compute this quantity, we - // first have to find the space-average - // $\bar{E}(T)$ and then evaluate the - // maximum. However, that means that we - // would need to perform two loops. We can - // avoid the overhead by noting that - // $\|E(T)-\bar{E}(T)\|_\infty = - // \max\big(E_{\textrm{max}}(T)-\bar{E}(T), - // \bar{E}(T)-E_{\textrm{min}}(T)\big)$, i.e., the - // maximum out of the deviation from the - // average entropy in positive and negative - // directions. The four quantities we need - // for the latter formula (maximum entropy, - // minimum entropy, average entropy, area) - // can all be evaluated in the same loop - // over all cells, so we choose this - // simpler variant. + // @sect5{BoussinesqFlowProblem::get_entropy_variation} + // Next comes the computation of + // the global entropy variation + // $\|E(T)-\bar{E}(T)\|_\infty$ + // where the entropy $E$ is defined + // as discussed in the + // introduction. This is needed for + // the evaluation of the + // stabilization in the temperature + // equation as explained in the + // introduction. The entropy + // variation is actually only + // needed if we use $\alpha=2$ as a + // power in the residual + // computation. The infinity norm + // is computed by the maxima over + // quadrature points, as usual in + // discrete computations. + // + // In order to compute this quantity, we + // first have to find the space-average + // $\bar{E}(T)$ and then evaluate the + // maximum. However, that means that we + // would need to perform two loops. We can + // avoid the overhead by noting that + // $\|E(T)-\bar{E}(T)\|_\infty = + // \max\big(E_{\textrm{max}}(T)-\bar{E}(T), + // \bar{E}(T)-E_{\textrm{min}}(T)\big)$, i.e., the + // maximum out of the deviation from the + // average entropy in positive and negative + // directions. The four quantities we need + // for the latter formula (maximum entropy, + // minimum entropy, average entropy, area) + // can all be evaluated in the same loop + // over all cells, so we choose this + // simpler variant. template double BoussinesqFlowProblem::get_entropy_variation (const double average_temperature) const @@ -1743,212 +1743,213 @@ namespace Step32 const unsigned int n_q_points = quadrature_formula.size(); FEValues fe_values (temperature_fe, quadrature_formula, - update_values | update_JxW_values); + update_values | update_JxW_values); std::vector old_temperature_values(n_q_points); std::vector old_old_temperature_values(n_q_points); - // In the two functions above we - // computed the maximum of - // numbers that were all - // non-negative, so we knew that - // zero was certainly a lower - // bound. On the other hand, here - // we need to find the maximum - // deviation from the average - // value, i.e., we will need to - // know the maximal and minimal - // values of the entropy for - // which we don't a priori know - // the sign. - // - // To compute it, we can - // therefore start with the - // largest and smallest possible - // values we can store in a - // double precision number: The - // minimum is initialized with a - // bigger and the maximum with a - // smaller number than any one - // that is going to appear. We - // are then guaranteed that these - // numbers will be overwritten in - // the loop on the first cell or, - // if this processor does not own - // any cells, in the - // communication step at the - // latest. The following loop - // then computes the minimum and - // maximum local entropy as well - // as keeps track of the - // area/volume of the part of the - // domain we locally own and the - // integral over the entropy on - // it: + // In the two functions above we + // computed the maximum of + // numbers that were all + // non-negative, so we knew that + // zero was certainly a lower + // bound. On the other hand, here + // we need to find the maximum + // deviation from the average + // value, i.e., we will need to + // know the maximal and minimal + // values of the entropy for + // which we don't a priori know + // the sign. + // + // To compute it, we can + // therefore start with the + // largest and smallest possible + // values we can store in a + // double precision number: The + // minimum is initialized with a + // bigger and the maximum with a + // smaller number than any one + // that is going to appear. We + // are then guaranteed that these + // numbers will be overwritten in + // the loop on the first cell or, + // if this processor does not own + // any cells, in the + // communication step at the + // latest. The following loop + // then computes the minimum and + // maximum local entropy as well + // as keeps track of the + // area/volume of the part of the + // domain we locally own and the + // integral over the entropy on + // it: double min_entropy = std::numeric_limits::max(), - max_entropy = -std::numeric_limits::max(), - area = 0, - entropy_integrated = 0; + max_entropy = -std::numeric_limits::max(), + area = 0, + entropy_integrated = 0; typename DoFHandler::active_cell_iterator - cell = temperature_dof_handler.begin_active(), - endc = temperature_dof_handler.end(); + cell = temperature_dof_handler.begin_active(), + endc = temperature_dof_handler.end(); for (; cell!=endc; ++cell) if (cell->is_locally_owned()) - { - fe_values.reinit (cell); - fe_values.get_function_values (old_temperature_solution, - old_temperature_values); - fe_values.get_function_values (old_old_temperature_solution, - old_old_temperature_values); - for (unsigned int q=0; qarea, - // entropy_integrated), - // and get the extrema for - // maximum and minimum. We could - // do this through four different - // data exchanges, but we can it - // with two: Utilities::MPI::sum - // also exists in a variant that - // takes an array of values that - // are all to be summed up. And - // we can also utilize the - // Utilities::MPI::max function - // by realizing that forming the - // minimum over the minimal - // entropies equals forming the - // negative of the maximum over - // the negative of the minimal - // entropies; this maximum can - // then be combined with forming - // the maximum over the maximal - // entropies. + { + fe_values.reinit (cell); + fe_values.get_function_values (old_temperature_solution, + old_temperature_values); + fe_values.get_function_values (old_old_temperature_solution, + old_old_temperature_values); + for (unsigned int q=0; qarea, + // entropy_integrated), + // and get the extrema for + // maximum and minimum. We could + // do this through four different + // data exchanges, but we can it + // with two: Utilities::MPI::sum + // also exists in a variant that + // takes an array of values that + // are all to be summed up. And + // we can also utilize the + // Utilities::MPI::max function + // by realizing that forming the + // minimum over the minimal + // entropies equals forming the + // negative of the maximum over + // the negative of the minimal + // entropies; this maximum can + // then be combined with forming + // the maximum over the maximal + // entropies. const double local_sums[2] = { entropy_integrated, area }, - local_maxima[2] = { -min_entropy, max_entropy }; + local_maxima[2] = { -min_entropy, max_entropy }; double global_sums[2], global_maxima[2]; Utilities::MPI::sum (local_sums, MPI_COMM_WORLD, global_sums); Utilities::MPI::max (local_maxima, MPI_COMM_WORLD, global_maxima); - // Having computed everything - // this way, we can then compute - // the average entropy and find - // the $L^\infty$ norm by taking - // the larger of the deviation of - // the maximum or minimum from - // the average: + // Having computed everything + // this way, we can then compute + // the average entropy and find + // the $L^\infty$ norm by taking + // the larger of the deviation of + // the maximum or minimum from + // the average: const double average_entropy = global_sums[0] / global_sums[1]; const double entropy_diff = std::max(global_maxima[1] - average_entropy, - average_entropy - (-global_maxima[0])); + average_entropy - (-global_maxima[0])); return entropy_diff; } - // @sect5{BoussinesqFlowProblem::get_extrapolated_temperature_range} - // The next function computes the - // minimal and maximal value of the - // extrapolated temperature over - // the entire domain. Again, this - // is only a slightly modified - // version of the respective - // function in step-31. As in the - // function above, we collect local - // minima and maxima and then - // compute the global extrema using - // the same trick as above. - // - // As already discussed in step-31, the - // function needs to distinguish between - // the first and all following time steps - // because it uses a higher order - // temperature extrapolation scheme when at - // least two previous time steps are - // available. + // @sect5{BoussinesqFlowProblem::get_extrapolated_temperature_range} + // The next function computes the + // minimal and maximal value of the + // extrapolated temperature over + // the entire domain. Again, this + // is only a slightly modified + // version of the respective + // function in step-31. As in the + // function above, we collect local + // minima and maxima and then + // compute the global extrema using + // the same trick as above. + // + // As already discussed in step-31, the + // function needs to distinguish between + // the first and all following time steps + // because it uses a higher order + // temperature extrapolation scheme when at + // least two previous time steps are + // available. template std::pair BoussinesqFlowProblem::get_extrapolated_temperature_range () const { const QIterated quadrature_formula (QTrapez<1>(), - parameters.temperature_degree); + parameters.temperature_degree); const unsigned int n_q_points = quadrature_formula.size(); FEValues fe_values (mapping, temperature_fe, quadrature_formula, - update_values); + update_values); std::vector old_temperature_values(n_q_points); std::vector old_old_temperature_values(n_q_points); double min_local_temperature = std::numeric_limits::max(), - max_local_temperature = -std::numeric_limits::max(); + max_local_temperature = -std::numeric_limits::max(); if (timestep_number != 0) { - typename DoFHandler::active_cell_iterator - cell = temperature_dof_handler.begin_active(), - endc = temperature_dof_handler.end(); - for (; cell!=endc; ++cell) - if (cell->is_locally_owned()) - { - fe_values.reinit (cell); - fe_values.get_function_values (old_temperature_solution, - old_temperature_values); - fe_values.get_function_values (old_old_temperature_solution, - old_old_temperature_values); - - for (unsigned int q=0; q::active_cell_iterator + cell = temperature_dof_handler.begin_active(), + endc = temperature_dof_handler.end(); + for (; cell!=endc; ++cell) + if (cell->is_locally_owned()) + { + fe_values.reinit (cell); + fe_values.get_function_values (old_temperature_solution, + old_temperature_values); + fe_values.get_function_values (old_old_temperature_solution, + old_old_temperature_values); + + for (unsigned int q=0; q::active_cell_iterator - cell = temperature_dof_handler.begin_active(), - endc = temperature_dof_handler.end(); - for (; cell!=endc; ++cell) - if (cell->is_locally_owned()) - { - fe_values.reinit (cell); - fe_values.get_function_values (old_temperature_solution, - old_temperature_values); - - for (unsigned int q=0; q::active_cell_iterator + cell = temperature_dof_handler.begin_active(), + endc = temperature_dof_handler.end(); + for (; cell!=endc; ++cell) + if (cell->is_locally_owned()) + { + fe_values.reinit (cell); + fe_values.get_function_values (old_temperature_solution, + old_temperature_values); + + for (unsigned int q=0; q double BoussinesqFlowProblem:: compute_viscosity (const std::vector &old_temperature, - const std::vector &old_old_temperature, - const std::vector > &old_temperature_grads, - const std::vector > &old_old_temperature_grads, - const std::vector &old_temperature_laplacians, - const std::vector &old_old_temperature_laplacians, - const std::vector > &old_velocity_values, - const std::vector > &old_old_velocity_values, - const std::vector > &old_strain_rates, - const std::vector > &old_old_strain_rates, - const double global_u_infty, - const double global_T_variation, - const double average_temperature, - const double global_entropy_variation, - const double cell_diameter) const + const std::vector &old_old_temperature, + const std::vector > &old_temperature_grads, + const std::vector > &old_old_temperature_grads, + const std::vector &old_temperature_laplacians, + const std::vector &old_old_temperature_laplacians, + const std::vector > &old_velocity_values, + const std::vector > &old_old_velocity_values, + const std::vector > &old_strain_rates, + const std::vector > &old_old_strain_rates, + const double global_u_infty, + const double global_T_variation, + const double average_temperature, + const double global_entropy_variation, + const double cell_diameter) const { if (global_u_infty == 0) return 5e-3 * cell_diameter; @@ -1992,144 +1993,144 @@ namespace Step32 for (unsigned int q=0; q < n_q_points; ++q) { - const Tensor<1,dim> u = (old_velocity_values[q] + - old_old_velocity_values[q]) / 2; - - const SymmetricTensor<2,dim> strain_rate = (old_strain_rates[q] + - old_old_strain_rates[q]) / 2; - - const double T = (old_temperature[q] + old_old_temperature[q]) / 2; - const double dT_dt = (old_temperature[q] - old_old_temperature[q]) - / old_time_step; - const double u_grad_T = u * (old_temperature_grads[q] + - old_old_temperature_grads[q]) / 2; - - const double kappa_Delta_T = EquationData::kappa - * (old_temperature_laplacians[q] + - old_old_temperature_laplacians[q]) / 2; - const double gamma - = ((EquationData::radiogenic_heating * EquationData::density(T) - + - 2 * EquationData::eta * strain_rate * strain_rate) / - (EquationData::density(T) * EquationData::specific_heat)); - - double residual - = std::abs(dT_dt + u_grad_T - kappa_Delta_T - gamma); - if (parameters.stabilization_alpha == 2) - residual *= std::abs(T - average_temperature); - - max_residual = std::max (residual, max_residual); - max_velocity = std::max (std::sqrt (u*u), max_velocity); + const Tensor<1,dim> u = (old_velocity_values[q] + + old_old_velocity_values[q]) / 2; + + const SymmetricTensor<2,dim> strain_rate = (old_strain_rates[q] + + old_old_strain_rates[q]) / 2; + + const double T = (old_temperature[q] + old_old_temperature[q]) / 2; + const double dT_dt = (old_temperature[q] - old_old_temperature[q]) + / old_time_step; + const double u_grad_T = u * (old_temperature_grads[q] + + old_old_temperature_grads[q]) / 2; + + const double kappa_Delta_T = EquationData::kappa + * (old_temperature_laplacians[q] + + old_old_temperature_laplacians[q]) / 2; + const double gamma + = ((EquationData::radiogenic_heating * EquationData::density(T) + + + 2 * EquationData::eta * strain_rate * strain_rate) / + (EquationData::density(T) * EquationData::specific_heat)); + + double residual + = std::abs(dT_dt + u_grad_T - kappa_Delta_T - gamma); + if (parameters.stabilization_alpha == 2) + residual *= std::abs(T - average_temperature); + + max_residual = std::max (residual, max_residual); + max_velocity = std::max (std::sqrt (u*u), max_velocity); } const double max_viscosity = (parameters.stabilization_beta * - max_velocity * cell_diameter); + max_velocity * cell_diameter); if (timestep_number == 0) return max_viscosity; else { - Assert (old_time_step > 0, ExcInternalError()); - - double entropy_viscosity; - if (parameters.stabilization_alpha == 2) - entropy_viscosity = (parameters.stabilization_c_R * - cell_diameter * cell_diameter * - max_residual / - global_entropy_variation); - else - entropy_viscosity = (parameters.stabilization_c_R * - cell_diameter * global_Omega_diameter * - max_velocity * max_residual / - (global_u_infty * global_T_variation)); - - return std::min (max_viscosity, entropy_viscosity); + Assert (old_time_step > 0, ExcInternalError()); + + double entropy_viscosity; + if (parameters.stabilization_alpha == 2) + entropy_viscosity = (parameters.stabilization_c_R * + cell_diameter * cell_diameter * + max_residual / + global_entropy_variation); + else + entropy_viscosity = (parameters.stabilization_c_R * + cell_diameter * global_Omega_diameter * + max_velocity * max_residual / + (global_u_infty * global_T_variation)); + + return std::min (max_viscosity, entropy_viscosity); } } - // @sect5{BoussinesqFlowProblem::project_temperature_field} - - // This function is new compared to - // step-31. What is does is to re-implement - // the library function - // VectorTools::project() for - // an MPI-based parallelization, a function - // we used for generating an initial vector - // for temperature based on some initial - // function. The library function only - // works with shared memory but doesn't - // know how to utilize multiple machines - // coupled through MPI to compute the - // projected field. The details of a - // project() function are not - // very difficult. All we do is to use a - // mass matrix and put the evaluation of - // the initial value function on the right - // hand side. The mass matrix for - // temperature we can simply generate using - // the respective assembly function, so all - // we need to do here is to create the - // right hand side and do a CG solve. The - // assembly function does a loop over all - // cells and evaluates the function in the - // EquationData namespace, and - // does this only on cells owned by the - // respective processor. The implementation - // of this assembly differs from the - // assembly we do for the principal - // assembly functions further down (which - // include thread-based parallelization - // with the WorkStream concept). Here we - // chose to keep things simple (keeping in - // mind that this function is also only - // called once at the beginning of the - // program, not in every time step), and - // generating the right hand side is cheap - // anyway so we won't even notice that this - // part is not parallized by threads. - // - // Regarding the implementation of - // inhomogeneous Dirichlet boundary - // conditions: Since we use the temperature - // ConstraintMatrix, we could apply the - // boundary conditions directly when - // building the respective matrix and right - // hand side. In this case, the boundary - // conditions are inhomogeneous, which - // makes this procedure somewhat tricky - // since we get the matrix from some other - // function that uses its own integration - // and assembly loop. However, the correct - // imposition of boundary conditions needs - // the matrix data we work on plus the - // right hand side simultaneously, since - // the right hand side is created by - // Gaussian elimination on the matrix - // rows. In order to not introduce the - // matrix assembly at this place, but still - // having the matrix data available, we - // choose to create a dummy matrix - // matrix_for_bc that we only - // fill with data when we need it for - // imposing boundary conditions. These - // positions are exactly those where we - // have an inhomogeneous entry in the - // ConstraintMatrix. There are only a few - // such positions (on the boundary DoFs), - // so it is still much cheaper to use this - // function than to create the full matrix - // here. To implement this, we ask the - // constraint matrix whether the DoF under - // consideration is inhomogeneously - // constrained. In that case, we generate - // the respective matrix column that we - // need for creating the correct right hand - // side. Note that this (manually - // generated) matrix entry needs to be - // exactly the entry that we would fill the - // matrix with — otherwise, this will - // not work. + // @sect5{BoussinesqFlowProblem::project_temperature_field} + + // This function is new compared to + // step-31. What is does is to re-implement + // the library function + // VectorTools::project() for + // an MPI-based parallelization, a function + // we used for generating an initial vector + // for temperature based on some initial + // function. The library function only + // works with shared memory but doesn't + // know how to utilize multiple machines + // coupled through MPI to compute the + // projected field. The details of a + // project() function are not + // very difficult. All we do is to use a + // mass matrix and put the evaluation of + // the initial value function on the right + // hand side. The mass matrix for + // temperature we can simply generate using + // the respective assembly function, so all + // we need to do here is to create the + // right hand side and do a CG solve. The + // assembly function does a loop over all + // cells and evaluates the function in the + // EquationData namespace, and + // does this only on cells owned by the + // respective processor. The implementation + // of this assembly differs from the + // assembly we do for the principal + // assembly functions further down (which + // include thread-based parallelization + // with the WorkStream concept). Here we + // chose to keep things simple (keeping in + // mind that this function is also only + // called once at the beginning of the + // program, not in every time step), and + // generating the right hand side is cheap + // anyway so we won't even notice that this + // part is not parallized by threads. + // + // Regarding the implementation of + // inhomogeneous Dirichlet boundary + // conditions: Since we use the temperature + // ConstraintMatrix, we could apply the + // boundary conditions directly when + // building the respective matrix and right + // hand side. In this case, the boundary + // conditions are inhomogeneous, which + // makes this procedure somewhat tricky + // since we get the matrix from some other + // function that uses its own integration + // and assembly loop. However, the correct + // imposition of boundary conditions needs + // the matrix data we work on plus the + // right hand side simultaneously, since + // the right hand side is created by + // Gaussian elimination on the matrix + // rows. In order to not introduce the + // matrix assembly at this place, but still + // having the matrix data available, we + // choose to create a dummy matrix + // matrix_for_bc that we only + // fill with data when we need it for + // imposing boundary conditions. These + // positions are exactly those where we + // have an inhomogeneous entry in the + // ConstraintMatrix. There are only a few + // such positions (on the boundary DoFs), + // so it is still much cheaper to use this + // function than to create the full matrix + // here. To implement this, we ask the + // constraint matrix whether the DoF under + // consideration is inhomogeneously + // constrained. In that case, we generate + // the respective matrix column that we + // need for creating the correct right hand + // side. Note that this (manually + // generated) matrix entry needs to be + // exactly the entry that we would fill the + // matrix with — otherwise, this will + // not work. template void BoussinesqFlowProblem::project_temperature_field () { @@ -2137,12 +2138,12 @@ namespace Step32 QGauss quadrature(parameters.temperature_degree+2); UpdateFlags update_flags = UpdateFlags(update_values | - update_quadrature_points | - update_JxW_values); + update_quadrature_points | + update_JxW_values); FEValues fe_values (mapping, temperature_fe, quadrature, update_flags); const unsigned int dofs_per_cell = fe_values.dofs_per_cell, - n_q_points = fe_values.n_quadrature_points; + n_q_points = fe_values.n_quadrature_points; std::vector local_dof_indices (dofs_per_cell); Vector cell_vector (dofs_per_cell); @@ -2151,53 +2152,53 @@ namespace Step32 std::vector rhs_values(n_q_points); TrilinosWrappers::MPI::Vector - rhs (temperature_mass_matrix.row_partitioner()), - solution (temperature_mass_matrix.row_partitioner()); + rhs (temperature_mass_matrix.row_partitioner()), + solution (temperature_mass_matrix.row_partitioner()); const EquationData::TemperatureInitialValues initial_temperature; typename DoFHandler::active_cell_iterator - cell = temperature_dof_handler.begin_active(), - endc = temperature_dof_handler.end(); + cell = temperature_dof_handler.begin_active(), + endc = temperature_dof_handler.end(); for (; cell!=endc; ++cell) if (cell->is_locally_owned()) - { - cell->get_dof_indices (local_dof_indices); - fe_values.reinit (cell); - - initial_temperature.value_list (fe_values.get_quadrature_points(), - rhs_values); - - cell_vector = 0; - matrix_for_bc = 0; - for (unsigned int point=0; pointget_dof_indices (local_dof_indices); + fe_values.reinit (cell); + + initial_temperature.value_list (fe_values.get_quadrature_points(), + rhs_values); + + cell_vector = 0; + matrix_for_bc = 0; + for (unsigned int point=0; point cg(solver_control); @@ -2208,38 +2209,38 @@ namespace Step32 temperature_constraints.distribute (solution); - // Having so computed the current - // temperature field, let us set the - // member variable that holds the - // temperature nodes. Strictly speaking, - // we really only need to set - // old_temperature_solution - // since the first thing we will do is to - // compute the Stokes solution that only - // requires the previous time step's - // temperature field. That said, nothing - // good can come from not initializing - // the other vectors as well (especially - // since it's a relatively cheap - // operation and we only have to do it - // once at the beginning of the program) - // if we ever want to extend our - // numerical method or physical model, - // and so we initialize - // temperature_solution and - // old_old_temperature_solution - // as well. As a sidenote, while the - // solution vector is - // strictly distributed (i.e. each - // processor only stores a mutually - // exclusive subset of elements), the - // assignment makes sure that the vectors - // on the left hand side (which where - // initialized to contain ghost elements - // as well) also get the correct ghost - // elements. In other words, the - // assignment here requires communication - // between processors: + // Having so computed the current + // temperature field, let us set the + // member variable that holds the + // temperature nodes. Strictly speaking, + // we really only need to set + // old_temperature_solution + // since the first thing we will do is to + // compute the Stokes solution that only + // requires the previous time step's + // temperature field. That said, nothing + // good can come from not initializing + // the other vectors as well (especially + // since it's a relatively cheap + // operation and we only have to do it + // once at the beginning of the program) + // if we ever want to extend our + // numerical method or physical model, + // and so we initialize + // temperature_solution and + // old_old_temperature_solution + // as well. As a sidenote, while the + // solution vector is + // strictly distributed (i.e. each + // processor only stores a mutually + // exclusive subset of elements), the + // assignment makes sure that the vectors + // on the left hand side (which where + // initialized to contain ghost elements + // as well) also get the correct ghost + // elements. In other words, the + // assignment here requires communication + // between processors: temperature_solution = solution; old_temperature_solution = solution; old_old_temperature_solution = solution; @@ -2248,70 +2249,70 @@ namespace Step32 - // @sect4{The BoussinesqFlowProblem setup functions} - - // The following three functions set up the - // Stokes matrix, the matrix used for the - // Stokes preconditioner, and the - // temperature matrix. The code is mostly - // the same as in step-31, but it has been - // broken out into three functions of their - // own for simplicity. - // - // The main functional difference between - // the code here and that in step-31 is - // that the matrices we want to set up are - // distributed across multiple - // processors. Since we still want to build - // up the sparsity pattern first for - // efficiency reasons, we could continue to - // build the entire sparsity pattern - // as a - // BlockCompressedSimpleSparsityPattern, as - // we did in step-31. However, that would - // be inefficient: every processor would - // build the same sparsity pattern, but - // only initialize a small part of the - // matrix using it. It also violates the - // principle that every processor should - // only work on those cells it owns (and, - // if necessary the layer of ghost cells - // around it). - // - // Rather, we use an object of type - // TrilinosWrappers::BlockSparsityPattern, - // which is (obviously) a wrapper around a - // sparsity pattern object provided by - // Trilinos. The advantage is that the - // Trilinos sparsity pattern class can - // communicate across multiple processors: - // if this processor fills in all the - // nonzero entries that result from the - // cells it owns, and every other processor - // does so as well, then at the end after - // some MPI communication initiated by the - // compress() call, we will - // have the globally assembled sparsity - // pattern available with which the global - // matrix can be initialized. - // - // The only other change we need to make is - // to tell the - // DoFTools::make_sparsity_pattern() function - // that it is only supposed to work on a - // subset of cells, namely the ones whose - // subdomain_id equals the - // number of the current processor, and to - // ignore all other cells. - // - // This strategy is replicated across all - // three of the following functions. - // - // Note that Trilinos matrices store the - // information contained in the sparsity - // patterns, so we can safely release the - // sp variable once the matrix - // has been given the sparsity structure. + // @sect4{The BoussinesqFlowProblem setup functions} + + // The following three functions set up the + // Stokes matrix, the matrix used for the + // Stokes preconditioner, and the + // temperature matrix. The code is mostly + // the same as in step-31, but it has been + // broken out into three functions of their + // own for simplicity. + // + // The main functional difference between + // the code here and that in step-31 is + // that the matrices we want to set up are + // distributed across multiple + // processors. Since we still want to build + // up the sparsity pattern first for + // efficiency reasons, we could continue to + // build the entire sparsity pattern + // as a + // BlockCompressedSimpleSparsityPattern, as + // we did in step-31. However, that would + // be inefficient: every processor would + // build the same sparsity pattern, but + // only initialize a small part of the + // matrix using it. It also violates the + // principle that every processor should + // only work on those cells it owns (and, + // if necessary the layer of ghost cells + // around it). + // + // Rather, we use an object of type + // TrilinosWrappers::BlockSparsityPattern, + // which is (obviously) a wrapper around a + // sparsity pattern object provided by + // Trilinos. The advantage is that the + // Trilinos sparsity pattern class can + // communicate across multiple processors: + // if this processor fills in all the + // nonzero entries that result from the + // cells it owns, and every other processor + // does so as well, then at the end after + // some MPI communication initiated by the + // compress() call, we will + // have the globally assembled sparsity + // pattern available with which the global + // matrix can be initialized. + // + // The only other change we need to make is + // to tell the + // DoFTools::make_sparsity_pattern() function + // that it is only supposed to work on a + // subset of cells, namely the ones whose + // subdomain_id equals the + // number of the current processor, and to + // ignore all other cells. + // + // This strategy is replicated across all + // three of the following functions. + // + // Note that Trilinos matrices store the + // information contained in the sparsity + // patterns, so we can safely release the + // sp variable once the matrix + // has been given the sparsity structure. template void BoussinesqFlowProblem:: setup_stokes_matrix (const std::vector &stokes_partitioning) @@ -2319,22 +2320,22 @@ namespace Step32 stokes_matrix.clear (); TrilinosWrappers::BlockSparsityPattern sp (stokes_partitioning, - MPI_COMM_WORLD); + MPI_COMM_WORLD); Table<2,DoFTools::Coupling> coupling (dim+1, dim+1); for (unsigned int c=0; c coupling (dim+1, dim+1); for (unsigned int c=0; cstd::locale("")) implies - // printing numbers with a comma separator - // for every third digit (i.e., thousands, - // millions, billions). + // The remainder of the setup function + // (after splitting out the three functions + // above) mostly has to deal with the + // things we need to do for parallelization + // across processors. Because setting all + // of this up is a significant compute time + // expense of the program, we put + // everything we do here into a timer group + // so that we can get summary information + // about the fraction of time spent in this + // part of the program at its end. + // + // At the top as usual we enumerate degrees + // of freedom and sort them by + // component/block, followed by writing + // their numbers to the screen from + // processor zero. The DoFHandler::distributed_dofs() function, when applied to a parallel::distributed::Triangulation object, sorts degrees of freedom in such a + // way that all degrees of freedom + // associated with subdomain zero come + // before all those associated with + // subdomain one, etc. For the Stokes + // part, this entails, however, that + // velocities and pressures become + // intermixed, but this is trivially + // solved by sorting again by blocks; it + // is worth noting that this latter + // operation leaves the relative ordering + // of all velocities and pressures alone, + // i.e. within the velocity block we will + // still have all those associated with + // subdomain zero before all velocities + // associated with subdomain one, + // etc. This is important since we store + // each of the blocks of this matrix + // distributed across all processors and + // want this to be done in such a way + // that each processor stores that part + // of the matrix that is roughly equal to + // the degrees of freedom located on + // those cells that it will actually work + // on. + // + // When printing the numbers of degrees of + // freedom, note that these numbers are + // going to be large if we use many + // processors. Consequently, we let the + // stream put a comma separator in between + // every three digits. The state of the + // stream, using the locale, is saved from + // before to after this operation. While + // slightly opaque, the code works because + // the default locale (which we get using + // the constructor call + // std::locale("")) implies + // printing numbers with a comma separator + // for every third digit (i.e., thousands, + // millions, billions). template void BoussinesqFlowProblem::setup_dofs () { @@ -2468,38 +2469,38 @@ namespace Step32 std::vector stokes_dofs_per_block (2); DoFTools::count_dofs_per_block (stokes_dof_handler, stokes_dofs_per_block, - stokes_sub_blocks); + stokes_sub_blocks); const unsigned int n_u = stokes_dofs_per_block[0], - n_p = stokes_dofs_per_block[1], - n_T = temperature_dof_handler.n_dofs(); + n_p = stokes_dofs_per_block[1], + n_T = temperature_dof_handler.n_dofs(); std::locale s = pcout.get_stream().getloc(); pcout.get_stream().imbue(std::locale("")); pcout << "Number of active cells: " - << triangulation.n_global_active_cells() - << " (on " - << triangulation.n_levels() - << " levels)" - << std::endl - << "Number of degrees of freedom: " - << n_u + n_p + n_T - << " (" << n_u << '+' << n_p << '+'<< n_T <<')' - << std::endl - << std::endl; + << triangulation.n_global_active_cells() + << " (on " + << triangulation.n_levels() + << " levels)" + << std::endl + << "Number of degrees of freedom: " + << n_u + n_p + n_T + << " (" << n_u << '+' << n_p << '+'<< n_T <<')' + << std::endl + << std::endl; pcout.get_stream().imbue(s); - // After this, we have to set up the - // various partitioners (of type - // IndexSet, see the - // introduction) that describe which - // parts of each matrix or vector will be - // stored where, then call the functions - // that actually set up the matrices, and - // at the end also resize the various - // vectors we keep around in this - // program. + // After this, we have to set up the + // various partitioners (of type + // IndexSet, see the + // introduction) that describe which + // parts of each matrix or vector will be + // stored where, then call the functions + // that actually set up the matrices, and + // at the end also resize the various + // vectors we keep around in this + // program. std::vector stokes_partitioning, stokes_relevant_partitioning; IndexSet temperature_partitioning (n_T), temperature_relevant_partitioning (n_T); IndexSet stokes_relevant_set; @@ -2509,49 +2510,49 @@ namespace Step32 stokes_partitioning.push_back(stokes_index_set.get_view(n_u,n_u+n_p)); DoFTools::extract_locally_relevant_dofs (stokes_dof_handler, - stokes_relevant_set); + stokes_relevant_set); stokes_relevant_partitioning.push_back(stokes_relevant_set.get_view(0,n_u)); stokes_relevant_partitioning.push_back(stokes_relevant_set.get_view(n_u,n_u+n_p)); temperature_partitioning = temperature_dof_handler.locally_owned_dofs(); DoFTools::extract_locally_relevant_dofs (temperature_dof_handler, - temperature_relevant_partitioning); + temperature_relevant_partitioning); } - // Following this, we can compute - // constraints for the solution vectors, - // including hanging node constraints and - // homogenous and inhomogenous boundary - // values for the Stokes and temperature - // fields. Note that as for everything - // else, the constraint objects can not - // hold all constraints on every - // processor. Rather, each processor - // needs to store only those that are - // actually necessary for correctness - // given that it only assembles linear - // systems on cells it owns. As discussed - // in the - // @ref distributed_paper "this paper", - // the set of constraints we need to know - // about is exactly the set of - // constraints on all locally relevant - // degrees of freedom, so this is what we - // use to initialize the constraint - // objects. + // Following this, we can compute + // constraints for the solution vectors, + // including hanging node constraints and + // homogenous and inhomogenous boundary + // values for the Stokes and temperature + // fields. Note that as for everything + // else, the constraint objects can not + // hold all constraints on every + // processor. Rather, each processor + // needs to store only those that are + // actually necessary for correctness + // given that it only assembles linear + // systems on cells it owns. As discussed + // in the + // @ref distributed_paper "this paper", + // the set of constraints we need to know + // about is exactly the set of + // constraints on all locally relevant + // degrees of freedom, so this is what we + // use to initialize the constraint + // objects. { stokes_constraints.clear (); stokes_constraints.reinit (stokes_relevant_set); DoFTools::make_hanging_node_constraints (stokes_dof_handler, - stokes_constraints); + stokes_constraints); FEValuesExtractors::Vector velocity_components(0); VectorTools::interpolate_boundary_values (stokes_dof_handler, - 0, - ZeroFunction(dim+1), - stokes_constraints, - stokes_fe.component_mask(velocity_components)); + 0, + ZeroFunction(dim+1), + stokes_constraints, + stokes_fe.component_mask(velocity_components)); std::set no_normal_flux_boundaries; no_normal_flux_boundaries.insert (1); @@ -2578,13 +2579,13 @@ namespace Step32 temperature_constraints.close (); } - // All this done, we can then initialize - // the various matrix and vector objects - // to their proper sizes. At the end, we - // also record that all matrices and - // preconditioners have to be re-computed - // at the beginning of the next time - // step. + // All this done, we can then initialize + // the various matrix and vector objects + // to their proper sizes. At the end, we + // also record that all matrices and + // preconditioners have to be re-computed + // at the beginning of the next time + // step. setup_stokes_matrix (stokes_partitioning); setup_stokes_preconditioner (stokes_partitioning); setup_temperature_matrices (temperature_partitioning); @@ -2608,56 +2609,56 @@ namespace Step32 - // @sect4{The BoussinesqFlowProblem assembly functions} - // - // Following the discussion in the - // introduction and in the @ref threads - // module, we split the assembly functions - // into different parts: - // - //
  • The local calculations of - // matrices and right hand sides, given a - // certain cell as input (these functions - // are named local_assemble_* - // below). The resulting function is, in - // other words, essentially the body of the - // loop over all cells in step-31. Note, - // however, that these functions store the - // result from the local calculations in - // variables of classes from the CopyData - // namespace. - // - //
  • These objects are then given to the - // second step which writes the local data - // into the global data structures (these - // functions are named - // copy_local_to_global_* - // below). These functions are pretty - // trivial. - // - //
  • These two subfunctions are then used - // in the respective assembly routine - // (called assemble_* below), - // where a WorkStream object is set up and - // runs over all the cells that belong to - // the processor's subdomain.
- - // @sect5{Stokes preconditioner assembly} - // - // Let us start with the functions that - // builds the Stokes preconditioner. The - // first two of these are pretty trivial, - // given the discussion above. Note in - // particular that the main point in using - // the scratch data object is that we want - // to avoid allocating any objects on the - // free space each time we visit a new - // cell. As a consequence, the assembly - // function below only has automatic local - // variables, and everything else is - // accessed through the scratch data - // object, which is allocated only once - // before we start the loop over all cells: + // @sect4{The BoussinesqFlowProblem assembly functions} + // + // Following the discussion in the + // introduction and in the @ref threads + // module, we split the assembly functions + // into different parts: + // + //
  • The local calculations of + // matrices and right hand sides, given a + // certain cell as input (these functions + // are named local_assemble_* + // below). The resulting function is, in + // other words, essentially the body of the + // loop over all cells in step-31. Note, + // however, that these functions store the + // result from the local calculations in + // variables of classes from the CopyData + // namespace. + // + //
  • These objects are then given to the + // second step which writes the local data + // into the global data structures (these + // functions are named + // copy_local_to_global_* + // below). These functions are pretty + // trivial. + // + //
  • These two subfunctions are then used + // in the respective assembly routine + // (called assemble_* below), + // where a WorkStream object is set up and + // runs over all the cells that belong to + // the processor's subdomain.
+ + // @sect5{Stokes preconditioner assembly} + // + // Let us start with the functions that + // builds the Stokes preconditioner. The + // first two of these are pretty trivial, + // given the discussion above. Note in + // particular that the main point in using + // the scratch data object is that we want + // to avoid allocating any objects on the + // free space each time we visit a new + // cell. As a consequence, the assembly + // function below only has automatic local + // variables, and everything else is + // accessed through the scratch data + // object, which is allocated only once + // before we start the loop over all cells: template void BoussinesqFlowProblem:: @@ -2711,89 +2712,89 @@ namespace Step32 } - // Now for the function that actually puts - // things together, using the WorkStream - // functions. WorkStream::run needs a - // start and end iterator to enumerate the - // cells it is supposed to work - // on. Typically, one would use - // DoFHandler::begin_active() and - // DoFHandler::end() for that but here we - // actually only want the subset of cells - // that in fact are owned by the current - // processor. This is where the - // FilteredIterator class comes into play: - // you give it a range of cells and it - // provides an iterator that only iterates - // over that subset of cells that satisfy a - // certain predicate (a predicate is a - // function of one argument that either - // returns true or false). The predicate we - // use here is - // IteratorFilters::LocallyOwnedCell, i.e., - // it returns true exactly if the cell is - // owned by the current processor. The - // resulting iterator range is then exactly - // what we need. - // - // With this obstacle out of the way, we - // call the WorkStream::run function with - // this set of cells, scratch and copy - // objects, and with pointers to two - // functions: the local assembly and - // copy-local-to-global function. These - // functions need to have very specific - // signatures: three arguments in the first - // and one argument in the latter case (see - // the documentation of the WorkStream::run - // function for the meaning of these - // arguments). Note how we use the - // construct std_cxx1x::bind - // to create a function object that - // satisfies this requirement. It uses - // placeholders _1, std_cxx1x::_2, - // _3 for the local assembly - // function that specify cell, scratch - // data, and copy data, as well as the - // placeholder _1 for the copy - // function that expects the data to be - // written into the global matrix. On the - // other hand, the implicit zeroth argument - // of member functions (namely the - // this pointer of the object - // on which that member function is to - // operate on) is bound to the - // this pointer of the current - // function. The WorkStream::run function, - // as a consequence, does not need to know - // anything about the object these - // functions work on. - // - // When the WorkStream is executed, it will - // create several local assembly routines - // of the first kind for several cells and - // let some available processors work on - // them. The function that needs to be - // synchronized, i.e., the write operation - // into the global matrix, however, is - // executed by only one thread at a time in - // the prescribed order. Of course, this - // only holds for the parallelization on a - // single MPI process. Different MPI - // processes will have their own WorkStream - // objects and do that work completely - // independently (and in different memory - // spaces). In a distributed calculation, - // some data will accumulate at degrees of - // freedom that are not owned by the - // respective processor. It would be - // inefficient to send data around every - // time we encounter such a dof. What - // happens instead is that the Trilinos - // sparse matrix will keep that data and - // send it to the owner at the end of - // assembly, by calling the - // compress() command. + // Now for the function that actually puts + // things together, using the WorkStream + // functions. WorkStream::run needs a + // start and end iterator to enumerate the + // cells it is supposed to work + // on. Typically, one would use + // DoFHandler::begin_active() and + // DoFHandler::end() for that but here we + // actually only want the subset of cells + // that in fact are owned by the current + // processor. This is where the + // FilteredIterator class comes into play: + // you give it a range of cells and it + // provides an iterator that only iterates + // over that subset of cells that satisfy a + // certain predicate (a predicate is a + // function of one argument that either + // returns true or false). The predicate we + // use here is + // IteratorFilters::LocallyOwnedCell, i.e., + // it returns true exactly if the cell is + // owned by the current processor. The + // resulting iterator range is then exactly + // what we need. + // + // With this obstacle out of the way, we + // call the WorkStream::run function with + // this set of cells, scratch and copy + // objects, and with pointers to two + // functions: the local assembly and + // copy-local-to-global function. These + // functions need to have very specific + // signatures: three arguments in the first + // and one argument in the latter case (see + // the documentation of the WorkStream::run + // function for the meaning of these + // arguments). Note how we use the + // construct std_cxx1x::bind + // to create a function object that + // satisfies this requirement. It uses + // placeholders _1, std_cxx1x::_2, + // _3 for the local assembly + // function that specify cell, scratch + // data, and copy data, as well as the + // placeholder _1 for the copy + // function that expects the data to be + // written into the global matrix. On the + // other hand, the implicit zeroth argument + // of member functions (namely the + // this pointer of the object + // on which that member function is to + // operate on) is bound to the + // this pointer of the current + // function. The WorkStream::run function, + // as a consequence, does not need to know + // anything about the object these + // functions work on. + // + // When the WorkStream is executed, it will + // create several local assembly routines + // of the first kind for several cells and + // let some available processors work on + // them. The function that needs to be + // synchronized, i.e., the write operation + // into the global matrix, however, is + // executed by only one thread at a time in + // the prescribed order. Of course, this + // only holds for the parallelization on a + // single MPI process. Different MPI + // processes will have their own WorkStream + // objects and do that work completely + // independently (and in different memory + // spaces). In a distributed calculation, + // some data will accumulate at degrees of + // freedom that are not owned by the + // respective processor. It would be + // inefficient to send data around every + // time we encounter such a dof. What + // happens instead is that the Trilinos + // sparse matrix will keep that data and + // send it to the owner at the end of + // assembly, by calling the + // compress() command. template void BoussinesqFlowProblem::assemble_stokes_preconditioner () @@ -2803,47 +2804,47 @@ namespace Step32 const QGauss quadrature_formula(parameters.stokes_velocity_degree+1); typedef - FilteredIterator::active_cell_iterator> - CellFilter; + FilteredIterator::active_cell_iterator> + CellFilter; WorkStream:: - run (CellFilter (IteratorFilters::LocallyOwnedCell(), - stokes_dof_handler.begin_active()), - CellFilter (IteratorFilters::LocallyOwnedCell(), - stokes_dof_handler.end()), - std_cxx1x::bind (&BoussinesqFlowProblem:: - local_assemble_stokes_preconditioner, - this, - std_cxx1x::_1, - std_cxx1x::_2, - std_cxx1x::_3), - std_cxx1x::bind (&BoussinesqFlowProblem:: - copy_local_to_global_stokes_preconditioner, - this, - std_cxx1x::_1), - Assembly::Scratch:: - StokesPreconditioner (stokes_fe, quadrature_formula, - mapping, - update_JxW_values | - update_values | - update_gradients), - Assembly::CopyData:: - StokesPreconditioner (stokes_fe)); + run (CellFilter (IteratorFilters::LocallyOwnedCell(), + stokes_dof_handler.begin_active()), + CellFilter (IteratorFilters::LocallyOwnedCell(), + stokes_dof_handler.end()), + std_cxx1x::bind (&BoussinesqFlowProblem:: + local_assemble_stokes_preconditioner, + this, + std_cxx1x::_1, + std_cxx1x::_2, + std_cxx1x::_3), + std_cxx1x::bind (&BoussinesqFlowProblem:: + copy_local_to_global_stokes_preconditioner, + this, + std_cxx1x::_1), + Assembly::Scratch:: + StokesPreconditioner (stokes_fe, quadrature_formula, + mapping, + update_JxW_values | + update_values | + update_gradients), + Assembly::CopyData:: + StokesPreconditioner (stokes_fe)); stokes_preconditioner_matrix.compress(); } - // The final function in this block - // initiates assembly of the Stokes - // preconditioner matrix and then in fact - // builds the Stokes preconditioner. It is - // mostly the same as in the serial - // case. The only difference to step-31 is - // that we use a Jacobi preconditioner for - // the pressure mass matrix instead of IC, - // as discussed in the introduction. + // The final function in this block + // initiates assembly of the Stokes + // preconditioner matrix and then in fact + // builds the Stokes preconditioner. It is + // mostly the same as in the serial + // case. The only difference to step-31 is + // that we use a Jacobi preconditioner for + // the pressure mass matrix instead of IC, + // as discussed in the introduction. template void BoussinesqFlowProblem::build_stokes_preconditioner () @@ -2859,7 +2860,7 @@ namespace Step32 std::vector > constant_modes; FEValuesExtractors::Vector velocity_components(0); DoFTools::extract_constant_modes (stokes_dof_handler, - stokes_fe.component_mask(velocity_components), + stokes_fe.component_mask(velocity_components), constant_modes); Mp_preconditioner.reset (new TrilinosWrappers::PreconditionJacobi()); @@ -2883,27 +2884,27 @@ namespace Step32 } - // @sect5{Stokes system assembly} - - // The next three functions implement the - // assembly of the Stokes system, again - // split up into a part performing local - // calculations, one for writing the local - // data into the global matrix and vector, - // and one for actually running the loop - // over all cells with the help of the - // WorkStream class. Note that the assembly - // of the Stokes matrix needs only to be - // done in case we have changed the - // mesh. Otherwise, just the - // (temperature-dependent) right hand side - // needs to be calculated here. Since we - // are working with distributed matrices - // and vectors, we have to call the - // respective compress() - // functions in the end of the assembly in - // order to send non-local data to the - // owner process. + // @sect5{Stokes system assembly} + + // The next three functions implement the + // assembly of the Stokes system, again + // split up into a part performing local + // calculations, one for writing the local + // data into the global matrix and vector, + // and one for actually running the loop + // over all cells with the help of the + // WorkStream class. Note that the assembly + // of the Stokes matrix needs only to be + // done in case we have changed the + // mesh. Otherwise, just the + // (temperature-dependent) right hand side + // needs to be calculated here. Since we + // are working with distributed matrices + // and vectors, we have to call the + // respective compress() + // functions in the end of the assembly in + // order to send non-local data to the + // owner process. template void BoussinesqFlowProblem:: @@ -2920,10 +2921,10 @@ namespace Step32 scratch.stokes_fe_values.reinit (cell); typename DoFHandler::active_cell_iterator - temperature_cell (&triangulation, - cell->level(), - cell->index(), - &temperature_dof_handler); + temperature_cell (&triangulation, + cell->level(), + cell->index(), + &temperature_dof_handler); scratch.temperature_fe_values.reinit (temperature_cell); if (rebuild_stokes_matrix) @@ -2960,8 +2961,8 @@ namespace Step32 * scratch.stokes_fe_values.JxW(q); const Tensor<1,dim> - gravity = EquationData::gravity_vector (scratch.stokes_fe_values - .quadrature_point(q)); + gravity = EquationData::gravity_vector (scratch.stokes_fe_values + .quadrature_point(q)); for (unsigned int i=0; i quadrature_formula(parameters.stokes_velocity_degree+1); typedef - FilteredIterator::active_cell_iterator> - CellFilter; + FilteredIterator::active_cell_iterator> + CellFilter; WorkStream:: - run (CellFilter (IteratorFilters::LocallyOwnedCell(), - stokes_dof_handler.begin_active()), - CellFilter (IteratorFilters::LocallyOwnedCell(), - stokes_dof_handler.end()), - std_cxx1x::bind (&BoussinesqFlowProblem:: - local_assemble_stokes_system, - this, - std_cxx1x::_1, - std_cxx1x::_2, - std_cxx1x::_3), - std_cxx1x::bind (&BoussinesqFlowProblem:: - copy_local_to_global_stokes_system, - this, - std_cxx1x::_1), - Assembly::Scratch:: - StokesSystem (stokes_fe, mapping, quadrature_formula, - (update_values | - update_quadrature_points | - update_JxW_values | - (rebuild_stokes_matrix == true - ? - update_gradients - : - UpdateFlags(0))), - temperature_fe, - update_values), - Assembly::CopyData:: - StokesSystem (stokes_fe)); + run (CellFilter (IteratorFilters::LocallyOwnedCell(), + stokes_dof_handler.begin_active()), + CellFilter (IteratorFilters::LocallyOwnedCell(), + stokes_dof_handler.end()), + std_cxx1x::bind (&BoussinesqFlowProblem:: + local_assemble_stokes_system, + this, + std_cxx1x::_1, + std_cxx1x::_2, + std_cxx1x::_3), + std_cxx1x::bind (&BoussinesqFlowProblem:: + copy_local_to_global_stokes_system, + this, + std_cxx1x::_1), + Assembly::Scratch:: + StokesSystem (stokes_fe, mapping, quadrature_formula, + (update_values | + update_quadrature_points | + update_JxW_values | + (rebuild_stokes_matrix == true + ? + update_gradients + : + UpdateFlags(0))), + temperature_fe, + update_values), + Assembly::CopyData:: + StokesSystem (stokes_fe)); stokes_matrix.compress(); stokes_rhs.compress(Add); @@ -3050,22 +3051,22 @@ namespace Step32 } - // @sect5{Temperature matrix assembly} - - // The task to be performed by the next - // three functions is to calculate a mass - // matrix and a Laplace matrix on the - // temperature system. These will be - // combined in order to yield the - // semi-implicit time stepping matrix that - // consists of the mass matrix plus a time - // step-dependent weight factor times the - // Laplace matrix. This function is again - // essentially the body of the loop over - // all cells from step-31. - // - // The two following functions perform - // similar services as the ones above. + // @sect5{Temperature matrix assembly} + + // The task to be performed by the next + // three functions is to calculate a mass + // matrix and a Laplace matrix on the + // temperature system. These will be + // combined in order to yield the + // semi-implicit time stepping matrix that + // consists of the mass matrix plus a time + // step-dependent weight factor times the + // Laplace matrix. This function is again + // essentially the body of the loop over + // all cells from step-31. + // + // The two following functions perform + // similar services as the ones above. template void BoussinesqFlowProblem:: local_assemble_temperature_matrix (const typename DoFHandler::active_cell_iterator &cell, @@ -3093,13 +3094,13 @@ namespace Step32 for (unsigned int j=0; j quadrature_formula(parameters.temperature_degree+2); typedef - FilteredIterator::active_cell_iterator> - CellFilter; + FilteredIterator::active_cell_iterator> + CellFilter; WorkStream:: - run (CellFilter (IteratorFilters::LocallyOwnedCell(), - temperature_dof_handler.begin_active()), - CellFilter (IteratorFilters::LocallyOwnedCell(), - temperature_dof_handler.end()), - std_cxx1x::bind (&BoussinesqFlowProblem:: - local_assemble_temperature_matrix, - this, - std_cxx1x::_1, - std_cxx1x::_2, - std_cxx1x::_3), - std_cxx1x::bind (&BoussinesqFlowProblem:: - copy_local_to_global_temperature_matrix, - this, - std_cxx1x::_1), - Assembly::Scratch:: - TemperatureMatrix (temperature_fe, mapping, quadrature_formula), - Assembly::CopyData:: - TemperatureMatrix (temperature_fe)); + run (CellFilter (IteratorFilters::LocallyOwnedCell(), + temperature_dof_handler.begin_active()), + CellFilter (IteratorFilters::LocallyOwnedCell(), + temperature_dof_handler.end()), + std_cxx1x::bind (&BoussinesqFlowProblem:: + local_assemble_temperature_matrix, + this, + std_cxx1x::_1, + std_cxx1x::_2, + std_cxx1x::_3), + std_cxx1x::bind (&BoussinesqFlowProblem:: + copy_local_to_global_temperature_matrix, + this, + std_cxx1x::_1), + Assembly::Scratch:: + TemperatureMatrix (temperature_fe, mapping, quadrature_formula), + Assembly::CopyData:: + TemperatureMatrix (temperature_fe)); temperature_mass_matrix.compress(); temperature_stiffness_matrix.compress(); @@ -3166,31 +3167,31 @@ namespace Step32 } - // @sect5{Temperature right hand side assembly} - - // This is the last assembly function. It - // calculates the right hand side of the - // temperature system, which includes the - // convection and the stabilization - // terms. It includes a lot of evaluations - // of old solutions at the quadrature - // points (which are necessary for - // calculating the artificial viscosity of - // stabilization), but is otherwise similar - // to the other assembly functions. Notice, - // once again, how we resolve the dilemma - // of having inhomogeneous boundary - // conditions, by just making a right hand - // side at this point (compare the comments - // for the project() function - // above): We create some matrix columns - // with exactly the values that would be - // entered for the temperature stiffness - // matrix, in case we have inhomogeneously - // constrained dofs. That will account for - // the correct balance of the right hand - // side vector with the matrix system of - // temperature. + // @sect5{Temperature right hand side assembly} + + // This is the last assembly function. It + // calculates the right hand side of the + // temperature system, which includes the + // convection and the stabilization + // terms. It includes a lot of evaluations + // of old solutions at the quadrature + // points (which are necessary for + // calculating the artificial viscosity of + // stabilization), but is otherwise similar + // to the other assembly functions. Notice, + // once again, how we resolve the dilemma + // of having inhomogeneous boundary + // conditions, by just making a right hand + // side at this point (compare the comments + // for the project() function + // above): We create some matrix columns + // with exactly the values that would be + // entered for the temperature stiffness + // matrix, in case we have inhomogeneously + // constrained dofs. That will account for + // the correct balance of the right hand + // side vector with the matrix system of + // temperature. template void BoussinesqFlowProblem:: local_assemble_temperature_rhs (const std::pair global_T_range, @@ -3214,10 +3215,10 @@ namespace Step32 scratch.temperature_fe_values.reinit (cell); typename DoFHandler::active_cell_iterator - stokes_cell (&triangulation, - cell->level(), - cell->index(), - &stokes_dof_handler); + stokes_cell (&triangulation, + cell->level(), + cell->index(), + &stokes_dof_handler); scratch.stokes_fe_values.reinit (stokes_cell); scratch.temperature_fe_values.get_function_values (old_temperature_solution, @@ -3240,9 +3241,9 @@ namespace Step32 scratch.stokes_fe_values[velocities].get_function_values (old_stokes_solution, scratch.old_old_velocity_values); scratch.stokes_fe_values[velocities].get_function_symmetric_gradients (stokes_solution, - scratch.old_strain_rates); + scratch.old_strain_rates); scratch.stokes_fe_values[velocities].get_function_symmetric_gradients (old_stokes_solution, - scratch.old_old_strain_rates); + scratch.old_old_strain_rates); const double nu = compute_viscosity (scratch.old_temperature_values, @@ -3375,38 +3376,38 @@ namespace Step32 - // In the function that runs the WorkStream - // for actually calculating the right hand - // side, we also generate the final - // matrix. As mentioned above, it is a sum - // of the mass matrix and the Laplace - // matrix, times some time step-dependent - // weight. This weight is specified by the - // BDF-2 time integration scheme, see the - // introduction in step-31. What is new in - // this tutorial program (in addition to - // the use of MPI parallelization and the - // WorkStream class), is that we now - // precompute the temperature - // preconditioner as well. The reason is - // that the setup of the Jacobi - // preconditioner takes a noticeable time - // compared to the solver because we - // usually only need between 10 and 20 - // iterations for solving the temperature - // system (this might sound strange, as - // Jacobi really only consists of a - // diagonal, but in Trilinos it is derived - // from more general framework for point - // relaxation preconditioners which is a - // bit inefficient). Hence, it is more - // efficient to precompute the - // preconditioner, even though the matrix - // entries may slightly change because the - // time step might change. This is not too - // big a problem because we remesh every - // few time steps (and regenerate the - // preconditioner then). + // In the function that runs the WorkStream + // for actually calculating the right hand + // side, we also generate the final + // matrix. As mentioned above, it is a sum + // of the mass matrix and the Laplace + // matrix, times some time step-dependent + // weight. This weight is specified by the + // BDF-2 time integration scheme, see the + // introduction in step-31. What is new in + // this tutorial program (in addition to + // the use of MPI parallelization and the + // WorkStream class), is that we now + // precompute the temperature + // preconditioner as well. The reason is + // that the setup of the Jacobi + // preconditioner takes a noticeable time + // compared to the solver because we + // usually only need between 10 and 20 + // iterations for solving the temperature + // system (this might sound strange, as + // Jacobi really only consists of a + // diagonal, but in Trilinos it is derived + // from more general framework for point + // relaxation preconditioners which is a + // bit inefficient). Hence, it is more + // efficient to precompute the + // preconditioner, even though the matrix + // entries may slightly change because the + // time step might change. This is not too + // big a problem because we remesh every + // few time steps (and regenerate the + // preconditioner then). template void BoussinesqFlowProblem::assemble_temperature_system (const double maximal_velocity) { @@ -3433,30 +3434,30 @@ namespace Step32 rebuild_temperature_preconditioner = false; } - // The next part is computing the right - // hand side vectors. To do so, we first - // compute the average temperature $T_m$ - // that we use for evaluating the - // artificial viscosity stabilization - // through the residual $E(T) = - // (T-T_m)^2$. We do this by defining the - // midpoint between maximum and minimum - // temperature as average temperature in - // the definition of the entropy - // viscosity. An alternative would be to - // use the integral average, but the - // results are not very sensitive to this - // choice. The rest then only requires - // calling WorkStream::run again, binding - // the arguments to the - // local_assemble_temperature_rhs - // function that are the same in every - // call to the correct values: + // The next part is computing the right + // hand side vectors. To do so, we first + // compute the average temperature $T_m$ + // that we use for evaluating the + // artificial viscosity stabilization + // through the residual $E(T) = + // (T-T_m)^2$. We do this by defining the + // midpoint between maximum and minimum + // temperature as average temperature in + // the definition of the entropy + // viscosity. An alternative would be to + // use the integral average, but the + // results are not very sensitive to this + // choice. The rest then only requires + // calling WorkStream::run again, binding + // the arguments to the + // local_assemble_temperature_rhs + // function that are the same in every + // call to the correct values: temperature_rhs = 0; const QGauss quadrature_formula(parameters.temperature_degree+2); const std::pair - global_T_range = get_extrapolated_temperature_range(); + global_T_range = get_extrapolated_temperature_range(); const double average_temperature = 0.5 * (global_T_range.first + global_T_range.second); @@ -3464,32 +3465,32 @@ namespace Step32 get_entropy_variation (average_temperature); typedef - FilteredIterator::active_cell_iterator> - CellFilter; + FilteredIterator::active_cell_iterator> + CellFilter; WorkStream:: - run (CellFilter (IteratorFilters::LocallyOwnedCell(), - temperature_dof_handler.begin_active()), - CellFilter (IteratorFilters::LocallyOwnedCell(), - temperature_dof_handler.end()), - std_cxx1x::bind (&BoussinesqFlowProblem:: - local_assemble_temperature_rhs, - this, - global_T_range, - maximal_velocity, - global_entropy_variation, - std_cxx1x::_1, - std_cxx1x::_2, - std_cxx1x::_3), - std_cxx1x::bind (&BoussinesqFlowProblem:: - copy_local_to_global_temperature_rhs, - this, - std_cxx1x::_1), - Assembly::Scratch:: - TemperatureRHS (temperature_fe, stokes_fe, mapping, - quadrature_formula), - Assembly::CopyData:: - TemperatureRHS (temperature_fe)); + run (CellFilter (IteratorFilters::LocallyOwnedCell(), + temperature_dof_handler.begin_active()), + CellFilter (IteratorFilters::LocallyOwnedCell(), + temperature_dof_handler.end()), + std_cxx1x::bind (&BoussinesqFlowProblem:: + local_assemble_temperature_rhs, + this, + global_T_range, + maximal_velocity, + global_entropy_variation, + std_cxx1x::_1, + std_cxx1x::_2, + std_cxx1x::_3), + std_cxx1x::bind (&BoussinesqFlowProblem:: + copy_local_to_global_temperature_rhs, + this, + std_cxx1x::_1), + Assembly::Scratch:: + TemperatureRHS (temperature_fe, stokes_fe, mapping, + quadrature_formula), + Assembly::CopyData:: + TemperatureRHS (temperature_fe)); temperature_rhs.compress(Add); } @@ -3497,89 +3498,89 @@ namespace Step32 - // @sect4{BoussinesqFlowProblem::solve} - - // This function solves the linear systems - // in each time step of the Boussinesq - // problem. First, we - // work on the Stokes system and then on - // the temperature system. In essence, it - // does the same things as the respective - // function in step-31. However, there are a few - // changes here. - // - // The first change is related to the way - // we store our solution: we keep the - // vectors with locally owned degrees of - // freedom plus ghost nodes on each MPI - // node. When we enter a solver which is - // supposed to perform matrix-vector - // products with a distributed matrix, this - // is not the appropriate form, - // though. There, we will want to have the - // solution vector to be distributed in the - // same way as the matrix, i.e. without any - // ghosts. So what we do first is to - // generate a distributed vector called - // distributed_stokes_solution - // and put only the locally owned dofs into - // that, which is neatly done by the - // operator= of the Trilinos - // vector. - // - // Next, we scale the pressure solution (or - // rather, the initial guess) for the - // solver so that it matches with the - // length scales in the matrices, as - // discussed in the introduction. We also - // immediately scale the pressure solution - // back to the correct units after the - // solution is completed. We also need to - // set the pressure values at hanging nodes - // to zero. This we also did in step-31 in - // order not to disturb the Schur - // complement by some vector entries that - // actually are irrelevant during the solve - // stage. As a difference to step-31, here - // we do it only for the locally owned - // pressure dofs. After solving for the - // Stokes solution, each processor copies - // the distributed solution back into the - // solution vector that also includes ghost - // elements. - // - // The third and most obvious change is - // that we have two variants for the Stokes - // solver: A fast solver that sometimes - // breaks down, and a robust solver that is - // slower. This is what we already - // discussed in the introduction. Here is - // how we realize it: First, we perform 30 - // iterations with the fast solver based on - // the simple preconditioner based on the - // AMG V-cycle instead of an approximate - // solve (this is indicated by the - // false argument to the - // LinearSolvers::BlockSchurPreconditioner - // object). If we converge, everything is - // fine. If we do not converge, the solver - // control object will throw an exception - // SolverControl::NoConvergence. Usually, - // this would abort the program because we - // don't catch them in our usual - // solve() functions. This is - // certainly not what we want to happen - // here. Rather, we want to switch to the - // strong solver and continue the solution - // process with whatever vector we got so - // far. Hence, we catch the exception with - // the C++ try/catch mechanism. We then - // simply go through the same solver - // sequence again in the catch - // clause, this time passing the @p true - // flag to the preconditioner for the - // strong solver, signaling an approximate - // CG solve. + // @sect4{BoussinesqFlowProblem::solve} + + // This function solves the linear systems + // in each time step of the Boussinesq + // problem. First, we + // work on the Stokes system and then on + // the temperature system. In essence, it + // does the same things as the respective + // function in step-31. However, there are a few + // changes here. + // + // The first change is related to the way + // we store our solution: we keep the + // vectors with locally owned degrees of + // freedom plus ghost nodes on each MPI + // node. When we enter a solver which is + // supposed to perform matrix-vector + // products with a distributed matrix, this + // is not the appropriate form, + // though. There, we will want to have the + // solution vector to be distributed in the + // same way as the matrix, i.e. without any + // ghosts. So what we do first is to + // generate a distributed vector called + // distributed_stokes_solution + // and put only the locally owned dofs into + // that, which is neatly done by the + // operator= of the Trilinos + // vector. + // + // Next, we scale the pressure solution (or + // rather, the initial guess) for the + // solver so that it matches with the + // length scales in the matrices, as + // discussed in the introduction. We also + // immediately scale the pressure solution + // back to the correct units after the + // solution is completed. We also need to + // set the pressure values at hanging nodes + // to zero. This we also did in step-31 in + // order not to disturb the Schur + // complement by some vector entries that + // actually are irrelevant during the solve + // stage. As a difference to step-31, here + // we do it only for the locally owned + // pressure dofs. After solving for the + // Stokes solution, each processor copies + // the distributed solution back into the + // solution vector that also includes ghost + // elements. + // + // The third and most obvious change is + // that we have two variants for the Stokes + // solver: A fast solver that sometimes + // breaks down, and a robust solver that is + // slower. This is what we already + // discussed in the introduction. Here is + // how we realize it: First, we perform 30 + // iterations with the fast solver based on + // the simple preconditioner based on the + // AMG V-cycle instead of an approximate + // solve (this is indicated by the + // false argument to the + // LinearSolvers::BlockSchurPreconditioner + // object). If we converge, everything is + // fine. If we do not converge, the solver + // control object will throw an exception + // SolverControl::NoConvergence. Usually, + // this would abort the program because we + // don't catch them in our usual + // solve() functions. This is + // certainly not what we want to happen + // here. Rather, we want to switch to the + // strong solver and continue the solution + // process with whatever vector we got so + // far. Hence, we catch the exception with + // the C++ try/catch mechanism. We then + // simply go through the same solver + // sequence again in the catch + // clause, this time passing the @p true + // flag to the preconditioner for the + // strong solver, signaling an approximate + // CG solve. template void BoussinesqFlowProblem::solve () { @@ -3589,16 +3590,16 @@ namespace Step32 pcout << " Solving Stokes system... " << std::flush; TrilinosWrappers::MPI::BlockVector - distributed_stokes_solution (stokes_rhs); + distributed_stokes_solution (stokes_rhs); distributed_stokes_solution = stokes_solution; distributed_stokes_solution.block(1) /= EquationData::pressure_scaling; const unsigned int - start = (distributed_stokes_solution.block(0).size() + - distributed_stokes_solution.block(1).local_range().first), - end = (distributed_stokes_solution.block(0).size() + - distributed_stokes_solution.block(1).local_range().second); + start = (distributed_stokes_solution.block(0).size() + + distributed_stokes_solution.block(1).local_range().first), + end = (distributed_stokes_solution.block(0).size() + + distributed_stokes_solution.block(1).local_range().second); for (unsigned int i=start; i - preconditioner (stokes_matrix, stokes_preconditioner_matrix, - *Mp_preconditioner, *Amg_preconditioner, - false); + TrilinosWrappers::PreconditionJacobi> + preconditioner (stokes_matrix, stokes_preconditioner_matrix, + *Mp_preconditioner, *Amg_preconditioner, + false); SolverFGMRES - solver(solver_control, mem, - SolverFGMRES:: - AdditionalData(30, true)); + solver(solver_control, mem, + SolverFGMRES:: + AdditionalData(30, true)); solver.solve(stokes_matrix, distributed_stokes_solution, stokes_rhs, preconditioner); @@ -3631,16 +3632,16 @@ namespace Step32 catch (SolverControl::NoConvergence) { const LinearSolvers::BlockSchurPreconditioner - preconditioner (stokes_matrix, stokes_preconditioner_matrix, - *Mp_preconditioner, *Amg_preconditioner, - true); + TrilinosWrappers::PreconditionJacobi> + preconditioner (stokes_matrix, stokes_preconditioner_matrix, + *Mp_preconditioner, *Amg_preconditioner, + true); SolverControl solver_control_refined (stokes_matrix.m(), solver_tolerance); SolverFGMRES - solver(solver_control_refined, mem, - SolverFGMRES:: - AdditionalData(50, true)); + solver(solver_control_refined, mem, + SolverFGMRES:: + AdditionalData(50, true)); solver.solve(stokes_matrix, distributed_stokes_solution, stokes_rhs, preconditioner); @@ -3660,39 +3661,39 @@ namespace Step32 computing_timer.exit_section(); - // Now let's turn to the temperature - // part: First, we compute the time step - // size. We found that we need smaller - // time steps for 3D than for 2D for the - // shell geometry. This is because the - // cells are more distorted in that case - // (it is the smallest edge length that - // determines the CFL number). Instead of - // computing the time step from maximum - // velocity and minimal mesh size as in - // step-31, we compute local CFL numbers, - // i.e., on each cell we compute the - // maximum velocity times the mesh size, - // and compute the maximum of - // them. Hence, we need to choose the - // factor in front of the time step - // slightly smaller. - // - // After temperature right hand side - // assembly, we solve the linear system - // for temperature (with fully - // distributed vectors without any - // ghosts), apply constraints and copy - // the vector back to one with ghosts. - // - // In the end, we extract the temperature - // range similarly to step-31 to produce - // some output (for example in order to - // help us choose the stabilization - // constants, as discussed in the - // introduction). The only difference is - // that we need to exchange maxima over - // all processors. + // Now let's turn to the temperature + // part: First, we compute the time step + // size. We found that we need smaller + // time steps for 3D than for 2D for the + // shell geometry. This is because the + // cells are more distorted in that case + // (it is the smallest edge length that + // determines the CFL number). Instead of + // computing the time step from maximum + // velocity and minimal mesh size as in + // step-31, we compute local CFL numbers, + // i.e., on each cell we compute the + // maximum velocity times the mesh size, + // and compute the maximum of + // them. Hence, we need to choose the + // factor in front of the time step + // slightly smaller. + // + // After temperature right hand side + // assembly, we solve the linear system + // for temperature (with fully + // distributed vectors without any + // ghosts), apply constraints and copy + // the vector back to one with ghosts. + // + // In the end, we extract the temperature + // range similarly to step-31 to produce + // some output (for example in order to + // help us choose the stabilization + // constants, as discussed in the + // introduction). The only difference is + // that we need to exchange maxima over + // all processors. computing_timer.enter_section (" Assemble temperature rhs"); { old_time_step = time_step; @@ -3724,7 +3725,7 @@ namespace Step32 SolverCG cg (solver_control); TrilinosWrappers::MPI::Vector - distributed_temperature_solution (temperature_rhs); + distributed_temperature_solution (temperature_rhs); distributed_temperature_solution = temperature_solution; cg.solve (temperature_matrix, distributed_temperature_solution, @@ -3739,7 +3740,8 @@ namespace Step32 computing_timer.exit_section(); double temperature[2] = { std::numeric_limits::max(), - -std::numeric_limits::max() }; + -std::numeric_limits::max() + }; double global_temperature[2]; for (unsigned int i=0; i class BoussinesqFlowProblem::Postprocessor : public DataPostprocessor { - public: - Postprocessor (const unsigned int partition, - const double minimal_pressure); + public: + Postprocessor (const unsigned int partition, + const double minimal_pressure); - virtual - void - compute_derived_quantities_vector (const std::vector > &uh, - const std::vector > > &duh, - const std::vector > > &dduh, - const std::vector > &normals, - const std::vector > &evaluation_points, - std::vector > &computed_quantities) const; + virtual + void + compute_derived_quantities_vector (const std::vector > &uh, + const std::vector > > &duh, + const std::vector > > &dduh, + const std::vector > &normals, + const std::vector > &evaluation_points, + std::vector > &computed_quantities) const; - virtual std::vector get_names () const; + virtual std::vector get_names () const; - virtual - std::vector - get_data_component_interpretation () const; + virtual + std::vector + get_data_component_interpretation () const; - virtual UpdateFlags get_needed_update_flags () const; + virtual UpdateFlags get_needed_update_flags () const; - private: - const unsigned int partition; - const double minimal_pressure; + private: + const unsigned int partition; + const double minimal_pressure; }; @@ -3818,23 +3820,23 @@ namespace Step32 BoussinesqFlowProblem::Postprocessor:: Postprocessor (const unsigned int partition, const double minimal_pressure) - : - partition (partition), - minimal_pressure (minimal_pressure) + : + partition (partition), + minimal_pressure (minimal_pressure) {} - // Here we define the names for the - // variables we want to output. These are - // the actual solution values for velocity, - // pressure, and temperature, as well as - // the friction heating and to each cell - // the number of the processor that owns - // it. This allows us to visualize the - // partitioning of the domain among the - // processors. Except for the velocity, - // which is vector-valued, all other - // quantities are scalar. + // Here we define the names for the + // variables we want to output. These are + // the actual solution values for velocity, + // pressure, and temperature, as well as + // the friction heating and to each cell + // the number of the processor that owns + // it. This allows us to visualize the + // partitioning of the domain among the + // processors. Except for the velocity, + // which is vector-valued, all other + // quantities are scalar. template std::vector BoussinesqFlowProblem::Postprocessor::get_names() const @@ -3855,8 +3857,8 @@ namespace Step32 get_data_component_interpretation () const { std::vector - interpretation (dim, - DataComponentInterpretation::component_is_part_of_vector); + interpretation (dim, + DataComponentInterpretation::component_is_part_of_vector); interpretation.push_back (DataComponentInterpretation::component_is_scalar); interpretation.push_back (DataComponentInterpretation::component_is_scalar); @@ -3875,26 +3877,26 @@ namespace Step32 } - // Now we implement the function that - // computes the derived quantities. As we - // also did for the output, we rescale the - // velocity from its SI units to something - // more readable, namely cm/year. Next, the - // pressure is scaled to be between 0 and - // the maximum pressure. This makes it more - // easily comparable -- in essence making - // all pressure variables positive or - // zero. Temperature is taken as is, and - // the friction heating is computed as $2 - // \eta \varepsilon(\mathbf{u}) \cdot - // \varepsilon(\mathbf{u})$. - // - // The quantities we output here are more - // for illustration, rather than for actual - // scientific value. We come back to this - // briefly in the results section of this - // program and explain what one may in fact - // be interested in. + // Now we implement the function that + // computes the derived quantities. As we + // also did for the output, we rescale the + // velocity from its SI units to something + // more readable, namely cm/year. Next, the + // pressure is scaled to be between 0 and + // the maximum pressure. This makes it more + // easily comparable -- in essence making + // all pressure variables positive or + // zero. Temperature is taken as is, and + // the friction heating is computed as $2 + // \eta \varepsilon(\mathbf{u}) \cdot + // \varepsilon(\mathbf{u})$. + // + // The quantities we output here are more + // for illustration, rather than for actual + // scientific value. We come back to this + // briefly in the results section of this + // program and explain what one may in fact + // be interested in. template void BoussinesqFlowProblem::Postprocessor:: @@ -3934,40 +3936,40 @@ namespace Step32 } - // The output_results() - // function does mostly what the - // corresponding one did in to step-31, in - // particular the merging data from the two - // DoFHandler objects (for the Stokes and - // the temperature parts of the problem) - // into one. There is one minor change: we - // make sure that each processor only works - // on the subdomain it owns locally (and - // not on ghost or artificial cells) when - // building the joint solution vector. The - // same will then have to be done in - // DataOut::build_patches(), but that - // function does so automatically. - // - // What we end up with is a set of patches - // that we can write using the functions in - // DataOutBase in a variety of output - // formats. Here, we then have to pay - // attention that what each processor - // writes is really only its own part of - // the domain, i.e. we will want to write - // each processor's contribution into a - // separate file. This we do by adding an - // additional number to the filename when - // we write the solution. This is not - // really new, we did it similarly in - // step-40. Note that we write in the - // compressed format @p .vtu instead of - // plain vtk files, which saves quite some - // storage. - // - // All the rest of the work is done in the - // PostProcessor class. + // The output_results() + // function does mostly what the + // corresponding one did in to step-31, in + // particular the merging data from the two + // DoFHandler objects (for the Stokes and + // the temperature parts of the problem) + // into one. There is one minor change: we + // make sure that each processor only works + // on the subdomain it owns locally (and + // not on ghost or artificial cells) when + // building the joint solution vector. The + // same will then have to be done in + // DataOut::build_patches(), but that + // function does so automatically. + // + // What we end up with is a set of patches + // that we can write using the functions in + // DataOutBase in a variety of output + // formats. Here, we then have to pay + // attention that what each processor + // writes is really only its own part of + // the domain, i.e. we will want to write + // each processor's contribution into a + // separate file. This we do by adding an + // additional number to the filename when + // we write the solution. This is not + // really new, we did it similarly in + // step-40. Note that we write in the + // compressed format @p .vtu instead of + // plain vtk files, which saves quite some + // storage. + // + // All the rest of the work is done in the + // PostProcessor class. template void BoussinesqFlowProblem::output_results () { @@ -3991,10 +3993,10 @@ namespace Step32 std::vector local_temperature_dof_indices (temperature_fe.dofs_per_cell); typename DoFHandler::active_cell_iterator - joint_cell = joint_dof_handler.begin_active(), - joint_endc = joint_dof_handler.end(), - stokes_cell = stokes_dof_handler.begin_active(), - temperature_cell = temperature_dof_handler.begin_active(); + joint_cell = joint_dof_handler.begin_active(), + joint_endc = joint_dof_handler.end(), + stokes_cell = stokes_dof_handler.begin_active(), + temperature_cell = temperature_dof_handler.begin_active(); for (; joint_cell!=joint_endc; ++joint_cell, ++stokes_cell, ++temperature_cell) if (joint_cell->is_locally_owned()) @@ -4056,19 +4058,19 @@ namespace Step32 data_out.write_vtu (output); - // At this point, all processors have - // written their own files to disk. We - // could visualize them individually in - // Visit or Paraview, but in reality we - // of course want to visualize the whole - // set of files at once. To this end, we - // create a master file in each of the - // formats understood by Visit - // (.visit) and Paraview - // (.pvtu) on the zeroth - // processor that describes how the - // individual files are defining the - // global data set. + // At this point, all processors have + // written their own files to disk. We + // could visualize them individually in + // Visit or Paraview, but in reality we + // of course want to visualize the whole + // set of files at once. To this end, we + // create a master file in each of the + // formats understood by Visit + // (.visit) and Paraview + // (.pvtu) on the zeroth + // processor that describes how the + // individual files are defining the + // global data set. if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) { std::vector filenames; @@ -4079,16 +4081,16 @@ namespace Step32 Utilities::int_to_string(i, 4) + ".vtu"); const std::string - pvtu_master_filename = ("solution-" + - Utilities::int_to_string (out_index, 5) + - ".pvtu"); + pvtu_master_filename = ("solution-" + + Utilities::int_to_string (out_index, 5) + + ".pvtu"); std::ofstream pvtu_master (pvtu_master_filename.c_str()); data_out.write_pvtu_record (pvtu_master, filenames); const std::string - visit_master_filename = ("solution-" + - Utilities::int_to_string (out_index, 5) + - ".visit"); + visit_master_filename = ("solution-" + + Utilities::int_to_string (out_index, 5) + + ".visit"); std::ofstream visit_master (visit_master_filename.c_str()); data_out.write_visit_record (visit_master, filenames); } @@ -4099,46 +4101,46 @@ namespace Step32 - // @sect4{BoussinesqFlowProblem::refine_mesh} - - // This function isn't really new - // either. Since the - // setup_dofs function that we - // call in the middle has its own timer - // section, we split timing this function - // into two sections. It will also allow us - // to easily identify which of the two is - // more expensive. - // - // One thing of note, however, is that we - // only want to compute error indicators on - // the locally owned subdomain. In order to - // achieve this, we pass one additional - // argument to the - // KellyErrorEstimator::estimate - // function. Note that the vector for error - // estimates is resized to the number of - // active cells present on the current - // process, which is less than the total - // number of active cells on all processors - // (but more than the number of locally - // owned active cells); each processor only - // has a few coarse cells around the - // locally owned ones, as also explained in - // step-40. - // - // The local error estimates are then - // handed to a %parallel version of - // GridRefinement (in namespace - // parallel::distributed::GridRefinement, - // see also step-40) which looks at the - // errors and finds the cells that need - // refinement by comparing the error values - // across processors. As in step-31, we - // want to limit the maximum grid level. So - // in case some cells have been marked that - // are already at the finest level, we - // simply clear the refine flags. + // @sect4{BoussinesqFlowProblem::refine_mesh} + + // This function isn't really new + // either. Since the + // setup_dofs function that we + // call in the middle has its own timer + // section, we split timing this function + // into two sections. It will also allow us + // to easily identify which of the two is + // more expensive. + // + // One thing of note, however, is that we + // only want to compute error indicators on + // the locally owned subdomain. In order to + // achieve this, we pass one additional + // argument to the + // KellyErrorEstimator::estimate + // function. Note that the vector for error + // estimates is resized to the number of + // active cells present on the current + // process, which is less than the total + // number of active cells on all processors + // (but more than the number of locally + // owned active cells); each processor only + // has a few coarse cells around the + // locally owned ones, as also explained in + // step-40. + // + // The local error estimates are then + // handed to a %parallel version of + // GridRefinement (in namespace + // parallel::distributed::GridRefinement, + // see also step-40) which looks at the + // errors and finds the cells that need + // refinement by comparing the error values + // across processors. As in step-31, we + // want to limit the maximum grid level. So + // in case some cells have been marked that + // are already at the finest level, we + // simply clear the refine flags. template void BoussinesqFlowProblem::refine_mesh (const unsigned int max_grid_level) { @@ -4156,30 +4158,30 @@ namespace Step32 triangulation.locally_owned_subdomain()); parallel::distributed::GridRefinement:: - refine_and_coarsen_fixed_fraction (triangulation, - estimated_error_per_cell, - 0.3, 0.1); + refine_and_coarsen_fixed_fraction (triangulation, + estimated_error_per_cell, + 0.3, 0.1); if (triangulation.n_levels() > max_grid_level) for (typename Triangulation::active_cell_iterator - cell = triangulation.begin_active(max_grid_level); + cell = triangulation.begin_active(max_grid_level); cell != triangulation.end(); ++cell) cell->clear_refine_flag (); - // With all flags marked as necessary, we - // set up the - // parallel::distributed::SolutionTransfer - // object to transfer the solutions for - // the current time level and the next - // older one. The syntax is similar to - // the non-%parallel solution transfer - // (with the exception that here a - // pointer to the vector entries is - // enough). The remainder of the function - // is concerned with setting up the data - // structures again after mesh refinement - // and restoring the solution vectors on - // the new mesh. + // With all flags marked as necessary, we + // set up the + // parallel::distributed::SolutionTransfer + // object to transfer the solutions for + // the current time level and the next + // older one. The syntax is similar to + // the non-%parallel solution transfer + // (with the exception that here a + // pointer to the vector entries is + // enough). The remainder of the function + // is concerned with setting up the data + // structures again after mesh refinement + // and restoring the solution vectors on + // the new mesh. std::vector x_temperature (2); x_temperature[0] = &temperature_solution; x_temperature[1] = &old_temperature_solution; @@ -4188,9 +4190,9 @@ namespace Step32 x_stokes[1] = &old_stokes_solution; parallel::distributed::SolutionTransfer - temperature_trans(temperature_dof_handler); + temperature_trans(temperature_dof_handler); parallel::distributed::SolutionTransfer - stokes_trans(stokes_dof_handler); + stokes_trans(stokes_dof_handler); triangulation.prepare_coarsening_and_refinement(); temperature_trans.prepare_for_coarsening_and_refinement(x_temperature); @@ -4234,19 +4236,19 @@ namespace Step32 - // @sect4{BoussinesqFlowProblem::run} + // @sect4{BoussinesqFlowProblem::run} - // This is the final and controlling - // function in this class. It, in fact, - // runs the entire rest of the program and - // is, once more, very similar to - // step-31. We use a different mesh now (a - // GridGenerator::hyper_shell instead of a - // simple cube geometry), and use the - // project_temperature_field() - // function instead of the library function - // VectorTools::project, the - // rest is as before. + // This is the final and controlling + // function in this class. It, in fact, + // runs the entire rest of the program and + // is, once more, very similar to + // step-31. We use a different mesh now (a + // GridGenerator::hyper_shell instead of a + // simple cube geometry), and use the + // project_temperature_field() + // function instead of the library function + // VectorTools::project, the + // rest is as before. template void BoussinesqFlowProblem::run () { @@ -4268,7 +4270,7 @@ namespace Step32 unsigned int pre_refinement_step = 0; - start_time_iteration: +start_time_iteration: project_temperature_field (); @@ -4311,27 +4313,27 @@ namespace Step32 (timestep_number % parameters.graphical_output_interval == 0)) output_results (); - // In order to speed up linear - // solvers, we extrapolate the - // solutions from the old time levels - // to the new one. This gives a very - // good initial guess, cutting the - // number of iterations needed in - // solvers by more than one half. We - // do not need to extrapolate in the - // last iteration, so if we reached - // the final time, we stop here. - // - // As the last thing during a - // time step (before actually - // bumping up the number of - // the time step), we check - // whether the current time - // step number is divisible - // by 100, and if so we let - // the computing timer print - // a summary of CPU times - // spent so far. + // In order to speed up linear + // solvers, we extrapolate the + // solutions from the old time levels + // to the new one. This gives a very + // good initial guess, cutting the + // number of iterations needed in + // solvers by more than one half. We + // do not need to extrapolate in the + // last iteration, so if we reached + // the final time, we stop here. + // + // As the last thing during a + // time step (before actually + // bumping up the number of + // the time step), we check + // whether the current time + // step number is divisible + // by 100, and if so we let + // the computing timer print + // a summary of CPU times + // spent so far. if (time > parameters.end_time * EquationData::year_in_seconds) break; @@ -4349,7 +4351,7 @@ namespace Step32 TrilinosWrappers::MPI::BlockVector distr_old_solution (stokes_rhs); distr_old_solution = old_old_stokes_solution; distr_solution .sadd (1.+time_step/old_time_step, -time_step/old_time_step, - distr_old_solution); + distr_old_solution); stokes_solution = distr_solution; } { @@ -4358,7 +4360,7 @@ namespace Step32 TrilinosWrappers::MPI::Vector distr_old_solution (temperature_rhs); distr_old_solution = old_old_temperature_solution; distr_solution .sadd (1.+time_step/old_time_step, -time_step/old_time_step, - distr_old_solution); + distr_old_solution); temperature_solution = distr_solution; } } @@ -4371,11 +4373,11 @@ namespace Step32 } while (true); - // If we are generating graphical - // output, do so also for the last - // time step unless we had just - // done so before we left the - // do-while loop + // If we are generating graphical + // output, do so also for the last + // time step unless we had just + // done so before we left the + // do-while loop if ((parameters.generate_graphical_output == true) && !((timestep_number-1) % parameters.graphical_output_interval == 0)) @@ -4385,25 +4387,25 @@ namespace Step32 - // @sect3{The main function} - - // The main function is short as usual and - // very similar to the one in step-31. Since - // we use a parameter file which is specified - // as an argument in the command line, we - // have to read it in here and pass it on to - // the Parameters class for parsing. If no - // filename is given in the command line, we - // simply use the \step-32.prm - // file which is distributed together with - // the program. - // - // Because 3d computations are simply - // very slow unless you throw a lot - // of processors at them, the program - // defaults to 2d. You can get the 3d - // version by changing the constant - // dimension below to 3. +// @sect3{The main function} + +// The main function is short as usual and +// very similar to the one in step-31. Since +// we use a parameter file which is specified +// as an argument in the command line, we +// have to read it in here and pass it on to +// the Parameters class for parsing. If no +// filename is given in the command line, we +// simply use the \step-32.prm +// file which is distributed together with +// the program. +// +// Because 3d computations are simply +// very slow unless you throw a lot +// of processors at them, the program +// defaults to 2d. You can get the 3d +// version by changing the constant +// dimension below to 3. int main (int argc, char *argv[]) { using namespace Step32; diff --git a/deal.II/examples/step-33/step-33.cc b/deal.II/examples/step-33/step-33.cc index dccad4abe0..1721b8b098 100644 --- a/deal.II/examples/step-33/step-33.cc +++ b/deal.II/examples/step-33/step-33.cc @@ -9,11 +9,11 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // First a standard set of deal.II - // includes. Nothing special to comment on - // here: +// First a standard set of deal.II +// includes. Nothing special to comment on +// here: #include #include #include @@ -45,688 +45,689 @@ #include #include - // Then, as mentioned in the introduction, we - // use various Trilinos packages as linear - // solvers as well as for automatic - // differentiation. These are in the - // following include files. - // - // Since deal.II provides interfaces to the - // basic Trilinos matrices, vectors, - // preconditioners and solvers, we include - // them similarly as deal.II linear algebra - // structures. +// Then, as mentioned in the introduction, we +// use various Trilinos packages as linear +// solvers as well as for automatic +// differentiation. These are in the +// following include files. +// +// Since deal.II provides interfaces to the +// basic Trilinos matrices, vectors, +// preconditioners and solvers, we include +// them similarly as deal.II linear algebra +// structures. #include #include #include #include - // Sacado is the automatic differentiation - // package within Trilinos, which is used - // to find the Jacobian for a fully - // implicit Newton iteration: +// Sacado is the automatic differentiation +// package within Trilinos, which is used +// to find the Jacobian for a fully +// implicit Newton iteration: #include - // And this again is C++: +// And this again is C++: #include #include #include #include - // To end this section, introduce everything - // in the dealii library into the namespace - // into which the contents of this program - // will go: +// To end this section, introduce everything +// in the dealii library into the namespace +// into which the contents of this program +// will go: namespace Step33 { using namespace dealii; - // @sect3{Euler equation specifics} - - // Here we define the flux function for this - // particular system of conservation laws, as - // well as pretty much everything else that's - // specific to the Euler equations for gas - // dynamics, for reasons discussed in the - // introduction. We group all this into a - // structure that defines everything that has - // to do with the flux. All members of this - // structure are static, i.e. the structure - // has no actual state specified by instance - // member variables. The better way to do - // this, rather than a structure with all - // static members would be to use a namespace - // -- but namespaces can't be templatized and - // we want some of the member variables of - // the structure to depend on the space - // dimension, which we in our usual way - // introduce using a template parameter. + // @sect3{Euler equation specifics} + + // Here we define the flux function for this + // particular system of conservation laws, as + // well as pretty much everything else that's + // specific to the Euler equations for gas + // dynamics, for reasons discussed in the + // introduction. We group all this into a + // structure that defines everything that has + // to do with the flux. All members of this + // structure are static, i.e. the structure + // has no actual state specified by instance + // member variables. The better way to do + // this, rather than a structure with all + // static members would be to use a namespace + // -- but namespaces can't be templatized and + // we want some of the member variables of + // the structure to depend on the space + // dimension, which we in our usual way + // introduce using a template parameter. template struct EulerEquations { - // @sect4{Component description} - - // First a few variables that - // describe the various components of our - // solution vector in a generic way. This - // includes the number of components in the - // system (Euler's equations have one entry - // for momenta in each spatial direction, - // plus the energy and density components, - // for a total of dim+2 - // components), as well as functions that - // describe the index within the solution - // vector of the first momentum component, - // the density component, and the energy - // density component. Note that all these - // %numbers depend on the space dimension; - // defining them in a generic way (rather - // than by implicit convention) makes our - // code more flexible and makes it easier - // to later extend it, for example by - // adding more components to the equations. - static const unsigned int n_components = dim + 2; - static const unsigned int first_momentum_component = 0; - static const unsigned int density_component = dim; - static const unsigned int energy_component = dim+1; - - // When generating graphical - // output way down in this - // program, we need to specify - // the names of the solution - // variables as well as how the - // various components group into - // vector and scalar fields. We - // could describe this there, but - // in order to keep things that - // have to do with the Euler - // equation localized here and - // the rest of the program as - // generic as possible, we - // provide this sort of - // information in the following - // two functions: - static - std::vector - component_names () - { - std::vector names (dim, "momentum"); - names.push_back ("density"); - names.push_back ("energy_density"); + // @sect4{Component description} + + // First a few variables that + // describe the various components of our + // solution vector in a generic way. This + // includes the number of components in the + // system (Euler's equations have one entry + // for momenta in each spatial direction, + // plus the energy and density components, + // for a total of dim+2 + // components), as well as functions that + // describe the index within the solution + // vector of the first momentum component, + // the density component, and the energy + // density component. Note that all these + // %numbers depend on the space dimension; + // defining them in a generic way (rather + // than by implicit convention) makes our + // code more flexible and makes it easier + // to later extend it, for example by + // adding more components to the equations. + static const unsigned int n_components = dim + 2; + static const unsigned int first_momentum_component = 0; + static const unsigned int density_component = dim; + static const unsigned int energy_component = dim+1; + + // When generating graphical + // output way down in this + // program, we need to specify + // the names of the solution + // variables as well as how the + // various components group into + // vector and scalar fields. We + // could describe this there, but + // in order to keep things that + // have to do with the Euler + // equation localized here and + // the rest of the program as + // generic as possible, we + // provide this sort of + // information in the following + // two functions: + static + std::vector + component_names () + { + std::vector names (dim, "momentum"); + names.push_back ("density"); + names.push_back ("energy_density"); - return names; - } + return names; + } - static + static + std::vector + component_interpretation () + { std::vector - component_interpretation () - { - std::vector - data_component_interpretation - (dim, DataComponentInterpretation::component_is_part_of_vector); - data_component_interpretation - .push_back (DataComponentInterpretation::component_is_scalar); - data_component_interpretation - .push_back (DataComponentInterpretation::component_is_scalar); - - return data_component_interpretation; - } + data_component_interpretation + (dim, DataComponentInterpretation::component_is_part_of_vector); + data_component_interpretation + .push_back (DataComponentInterpretation::component_is_scalar); + data_component_interpretation + .push_back (DataComponentInterpretation::component_is_scalar); + + return data_component_interpretation; + } - // @sect4{Transformations between variables} - - // Next, we define the gas - // constant. We will set it to 1.4 - // in its definition immediately - // following the declaration of - // this class (unlike integer - // variables, like the ones above, - // static const floating point - // member variables cannot be - // initialized within the class - // declaration in C++). This value - // of 1.4 is representative of a - // gas that consists of molecules - // composed of two atoms, such as - // air which consists up to small - // traces almost entirely of $N_2$ - // and $O_2$. - static const double gas_gamma; - - - // In the following, we will need to - // compute the kinetic energy and the - // pressure from a vector of conserved - // variables. This we can do based on the - // energy density and the kinetic energy - // $\frac 12 \rho |\mathbf v|^2 = - // \frac{|\rho \mathbf v|^2}{2\rho}$ - // (note that the independent variables - // contain the momentum components $\rho - // v_i$, not the velocities $v_i$). - // - // There is one slight problem: We will - // need to call the following functions - // with input arguments of type - // std::vector@ and - // Vector@. The - // problem is that the former has an - // access operator - // operator[] whereas the - // latter, for historical reasons, has - // operator(). We wouldn't - // be able to write the function in a - // generic way if we were to use one or - // the other of these. Fortunately, we - // can use the following trick: instead - // of writing v[i] or - // v(i), we can use - // *(v.begin() + i), i.e. we - // generate an iterator that points to - // the ith element, and then - // dereference it. This works for both - // kinds of vectors -- not the prettiest - // solution, but one that works. - template - static - number - compute_kinetic_energy (const InputVector &W) - { - number kinetic_energy = 0; - for (unsigned int d=0; dstd::vector@ and + // Vector@. The + // problem is that the former has an + // access operator + // operator[] whereas the + // latter, for historical reasons, has + // operator(). We wouldn't + // be able to write the function in a + // generic way if we were to use one or + // the other of these. Fortunately, we + // can use the following trick: instead + // of writing v[i] or + // v(i), we can use + // *(v.begin() + i), i.e. we + // generate an iterator that points to + // the ith element, and then + // dereference it. This works for both + // kinds of vectors -- not the prettiest + // solution, but one that works. + template + static + number + compute_kinetic_energy (const InputVector &W) + { + number kinetic_energy = 0; + for (unsigned int d=0; d - static - number - compute_pressure (const InputVector &W) - { - return ((gas_gamma-1.0) * - (*(W.begin() + energy_component) - - compute_kinetic_energy(W))); - } + template + static + number + compute_pressure (const InputVector &W) + { + return ((gas_gamma-1.0) * + (*(W.begin() + energy_component) - + compute_kinetic_energy(W))); + } - // @sect4{EulerEquations::compute_flux_matrix} - - // We define the flux function - // $F(W)$ as one large matrix. - // Each row of this matrix - // represents a scalar - // conservation law for the - // component in that row. The - // exact form of this matrix is - // given in the - // introduction. Note that we - // know the size of the matrix: - // it has as many rows as the - // system has components, and - // dim columns; - // rather than using a FullMatrix - // object for such a matrix - // (which has a variable number - // of rows and columns and must - // therefore allocate memory on - // the heap each time such a - // matrix is created), we use a - // rectangular array of numbers - // right away. - // - // We templatize the numerical type of - // the flux function so that we may use - // the automatic differentiation type - // here. Similarly, we will call the - // function with different input vector - // data types, so we templatize on it as - // well: - template - static - void compute_flux_matrix (const InputVector &W, - number (&flux)[n_components][dim]) + // @sect4{EulerEquations::compute_flux_matrix} + + // We define the flux function + // $F(W)$ as one large matrix. + // Each row of this matrix + // represents a scalar + // conservation law for the + // component in that row. The + // exact form of this matrix is + // given in the + // introduction. Note that we + // know the size of the matrix: + // it has as many rows as the + // system has components, and + // dim columns; + // rather than using a FullMatrix + // object for such a matrix + // (which has a variable number + // of rows and columns and must + // therefore allocate memory on + // the heap each time such a + // matrix is created), we use a + // rectangular array of numbers + // right away. + // + // We templatize the numerical type of + // the flux function so that we may use + // the automatic differentiation type + // here. Similarly, we will call the + // function with different input vector + // data types, so we templatize on it as + // well: + template + static + void compute_flux_matrix (const InputVector &W, + number (&flux)[n_components][dim]) + { + // First compute the pressure that + // appears in the flux matrix, and + // then compute the first + // dim columns of the + // matrix that correspond to the + // momentum terms: + const number pressure = compute_pressure (W); + + for (unsigned int d=0; ddim columns of the - // matrix that correspond to the - // momentum terms: - const number pressure = compute_pressure (W); - - for (unsigned int d=0; d - static - void numerical_normal_flux (const Point &normal, - const InputVector &Wplus, - const InputVector &Wminus, - const double alpha, - Sacado::Fad::DFad (&normal_flux)[n_components]) - { - Sacado::Fad::DFad iflux[n_components][dim]; - Sacado::Fad::DFad oflux[n_components][dim]; - - compute_flux_matrix (Wplus, iflux); - compute_flux_matrix (Wminus, oflux); - for (unsigned int di=0; di + static + void numerical_normal_flux (const Point &normal, + const InputVector &Wplus, + const InputVector &Wminus, + const double alpha, + Sacado::Fad::DFad (&normal_flux)[n_components]) + { + Sacado::Fad::DFad iflux[n_components][dim]; + Sacado::Fad::DFad oflux[n_components][dim]; - normal_flux[di] += 0.5*alpha*(Wplus[di] - Wminus[di]); - } - } + compute_flux_matrix (Wplus, iflux); + compute_flux_matrix (Wminus, oflux); - // @sect4{EulerEquations::compute_forcing_vector} - - // In the same way as describing the flux - // function $\mathbf F(\mathbf w)$, we - // also need to have a way to describe - // the right hand side forcing term. As - // mentioned in the introduction, we - // consider only gravity here, which - // leads to the specific form $\mathbf - // G(\mathbf w) = \left( - // g_1\rho, g_2\rho, g_3\rho, 0, - // \rho \mathbf g \cdot \mathbf v - // \right)^T$, shown here for - // the 3d case. More specifically, we - // will consider only $\mathbf - // g=(0,0,-1)^T$ in 3d, or $\mathbf - // g=(0,-1)^T$ in 2d. This naturally - // leads to the following function: - template - static - void compute_forcing_vector (const InputVector &W, - number (&forcing)[n_components]) + for (unsigned int di=0; di + static + void compute_forcing_vector (const InputVector &W, + number (&forcing)[n_components]) + { + const double gravity = -1.0; - // @sect4{Dealing with boundary conditions} + for (unsigned int c=0; cWminus will of course be - // modified, so it shouldn't be a - // const argument. Yet it is - // in the implementation below, and needs - // to be in order to allow the code to - // compile. The reason is that we call - // this function at a place where - // Wminus is of type - // Table@<2,Sacado::Fad::DFad@ - // @>, this being 2d table with - // indices representing the quadrature - // point and the vector component, - // respectively. We call this function - // with Wminus[q] as last - // argument; subscripting a 2d table - // yields a temporary accessor object - // representing a 1d vector, just what we - // want here. The problem is that a - // temporary accessor object can't be - // bound to a non-const reference - // argument of a function, as we would - // like here, according to the C++ 1998 - // and 2003 standards (something that - // will be fixed with the next standard - // in the form of rvalue references). We - // get away with making the output - // argument here a constant because it is - // the accessor object that's - // constant, not the table it points to: - // that one can still be written to. The - // hack is unpleasant nevertheless - // because it restricts the kind of data - // types that may be used as template - // argument to this function: a regular - // vector isn't going to do because that - // one can not be written to when marked - // const. With no good - // solution around at the moment, we'll - // go with the pragmatic, even if not - // pretty, solution shown here: - template - static - void - compute_Wminus (const BoundaryKind (&boundary_kind)[n_components], - const Point &normal_vector, - const DataVector &Wplus, - const Vector &boundary_values, - const DataVector &Wminus) - { - for (unsigned int c = 0; c < n_components; c++) - switch (boundary_kind[c]) - { - case inflow_boundary: - { - Wminus[c] = boundary_values(c); - break; - } + // Another thing we have to deal with is + // boundary conditions. To this end, let + // us first define the kinds of boundary + // conditions we currently know how to + // deal with: + enum BoundaryKind + { + inflow_boundary, + outflow_boundary, + no_penetration_boundary, + pressure_boundary + }; - case outflow_boundary: - { - Wminus[c] = Wplus[c]; - break; - } - // Prescribed pressure boundary - // conditions are a bit more - // complicated by the fact that - // even though the pressure is - // prescribed, we really are - // setting the energy component - // here, which will depend on - // velocity and pressure. So - // even though this seems like - // a Dirichlet type boundary - // condition, we get - // sensitivities of energy to - // velocity and density (unless - // these are also prescribed): - case pressure_boundary: - { - const typename DataVector::value_type - density = (boundary_kind[density_component] == - inflow_boundary - ? - boundary_values(density_component) - : - Wplus[density_component]); + // The next part is to actually decide + // what to do at each kind of + // boundary. To this end, remember from + // the introduction that boundary + // conditions are specified by choosing a + // value $\mathbf w^-$ on the outside of + // a boundary given an inhomogeneity + // $\mathbf j$ and possibly the + // solution's value $\mathbf w^+$ on the + // inside. Both are then passed to the + // numerical flux $\mathbf + // H(\mathbf{w}^+, \mathbf{w}^-, + // \mathbf{n})$ to define boundary + // contributions to the bilinear form. + // + // Boundary conditions can in some cases + // be specified for each component of the + // solution vector independently. For + // example, if component $c$ is marked + // for inflow, then $w^-_c = j_c$. If it + // is an outflow, then $w^-_c = + // w^+_c$. These two simple cases are + // handled first in the function below. + // + // There is a little snag that makes this + // function unpleasant from a C++ + // language viewpoint: The output vector + // Wminus will of course be + // modified, so it shouldn't be a + // const argument. Yet it is + // in the implementation below, and needs + // to be in order to allow the code to + // compile. The reason is that we call + // this function at a place where + // Wminus is of type + // Table@<2,Sacado::Fad::DFad@ + // @>, this being 2d table with + // indices representing the quadrature + // point and the vector component, + // respectively. We call this function + // with Wminus[q] as last + // argument; subscripting a 2d table + // yields a temporary accessor object + // representing a 1d vector, just what we + // want here. The problem is that a + // temporary accessor object can't be + // bound to a non-const reference + // argument of a function, as we would + // like here, according to the C++ 1998 + // and 2003 standards (something that + // will be fixed with the next standard + // in the form of rvalue references). We + // get away with making the output + // argument here a constant because it is + // the accessor object that's + // constant, not the table it points to: + // that one can still be written to. The + // hack is unpleasant nevertheless + // because it restricts the kind of data + // types that may be used as template + // argument to this function: a regular + // vector isn't going to do because that + // one can not be written to when marked + // const. With no good + // solution around at the moment, we'll + // go with the pragmatic, even if not + // pretty, solution shown here: + template + static + void + compute_Wminus (const BoundaryKind (&boundary_kind)[n_components], + const Point &normal_vector, + const DataVector &Wplus, + const Vector &boundary_values, + const DataVector &Wminus) + { + for (unsigned int c = 0; c < n_components; c++) + switch (boundary_kind[c]) + { + case inflow_boundary: + { + Wminus[c] = boundary_values(c); + break; + } - typename DataVector::value_type kinetic_energy = 0; - for (unsigned int d=0; d vdotn = 0; + for (unsigned int d = 0; d < dim; d++) + { + vdotn += Wplus[d]*normal_vector[d]; + } - case no_penetration_boundary: - { - // We prescribe the - // velocity (we are dealing with a - // particular component here so - // that the average of the - // velocities is orthogonal to the - // surface normal. This creates - // sensitivies of across the - // velocity components. - Sacado::Fad::DFad vdotn = 0; - for (unsigned int d = 0; d < dim; d++) { - vdotn += Wplus[d]*normal_vector[d]; - } - - Wminus[c] = Wplus[c] - 2.0*vdotn*normal_vector[c]; - break; - } + Wminus[c] = Wplus[c] - 2.0*vdotn*normal_vector[c]; + break; + } - default: - Assert (false, ExcNotImplemented()); - } - } + default: + Assert (false, ExcNotImplemented()); + } + } - // @sect4{EulerEquations::compute_refinement_indicators} - - // In this class, we also want to specify - // how to refine the mesh. The class - // ConservationLaw that will - // use all the information we provide - // here in the EulerEquation - // class is pretty agnostic about the - // particular conservation law it solves: - // as doesn't even really care how many - // components a solution vector - // has. Consequently, it can't know what - // a reasonable refinement indicator - // would be. On the other hand, here we - // do, or at least we can come up with a - // reasonable choice: we simply look at - // the gradient of the density, and - // compute - // $\eta_K=\log\left(1+|\nabla\rho(x_K)|\right)$, - // where $x_K$ is the center of cell $K$. - // - // There are certainly a number of - // equally reasonable refinement - // indicators, but this one does, and it - // is easy to compute: - static - void - compute_refinement_indicators (const DoFHandler &dof_handler, - const Mapping &mapping, - const Vector &solution, - Vector &refinement_indicators) - { - const unsigned int dofs_per_cell = dof_handler.get_fe().dofs_per_cell; - std::vector dofs (dofs_per_cell); + // @sect4{EulerEquations::compute_refinement_indicators} + + // In this class, we also want to specify + // how to refine the mesh. The class + // ConservationLaw that will + // use all the information we provide + // here in the EulerEquation + // class is pretty agnostic about the + // particular conservation law it solves: + // as doesn't even really care how many + // components a solution vector + // has. Consequently, it can't know what + // a reasonable refinement indicator + // would be. On the other hand, here we + // do, or at least we can come up with a + // reasonable choice: we simply look at + // the gradient of the density, and + // compute + // $\eta_K=\log\left(1+|\nabla\rho(x_K)|\right)$, + // where $x_K$ is the center of cell $K$. + // + // There are certainly a number of + // equally reasonable refinement + // indicators, but this one does, and it + // is easy to compute: + static + void + compute_refinement_indicators (const DoFHandler &dof_handler, + const Mapping &mapping, + const Vector &solution, + Vector &refinement_indicators) + { + const unsigned int dofs_per_cell = dof_handler.get_fe().dofs_per_cell; + std::vector dofs (dofs_per_cell); - const QMidpoint quadrature_formula; - const UpdateFlags update_flags = update_gradients; - FEValues fe_v (mapping, dof_handler.get_fe(), - quadrature_formula, update_flags); + const QMidpoint quadrature_formula; + const UpdateFlags update_flags = update_gradients; + FEValues fe_v (mapping, dof_handler.get_fe(), + quadrature_formula, update_flags); - std::vector > > - dU (1, std::vector >(n_components)); + std::vector > > + dU (1, std::vector >(n_components)); - typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); - for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no) - { - fe_v.reinit(cell); - fe_v.get_function_grads (solution, dU); + typename DoFHandler::active_cell_iterator + cell = dof_handler.begin_active(), + endc = dof_handler.end(); + for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no) + { + fe_v.reinit(cell); + fe_v.get_function_grads (solution, dU); - refinement_indicators(cell_no) - = std::log(1+ - std::sqrt(dU[0][density_component] * - dU[0][density_component])); - } + refinement_indicators(cell_no) + = std::log(1+ + std::sqrt(dU[0][density_component] * + dU[0][density_component])); } + } - // @sect4{EulerEquations::Postprocessor} - - // Finally, we declare a class that - // implements a postprocessing of data - // components. The problem this class - // solves is that the variables in the - // formulation of the Euler equations we - // use are in conservative rather than - // physical form: they are momentum - // densities $\mathbf m=\rho\mathbf v$, - // density $\rho$, and energy density - // $E$. What we would like to also put - // into our output file are velocities - // $\mathbf v=\frac{\mathbf m}{\rho}$ and - // pressure $p=(\gamma-1)(E-\frac{1}{2} - // \rho |\mathbf v|^2)$. - // - // In addition, we would like to add the - // possibility to generate schlieren - // plots. Schlieren plots are a way to - // visualize shocks and other sharp - // interfaces. The word "schlieren" is a - // German word that may be translated as - // "striae" -- it may be simpler to - // explain it by an example, however: - // schlieren is what you see when you, - // for example, pour highly concentrated - // alcohol, or a transparent saline - // solution, into water; the two have the - // same color, but they have different - // refractive indices and so before they - // are fully mixed light goes through the - // mixture along bent rays that lead to - // brightness variations if you look at - // it. That's "schlieren". A similar - // effect happens in compressible flow - // because the refractive index - // depends on the pressure (and therefore - // the density) of the gas. - // - // The origin of the word refers to - // two-dimensional projections of a - // three-dimensional volume (we see a 2d - // picture of the 3d fluid). In - // computational fluid dynamics, we can - // get an idea of this effect by - // considering what causes it: density - // variations. Schlieren plots are - // therefore produced by plotting - // $s=|\nabla \rho|^2$; obviously, $s$ is - // large in shocks and at other highly - // dynamic places. If so desired by the - // user (by specifying this in the input - // file), we would like to generate these - // schlieren plots in addition to the - // other derived quantities listed above. - // - // The implementation of the algorithms - // to compute derived quantities from the - // ones that solve our problem, and to - // output them into data file, rests on - // the DataPostprocessor class. It has - // extensive documentation, and other - // uses of the class can also be found in - // step-29. We therefore refrain from - // extensive comments. - class Postprocessor : public DataPostprocessor - { - public: - Postprocessor (const bool do_schlieren_plot); + // @sect4{EulerEquations::Postprocessor} + + // Finally, we declare a class that + // implements a postprocessing of data + // components. The problem this class + // solves is that the variables in the + // formulation of the Euler equations we + // use are in conservative rather than + // physical form: they are momentum + // densities $\mathbf m=\rho\mathbf v$, + // density $\rho$, and energy density + // $E$. What we would like to also put + // into our output file are velocities + // $\mathbf v=\frac{\mathbf m}{\rho}$ and + // pressure $p=(\gamma-1)(E-\frac{1}{2} + // \rho |\mathbf v|^2)$. + // + // In addition, we would like to add the + // possibility to generate schlieren + // plots. Schlieren plots are a way to + // visualize shocks and other sharp + // interfaces. The word "schlieren" is a + // German word that may be translated as + // "striae" -- it may be simpler to + // explain it by an example, however: + // schlieren is what you see when you, + // for example, pour highly concentrated + // alcohol, or a transparent saline + // solution, into water; the two have the + // same color, but they have different + // refractive indices and so before they + // are fully mixed light goes through the + // mixture along bent rays that lead to + // brightness variations if you look at + // it. That's "schlieren". A similar + // effect happens in compressible flow + // because the refractive index + // depends on the pressure (and therefore + // the density) of the gas. + // + // The origin of the word refers to + // two-dimensional projections of a + // three-dimensional volume (we see a 2d + // picture of the 3d fluid). In + // computational fluid dynamics, we can + // get an idea of this effect by + // considering what causes it: density + // variations. Schlieren plots are + // therefore produced by plotting + // $s=|\nabla \rho|^2$; obviously, $s$ is + // large in shocks and at other highly + // dynamic places. If so desired by the + // user (by specifying this in the input + // file), we would like to generate these + // schlieren plots in addition to the + // other derived quantities listed above. + // + // The implementation of the algorithms + // to compute derived quantities from the + // ones that solve our problem, and to + // output them into data file, rests on + // the DataPostprocessor class. It has + // extensive documentation, and other + // uses of the class can also be found in + // step-29. We therefore refrain from + // extensive comments. + class Postprocessor : public DataPostprocessor + { + public: + Postprocessor (const bool do_schlieren_plot); - virtual - void - compute_derived_quantities_vector (const std::vector > &uh, - const std::vector > > &duh, - const std::vector > > &dduh, - const std::vector > &normals, - const std::vector > &evaluation_points, - std::vector > &computed_quantities) const; + virtual + void + compute_derived_quantities_vector (const std::vector > &uh, + const std::vector > > &duh, + const std::vector > > &dduh, + const std::vector > &normals, + const std::vector > &evaluation_points, + std::vector > &computed_quantities) const; - virtual std::vector get_names () const; + virtual std::vector get_names () const; - virtual - std::vector - get_data_component_interpretation () const; + virtual + std::vector + get_data_component_interpretation () const; - virtual UpdateFlags get_needed_update_flags () const; + virtual UpdateFlags get_needed_update_flags () const; - private: - const bool do_schlieren_plot; - }; + private: + const bool do_schlieren_plot; + }; }; @@ -738,27 +739,27 @@ namespace Step33 template EulerEquations::Postprocessor:: Postprocessor (const bool do_schlieren_plot) - : - do_schlieren_plot (do_schlieren_plot) + : + do_schlieren_plot (do_schlieren_plot) {} - // This is the only function worth commenting - // on. When generating graphical output, the - // DataOut and related classes will call this - // function on each cell, with values, - // gradients, hessians, and normal vectors - // (in case we're working on faces) at each - // quadrature point. Note that the data at - // each quadrature point is itself - // vector-valued, namely the conserved - // variables. What we're going to do here is - // to compute the quantities we're interested - // in at each quadrature point. Note that for - // this we can ignore the hessians ("dduh") - // and normal vectors; to avoid compiler - // warnings about unused variables, we - // comment out their names. + // This is the only function worth commenting + // on. When generating graphical output, the + // DataOut and related classes will call this + // function on each cell, with values, + // gradients, hessians, and normal vectors + // (in case we're working on faces) at each + // quadrature point. Note that the data at + // each quadrature point is itself + // vector-valued, namely the conserved + // variables. What we're going to do here is + // to compute the quantities we're interested + // in at each quadrature point. Note that for + // this we can ignore the hessians ("dduh") + // and normal vectors; to avoid compiler + // warnings about unused variables, we + // comment out their names. template void EulerEquations::Postprocessor:: @@ -769,21 +770,21 @@ namespace Step33 const std::vector > &/*evaluation_points*/, std::vector > &computed_quantities) const { - // At the beginning of the function, let us - // make sure that all variables have the - // correct sizes, so that we can access - // individual vector elements without - // having to wonder whether we might read - // or write invalid elements; we also check - // that the duh vector only - // contains data if we really need it (the - // system knows about this because we say - // so in the - // get_needed_update_flags() - // function below). For the inner vectors, - // we check that at least the first element - // of the outer vector has the correct - // inner size: + // At the beginning of the function, let us + // make sure that all variables have the + // correct sizes, so that we can access + // individual vector elements without + // having to wonder whether we might read + // or write invalid elements; we also check + // that the duh vector only + // contains data if we really need it (the + // system knows about this because we say + // so in the + // get_needed_update_flags() + // function below). For the inner vectors, + // we check that at least the first element + // of the outer vector has the correct + // inner size: const unsigned int n_quadrature_points = uh.size(); if (do_schlieren_plot == true) @@ -804,18 +805,18 @@ namespace Step33 else Assert (computed_quantities[0].size() == dim+1, ExcInternalError()); - // Then loop over all quadrature points and - // do our work there. The code should be - // pretty self-explanatory. The order of - // output variables is first - // dim velocities, then the - // pressure, and if so desired the - // schlieren plot. Note that we try to be - // generic about the order of variables in - // the input vector, using the - // first_momentum_component - // and density_component - // information: + // Then loop over all quadrature points and + // do our work there. The code should be + // pretty self-explanatory. The order of + // output variables is first + // dim velocities, then the + // pressure, and if so desired the + // schlieren plot. Note that we try to be + // generic about the order of variables in + // the input vector, using the + // first_momentum_component + // and density_component + // information: for (unsigned int q=0; q - interpretation (dim, - DataComponentInterpretation::component_is_part_of_vector); + interpretation (dim, + DataComponentInterpretation::component_is_part_of_vector); interpretation.push_back (DataComponentInterpretation:: component_is_scalar); @@ -883,143 +884,143 @@ namespace Step33 } - // @sect3{Run time parameter handling} - - // Our next job is to define a few - // classes that will contain run-time - // parameters (for example solver - // tolerances, number of iterations, - // stabilization parameter, and the - // like). One could do this in the - // main class, but we separate it - // from that one to make the program - // more modular and easier to read: - // Everything that has to do with - // run-time parameters will be in the - // following namespace, whereas the - // program logic is in the main - // class. - // - // We will split the run-time - // parameters into a few separate - // structures, which we will all put - // into a namespace - // Parameters. Of these - // classes, there are a few that - // group the parameters for - // individual groups, such as for - // solvers, mesh refinement, or - // output. Each of these classes have - // functions - // declare_parameters() - // and - // parse_parameters() - // that declare parameter subsections - // and entries in a ParameterHandler - // object, and retrieve actual - // parameter values from such an - // object, respectively. These - // classes declare all their - // parameters in subsections of the - // ParameterHandler. - // - // The final class of the following - // namespace combines all the - // previous classes by deriving from - // them and taking care of a few more - // entries at the top level of the - // input file, as well as a few odd - // other entries in subsections that - // are too short to warrant a - // structure by themselves. - // - // It is worth pointing out one thing here: - // None of the classes below have a - // constructor that would initialize the - // various member variables. This isn't a - // problem, however, since we will read all - // variables declared in these classes from - // the input file (or indirectly: a - // ParameterHandler object will read it from - // there, and we will get the values from - // this object), and they will be initialized - // this way. In case a certain variable is - // not specified at all in the input file, - // this isn't a problem either: The - // ParameterHandler class will in this case - // simply take the default value that was - // specified when declaring an entry in the - // declare_parameters() - // functions of the classes below. + // @sect3{Run time parameter handling} + + // Our next job is to define a few + // classes that will contain run-time + // parameters (for example solver + // tolerances, number of iterations, + // stabilization parameter, and the + // like). One could do this in the + // main class, but we separate it + // from that one to make the program + // more modular and easier to read: + // Everything that has to do with + // run-time parameters will be in the + // following namespace, whereas the + // program logic is in the main + // class. + // + // We will split the run-time + // parameters into a few separate + // structures, which we will all put + // into a namespace + // Parameters. Of these + // classes, there are a few that + // group the parameters for + // individual groups, such as for + // solvers, mesh refinement, or + // output. Each of these classes have + // functions + // declare_parameters() + // and + // parse_parameters() + // that declare parameter subsections + // and entries in a ParameterHandler + // object, and retrieve actual + // parameter values from such an + // object, respectively. These + // classes declare all their + // parameters in subsections of the + // ParameterHandler. + // + // The final class of the following + // namespace combines all the + // previous classes by deriving from + // them and taking care of a few more + // entries at the top level of the + // input file, as well as a few odd + // other entries in subsections that + // are too short to warrant a + // structure by themselves. + // + // It is worth pointing out one thing here: + // None of the classes below have a + // constructor that would initialize the + // various member variables. This isn't a + // problem, however, since we will read all + // variables declared in these classes from + // the input file (or indirectly: a + // ParameterHandler object will read it from + // there, and we will get the values from + // this object), and they will be initialized + // this way. In case a certain variable is + // not specified at all in the input file, + // this isn't a problem either: The + // ParameterHandler class will in this case + // simply take the default value that was + // specified when declaring an entry in the + // declare_parameters() + // functions of the classes below. namespace Parameters { - // @sect4{Parameters::Solver} - // - // The first of these classes deals - // with parameters for the linear - // inner solver. It offers - // parameters that indicate which - // solver to use (GMRES as a solver - // for general non-symmetric - // indefinite systems, or a sparse - // direct solver), the amount of - // output to be produced, as well - // as various parameters that tweak - // the thresholded incomplete LU - // decomposition (ILUT) that we use - // as a preconditioner for GMRES. - // - // In particular, the ILUT takes - // the following parameters: - // - ilut_fill: the number of extra - // entries to add when forming the ILU - // decomposition - // - ilut_atol, ilut_rtol: When - // forming the preconditioner, for - // certain problems bad conditioning - // (or just bad luck) can cause the - // preconditioner to be very poorly - // conditioned. Hence it can help to - // add diagonal perturbations to the - // original matrix and form the - // preconditioner for this slightly - // better matrix. ATOL is an absolute - // perturbation that is added to the - // diagonal before forming the prec, - // and RTOL is a scaling factor $rtol - // \geq 1$. - // - ilut_drop: The ILUT will - // drop any values that - // have magnitude less than this value. - // This is a way to manage the amount - // of memory used by this - // preconditioner. - // - // The meaning of each parameter is - // also briefly described in the - // third argument of the - // ParameterHandler::declare_entry - // call in - // declare_parameters(). + // @sect4{Parameters::Solver} + // + // The first of these classes deals + // with parameters for the linear + // inner solver. It offers + // parameters that indicate which + // solver to use (GMRES as a solver + // for general non-symmetric + // indefinite systems, or a sparse + // direct solver), the amount of + // output to be produced, as well + // as various parameters that tweak + // the thresholded incomplete LU + // decomposition (ILUT) that we use + // as a preconditioner for GMRES. + // + // In particular, the ILUT takes + // the following parameters: + // - ilut_fill: the number of extra + // entries to add when forming the ILU + // decomposition + // - ilut_atol, ilut_rtol: When + // forming the preconditioner, for + // certain problems bad conditioning + // (or just bad luck) can cause the + // preconditioner to be very poorly + // conditioned. Hence it can help to + // add diagonal perturbations to the + // original matrix and form the + // preconditioner for this slightly + // better matrix. ATOL is an absolute + // perturbation that is added to the + // diagonal before forming the prec, + // and RTOL is a scaling factor $rtol + // \geq 1$. + // - ilut_drop: The ILUT will + // drop any values that + // have magnitude less than this value. + // This is a way to manage the amount + // of memory used by this + // preconditioner. + // + // The meaning of each parameter is + // also briefly described in the + // third argument of the + // ParameterHandler::declare_entry + // call in + // declare_parameters(). struct Solver { - enum SolverType { gmres, direct }; - SolverType solver; + enum SolverType { gmres, direct }; + SolverType solver; - enum OutputType { quiet, verbose }; - OutputType output; + enum OutputType { quiet, verbose }; + OutputType output; - double linear_residual; - int max_iterations; + double linear_residual; + int max_iterations; - double ilut_fill; - double ilut_atol; - double ilut_rtol; - double ilut_drop; + double ilut_fill; + double ilut_atol; + double ilut_rtol; + double ilut_drop; - static void declare_parameters (ParameterHandler &prm); - void parse_parameters (ParameterHandler &prm); + static void declare_parameters (ParameterHandler &prm); + void parse_parameters (ParameterHandler &prm); }; @@ -1089,22 +1090,22 @@ namespace Step33 - // @sect4{Parameters::Refinement} - // - // Similarly, here are a few parameters - // that determine how the mesh is to be - // refined (and if it is to be refined at - // all). For what exactly the shock - // parameters do, see the mesh refinement - // functions further down. + // @sect4{Parameters::Refinement} + // + // Similarly, here are a few parameters + // that determine how the mesh is to be + // refined (and if it is to be refined at + // all). For what exactly the shock + // parameters do, see the mesh refinement + // functions further down. struct Refinement { - bool do_refine; - double shock_val; - double shock_levels; + bool do_refine; + double shock_val; + double shock_levels; - static void declare_parameters (ParameterHandler &prm); - void parse_parameters (ParameterHandler &prm); + static void declare_parameters (ParameterHandler &prm); + void parse_parameters (ParameterHandler &prm); }; @@ -1150,34 +1151,34 @@ namespace Step33 - // @sect4{Parameters::Flux} - // - // Next a section on flux modifications to - // make it more stable. In particular, two - // options are offered to stabilize the - // Lax-Friedrichs flux: either choose - // $\mathbf{H}(\mathbf{a},\mathbf{b},\mathbf{n}) - // = - // \frac{1}{2}(\mathbf{F}(\mathbf{a})\cdot - // \mathbf{n} + \mathbf{F}(\mathbf{b})\cdot - // \mathbf{n} + \alpha (\mathbf{a} - - // \mathbf{b}))$ where $\alpha$ is either a - // fixed number specified in the input - // file, or where $\alpha$ is a mesh - // dependent value. In the latter case, it - // is chosen as $\frac{h}{2\delta T}$ with - // $h$ the diameter of the face to which - // the flux is applied, and $\delta T$ - // the current time step. + // @sect4{Parameters::Flux} + // + // Next a section on flux modifications to + // make it more stable. In particular, two + // options are offered to stabilize the + // Lax-Friedrichs flux: either choose + // $\mathbf{H}(\mathbf{a},\mathbf{b},\mathbf{n}) + // = + // \frac{1}{2}(\mathbf{F}(\mathbf{a})\cdot + // \mathbf{n} + \mathbf{F}(\mathbf{b})\cdot + // \mathbf{n} + \alpha (\mathbf{a} - + // \mathbf{b}))$ where $\alpha$ is either a + // fixed number specified in the input + // file, or where $\alpha$ is a mesh + // dependent value. In the latter case, it + // is chosen as $\frac{h}{2\delta T}$ with + // $h$ the diameter of the face to which + // the flux is applied, and $\delta T$ + // the current time step. struct Flux { - enum StabilizationKind { constant, mesh_dependent }; - StabilizationKind stabilization_kind; + enum StabilizationKind { constant, mesh_dependent }; + StabilizationKind stabilization_kind; - double stabilization_value; + double stabilization_value; - static void declare_parameters (ParameterHandler &prm); - void parse_parameters (ParameterHandler &prm); + static void declare_parameters (ParameterHandler &prm); + void parse_parameters (ParameterHandler &prm); }; @@ -1216,22 +1217,22 @@ namespace Step33 - // @sect4{Parameters::Output} - // - // Then a section on output parameters. We - // offer to produce Schlieren plots (the - // squared gradient of the density, a tool - // to visualize shock fronts), and a time - // interval between graphical output in - // case we don't want an output file every - // time step. + // @sect4{Parameters::Output} + // + // Then a section on output parameters. We + // offer to produce Schlieren plots (the + // squared gradient of the density, a tool + // to visualize shock fronts), and a time + // interval between graphical output in + // case we don't want an output file every + // time step. struct Output { - bool schlieren_plot; - double output_step; + bool schlieren_plot; + double output_step; - static void declare_parameters (ParameterHandler &prm); - void parse_parameters (ParameterHandler &prm); + static void declare_parameters (ParameterHandler &prm); + void parse_parameters (ParameterHandler &prm); }; @@ -1264,136 +1265,136 @@ namespace Step33 - // @sect4{Parameters::AllParameters} - // - // Finally the class that brings it all - // together. It declares a number of - // parameters itself, mostly ones at the - // top level of the parameter file as well - // as several in section too small to - // warrant their own classes. It also - // contains everything that is actually - // space dimension dependent, like initial - // or boundary conditions. - // - // Since this class is derived from all the - // ones above, the - // declare_parameters() and - // parse_parameters() - // functions call the respective functions - // of the base classes as well. - // - // Note that this class also handles the - // declaration of initial and boundary - // conditions specified in the input - // file. To this end, in both cases, - // there are entries like "w_0 value" - // which represent an expression in terms - // of $x,y,z$ that describe the initial - // or boundary condition as a formula - // that will later be parsed by the - // FunctionParser class. Similar - // expressions exist for "w_1", "w_2", - // etc, denoting the dim+2 - // conserved variables of the Euler - // system. Similarly, we allow up to - // max_n_boundaries boundary - // indicators to be used in the input - // file, and each of these boundary - // indicators can be associated with an - // inflow, outflow, or pressure boundary - // condition, with inhomogenous boundary - // conditions being specified for each - // component and each boundary indicator - // separately. - // - // The data structure used to store the - // boundary indicators is a bit - // complicated. It is an array of - // max_n_boundaries elements - // indicating the range of boundary - // indicators that will be accepted. For - // each entry in this array, we store a - // pair of data in the - // BoundaryCondition - // structure: first, an array of size - // n_components that for - // each component of the solution vector - // indicates whether it is an inflow, - // outflow, or other kind of boundary, - // and second a FunctionParser object - // that describes all components of the - // solution vector for this boundary id - // at once. - // - // The BoundaryCondition - // structure requires a constructor since - // we need to tell the function parser - // object at construction time how many - // vector components it is to - // describe. This initialization can - // therefore not wait till we actually - // set the formulas the FunctionParser - // object represents later in - // AllParameters::parse_parameters() - // - // For the same reason of having to tell - // Function objects their vector size at - // construction time, we have to have a - // constructor of the - // AllParameters class that - // at least initializes the other - // FunctionParser object, i.e. the one - // describing initial conditions. + // @sect4{Parameters::AllParameters} + // + // Finally the class that brings it all + // together. It declares a number of + // parameters itself, mostly ones at the + // top level of the parameter file as well + // as several in section too small to + // warrant their own classes. It also + // contains everything that is actually + // space dimension dependent, like initial + // or boundary conditions. + // + // Since this class is derived from all the + // ones above, the + // declare_parameters() and + // parse_parameters() + // functions call the respective functions + // of the base classes as well. + // + // Note that this class also handles the + // declaration of initial and boundary + // conditions specified in the input + // file. To this end, in both cases, + // there are entries like "w_0 value" + // which represent an expression in terms + // of $x,y,z$ that describe the initial + // or boundary condition as a formula + // that will later be parsed by the + // FunctionParser class. Similar + // expressions exist for "w_1", "w_2", + // etc, denoting the dim+2 + // conserved variables of the Euler + // system. Similarly, we allow up to + // max_n_boundaries boundary + // indicators to be used in the input + // file, and each of these boundary + // indicators can be associated with an + // inflow, outflow, or pressure boundary + // condition, with inhomogenous boundary + // conditions being specified for each + // component and each boundary indicator + // separately. + // + // The data structure used to store the + // boundary indicators is a bit + // complicated. It is an array of + // max_n_boundaries elements + // indicating the range of boundary + // indicators that will be accepted. For + // each entry in this array, we store a + // pair of data in the + // BoundaryCondition + // structure: first, an array of size + // n_components that for + // each component of the solution vector + // indicates whether it is an inflow, + // outflow, or other kind of boundary, + // and second a FunctionParser object + // that describes all components of the + // solution vector for this boundary id + // at once. + // + // The BoundaryCondition + // structure requires a constructor since + // we need to tell the function parser + // object at construction time how many + // vector components it is to + // describe. This initialization can + // therefore not wait till we actually + // set the formulas the FunctionParser + // object represents later in + // AllParameters::parse_parameters() + // + // For the same reason of having to tell + // Function objects their vector size at + // construction time, we have to have a + // constructor of the + // AllParameters class that + // at least initializes the other + // FunctionParser object, i.e. the one + // describing initial conditions. template struct AllParameters : public Solver, - public Refinement, - public Flux, - public Output + public Refinement, + public Flux, + public Output { - static const unsigned int max_n_boundaries = 10; + static const unsigned int max_n_boundaries = 10; - struct BoundaryConditions - { - typename EulerEquations::BoundaryKind - kind[EulerEquations::n_components]; + struct BoundaryConditions + { + typename EulerEquations::BoundaryKind + kind[EulerEquations::n_components]; - FunctionParser values; + FunctionParser values; - BoundaryConditions (); - }; + BoundaryConditions (); + }; - AllParameters (); + AllParameters (); - double diffusion_power; + double diffusion_power; - double time_step, final_time; - double theta; - bool is_stationary; + double time_step, final_time; + double theta; + bool is_stationary; - std::string mesh_filename; + std::string mesh_filename; - FunctionParser initial_conditions; - BoundaryConditions boundary_conditions[max_n_boundaries]; + FunctionParser initial_conditions; + BoundaryConditions boundary_conditions[max_n_boundaries]; - static void declare_parameters (ParameterHandler &prm); - void parse_parameters (ParameterHandler &prm); + static void declare_parameters (ParameterHandler &prm); + void parse_parameters (ParameterHandler &prm); }; template AllParameters::BoundaryConditions::BoundaryConditions () - : - values (EulerEquations::n_components) + : + values (EulerEquations::n_components) {} template AllParameters::AllParameters () - : - initial_conditions (EulerEquations::n_components) + : + initial_conditions (EulerEquations::n_components) {} @@ -1500,7 +1501,7 @@ namespace Step33 Utilities::int_to_string(boundary_id)); { std::vector - expressions(EulerEquations::n_components, "0.0"); + expressions(EulerEquations::n_components, "0.0"); const bool no_penetration = prm.get_bool("no penetration"); @@ -1529,9 +1530,9 @@ namespace Step33 } boundary_conditions[boundary_id].values - .initialize (FunctionParser::default_variable_names(), - expressions, - std::map()); + .initialize (FunctionParser::default_variable_names(), + expressions, + std::map()); } prm.leave_subsection(); } @@ -1559,165 +1560,165 @@ namespace Step33 - // @sect3{Conservation law class} - - // Here finally comes the class that - // actually does something with all - // the Euler equation and parameter - // specifics we've defined above. The - // public interface is pretty much - // the same as always (the - // constructor now takes the name of - // a file from which to read - // parameters, which is passed on the - // command line). The private - // function interface is also pretty - // similar to the usual arrangement, - // with the - // assemble_system - // function split into three parts: - // one that contains the main loop - // over all cells and that then calls - // the other two for integrals over - // cells and faces, respectively. + // @sect3{Conservation law class} + + // Here finally comes the class that + // actually does something with all + // the Euler equation and parameter + // specifics we've defined above. The + // public interface is pretty much + // the same as always (the + // constructor now takes the name of + // a file from which to read + // parameters, which is passed on the + // command line). The private + // function interface is also pretty + // similar to the usual arrangement, + // with the + // assemble_system + // function split into three parts: + // one that contains the main loop + // over all cells and that then calls + // the other two for integrals over + // cells and faces, respectively. template class ConservationLaw { - public: - ConservationLaw (const char *input_filename); - void run (); - - private: - void setup_system (); - - void assemble_system (); - void assemble_cell_term (const FEValues &fe_v, - const std::vector &dofs); - void assemble_face_term (const unsigned int face_no, - const FEFaceValuesBase &fe_v, - const FEFaceValuesBase &fe_v_neighbor, - const std::vector &dofs, - const std::vector &dofs_neighbor, - const bool external_face, - const unsigned int boundary_id, - const double face_diameter); - - std::pair solve (Vector &solution); - - void compute_refinement_indicators (Vector &indicator) const; - void refine_grid (const Vector &indicator); - - void output_results () const; - - - - // The first few member variables - // are also rather standard. Note - // that we define a mapping - // object to be used throughout - // the program when assembling - // terms (we will hand it to - // every FEValues and - // FEFaceValues object); the - // mapping we use is just the - // standard $Q_1$ mapping -- - // nothing fancy, in other words - // -- but declaring one here and - // using it throughout the - // program will make it simpler - // later on to change it if that - // should become necessary. This - // is, in fact, rather pertinent: - // it is known that for - // transsonic simulations with - // the Euler equations, - // computations do not converge - // even as $h\rightarrow 0$ if - // the boundary approximation is - // not of sufficiently high - // order. - Triangulation triangulation; - const MappingQ1 mapping; - - const FESystem fe; - DoFHandler dof_handler; - - const QGauss quadrature; - const QGauss face_quadrature; - - // Next come a number of data - // vectors that correspond to the - // solution of the previous time - // step - // (old_solution), - // the best guess of the current - // solution - // (current_solution; - // we say guess because - // the Newton iteration to - // compute it may not have - // converged yet, whereas - // old_solution - // refers to the fully converged - // final result of the previous - // time step), and a predictor - // for the solution at the next - // time step, computed by - // extrapolating the current and - // previous solution one time - // step into the future: - Vector old_solution; - Vector current_solution; - Vector predictor; - - Vector right_hand_side; - - // This final set of member variables - // (except for the object holding all - // run-time parameters at the very - // bottom and a screen output stream - // that only prints something if - // verbose output has been requested) - // deals with the inteface we have in - // this program to the Trilinos library - // that provides us with linear - // solvers. Similarly to including - // PETSc matrices in step-17, - // step-18, and step-19, all we - // need to do is to create a Trilinos - // sparse matrix instead of the - // standard deal.II class. The system - // matrix is used for the Jacobian in - // each Newton step. Since we do not - // intend to run this program in - // parallel (which wouldn't be too hard - // with Trilinos data structures, - // though), we don't have to think - // about anything else like - // distributing the degrees of freedom. - TrilinosWrappers::SparseMatrix system_matrix; - - Parameters::AllParameters parameters; - ConditionalOStream verbose_cout; + public: + ConservationLaw (const char *input_filename); + void run (); + + private: + void setup_system (); + + void assemble_system (); + void assemble_cell_term (const FEValues &fe_v, + const std::vector &dofs); + void assemble_face_term (const unsigned int face_no, + const FEFaceValuesBase &fe_v, + const FEFaceValuesBase &fe_v_neighbor, + const std::vector &dofs, + const std::vector &dofs_neighbor, + const bool external_face, + const unsigned int boundary_id, + const double face_diameter); + + std::pair solve (Vector &solution); + + void compute_refinement_indicators (Vector &indicator) const; + void refine_grid (const Vector &indicator); + + void output_results () const; + + + + // The first few member variables + // are also rather standard. Note + // that we define a mapping + // object to be used throughout + // the program when assembling + // terms (we will hand it to + // every FEValues and + // FEFaceValues object); the + // mapping we use is just the + // standard $Q_1$ mapping -- + // nothing fancy, in other words + // -- but declaring one here and + // using it throughout the + // program will make it simpler + // later on to change it if that + // should become necessary. This + // is, in fact, rather pertinent: + // it is known that for + // transsonic simulations with + // the Euler equations, + // computations do not converge + // even as $h\rightarrow 0$ if + // the boundary approximation is + // not of sufficiently high + // order. + Triangulation triangulation; + const MappingQ1 mapping; + + const FESystem fe; + DoFHandler dof_handler; + + const QGauss quadrature; + const QGauss face_quadrature; + + // Next come a number of data + // vectors that correspond to the + // solution of the previous time + // step + // (old_solution), + // the best guess of the current + // solution + // (current_solution; + // we say guess because + // the Newton iteration to + // compute it may not have + // converged yet, whereas + // old_solution + // refers to the fully converged + // final result of the previous + // time step), and a predictor + // for the solution at the next + // time step, computed by + // extrapolating the current and + // previous solution one time + // step into the future: + Vector old_solution; + Vector current_solution; + Vector predictor; + + Vector right_hand_side; + + // This final set of member variables + // (except for the object holding all + // run-time parameters at the very + // bottom and a screen output stream + // that only prints something if + // verbose output has been requested) + // deals with the inteface we have in + // this program to the Trilinos library + // that provides us with linear + // solvers. Similarly to including + // PETSc matrices in step-17, + // step-18, and step-19, all we + // need to do is to create a Trilinos + // sparse matrix instead of the + // standard deal.II class. The system + // matrix is used for the Jacobian in + // each Newton step. Since we do not + // intend to run this program in + // parallel (which wouldn't be too hard + // with Trilinos data structures, + // though), we don't have to think + // about anything else like + // distributing the degrees of freedom. + TrilinosWrappers::SparseMatrix system_matrix; + + Parameters::AllParameters parameters; + ConditionalOStream verbose_cout; }; - // @sect4{ConservationLaw::ConservationLaw} - // - // There is nothing much to say about - // the constructor. Essentially, it - // reads the input file and fills the - // parameter object with the parsed - // values: + // @sect4{ConservationLaw::ConservationLaw} + // + // There is nothing much to say about + // the constructor. Essentially, it + // reads the input file and fills the + // parameter object with the parsed + // values: template ConservationLaw::ConservationLaw (const char *input_filename) - : - mapping (), - fe (FE_Q(1), EulerEquations::n_components), - dof_handler (triangulation), - quadrature (2), - face_quadrature (2), - verbose_cout (std::cout, false) + : + mapping (), + fe (FE_Q(1), EulerEquations::n_components), + dof_handler (triangulation), + quadrature (2), + face_quadrature (2), + verbose_cout (std::cout, false) { ParameterHandler prm; Parameters::AllParameters::declare_parameters (prm); @@ -1730,14 +1731,14 @@ namespace Step33 - // @sect4{ConservationLaw::setup_system} - // - // The following (easy) function is called - // each time the mesh is changed. All it - // does is to resize the Trilinos matrix - // according to a sparsity pattern that we - // generate as in all the previous tutorial - // programs. + // @sect4{ConservationLaw::setup_system} + // + // The following (easy) function is called + // each time the mesh is changed. All it + // does is to resize the Trilinos matrix + // according to a sparsity pattern that we + // generate as in all the previous tutorial + // programs. template void ConservationLaw::setup_system () { @@ -1749,46 +1750,46 @@ namespace Step33 } - // @sect4{ConservationLaw::assemble_system} - // - // This and the following two - // functions are the meat of this - // program: They assemble the linear - // system that results from applying - // Newton's method to the nonlinear - // system of conservation - // equations. - // - // This first function puts all of - // the assembly pieces together in a - // routine that dispatches the - // correct piece for each cell/face. - // The actual implementation of the - // assembly on these objects is done - // in the following functions. - // - // At the top of the function we do the - // usual housekeeping: allocate FEValues, - // FEFaceValues, and FESubfaceValues - // objects necessary to do the integrations - // on cells, faces, and subfaces (in case - // of adjoining cells on different - // refinement levels). Note that we don't - // need all information (like values, - // gradients, or real locations of - // quadrature points) for all of these - // objects, so we only let the FEValues - // classes whatever is actually necessary - // by specifying the minimal set of - // UpdateFlags. For example, when using a - // FEFaceValues object for the neighboring - // cell we only need the shape values: - // Given a specific face, the quadrature - // points and JxW values are - // the same as for the current cells, and - // the normal vectors are known to be the - // negative of the normal vectors of the - // current cell. + // @sect4{ConservationLaw::assemble_system} + // + // This and the following two + // functions are the meat of this + // program: They assemble the linear + // system that results from applying + // Newton's method to the nonlinear + // system of conservation + // equations. + // + // This first function puts all of + // the assembly pieces together in a + // routine that dispatches the + // correct piece for each cell/face. + // The actual implementation of the + // assembly on these objects is done + // in the following functions. + // + // At the top of the function we do the + // usual housekeeping: allocate FEValues, + // FEFaceValues, and FESubfaceValues + // objects necessary to do the integrations + // on cells, faces, and subfaces (in case + // of adjoining cells on different + // refinement levels). Note that we don't + // need all information (like values, + // gradients, or real locations of + // quadrature points) for all of these + // objects, so we only let the FEValues + // classes whatever is actually necessary + // by specifying the minimal set of + // UpdateFlags. For example, when using a + // FEFaceValues object for the neighboring + // cell we only need the shape values: + // Given a specific face, the quadrature + // points and JxW values are + // the same as for the current cells, and + // the normal vectors are known to be the + // negative of the normal vectors of the + // current cell. template void ConservationLaw::assemble_system () { @@ -1801,11 +1802,11 @@ namespace Step33 | update_gradients | update_q_points | update_JxW_values, - face_update_flags = update_values - | update_q_points - | update_JxW_values - | update_normal_vectors, - neighbor_face_update_flags = update_values; + face_update_flags = update_values + | update_q_points + | update_JxW_values + | update_normal_vectors, + neighbor_face_update_flags = update_values; FEValues fe_v (mapping, fe, quadrature, update_flags); @@ -1818,13 +1819,13 @@ namespace Step33 FESubfaceValues fe_v_subface_neighbor (mapping, fe, face_quadrature, neighbor_face_update_flags); - // Then loop over all cells, initialize the - // FEValues object for the current cell and - // call the function that assembles the - // problem on this cell. + // Then loop over all cells, initialize the + // FEValues object for the current cell and + // call the function that assembles the + // problem on this cell. typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { fe_v.reinit (cell); @@ -1832,19 +1833,19 @@ namespace Step33 assemble_cell_term(fe_v, dof_indices); - // Then loop over all the faces of this - // cell. If a face is part of the - // external boundary, then assemble - // boundary conditions there (the fifth - // argument to - // assemble_face_terms - // indicates whether we are working on - // an external or internal face; if it - // is an external face, the fourth - // argument denoting the degrees of - // freedom indices of the neighbor is - // ignored, so we pass an empty - // vector): + // Then loop over all the faces of this + // cell. If a face is part of the + // external boundary, then assemble + // boundary conditions there (the fifth + // argument to + // assemble_face_terms + // indicates whether we are working on + // an external or internal face; if it + // is an external face, the fourth + // argument denoting the degrees of + // freedom indices of the neighbor is + // ignored, so we pass an empty + // vector): for (unsigned int face_no=0; face_no::faces_per_cell; ++face_no) if (cell->at_boundary(face_no)) @@ -1859,69 +1860,69 @@ namespace Step33 cell->face(face_no)->diameter()); } - // The alternative is that we are - // dealing with an internal face. There - // are two cases that we need to - // distinguish: that this is a normal - // face between two cells at the same - // refinement level, and that it is a - // face between two cells of the - // different refinement levels. - // - // In the first case, there is nothing - // we need to do: we are using a - // continuous finite element, and face - // terms do not appear in the bilinear - // form in this case. The second case - // usually does not lead to face terms - // either if we enforce hanging node - // constraints strongly (as in all - // previous tutorial programs so far - // whenever we used continuous finite - // elements -- this enforcement is done - // by the ConstraintMatrix class - // together with - // DoFTools::make_hanging_node_constraints). In - // the current program, however, we opt - // to enforce continuity weakly at - // faces between cells of different - // refinement level, for two reasons: - // (i) because we can, and more - // importantly (ii) because we would - // have to thread the automatic - // differentiation we use to compute - // the elements of the Newton matrix - // from the residual through the - // operations of the ConstraintMatrix - // class. This would be possible, but - // is not trivial, and so we choose - // this alternative approach. - // - // What needs to be decided is which - // side of an interface between two - // cells of different refinement level - // we are sitting on. - // - // Let's take the case where the - // neighbor is more refined first. We - // then have to loop over the children - // of the face of the current cell and - // integrate on each of them. We - // sprinkle a couple of assertions into - // the code to ensure that our - // reasoning trying to figure out which - // of the neighbor's children's faces - // coincides with a given subface of - // the current cell's faces is correct - // -- a bit of defensive programming - // never hurts. - // - // We then call the function that - // integrates over faces; since this is - // an internal face, the fifth argument - // is false, and the sixth one is - // ignored so we pass an invalid value - // again: + // The alternative is that we are + // dealing with an internal face. There + // are two cases that we need to + // distinguish: that this is a normal + // face between two cells at the same + // refinement level, and that it is a + // face between two cells of the + // different refinement levels. + // + // In the first case, there is nothing + // we need to do: we are using a + // continuous finite element, and face + // terms do not appear in the bilinear + // form in this case. The second case + // usually does not lead to face terms + // either if we enforce hanging node + // constraints strongly (as in all + // previous tutorial programs so far + // whenever we used continuous finite + // elements -- this enforcement is done + // by the ConstraintMatrix class + // together with + // DoFTools::make_hanging_node_constraints). In + // the current program, however, we opt + // to enforce continuity weakly at + // faces between cells of different + // refinement level, for two reasons: + // (i) because we can, and more + // importantly (ii) because we would + // have to thread the automatic + // differentiation we use to compute + // the elements of the Newton matrix + // from the residual through the + // operations of the ConstraintMatrix + // class. This would be possible, but + // is not trivial, and so we choose + // this alternative approach. + // + // What needs to be decided is which + // side of an interface between two + // cells of different refinement level + // we are sitting on. + // + // Let's take the case where the + // neighbor is more refined first. We + // then have to loop over the children + // of the face of the current cell and + // integrate on each of them. We + // sprinkle a couple of assertions into + // the code to ensure that our + // reasoning trying to figure out which + // of the neighbor's children's faces + // coincides with a given subface of + // the current cell's faces is correct + // -- a bit of defensive programming + // never hurts. + // + // We then call the function that + // integrates over faces; since this is + // an internal face, the fifth argument + // is false, and the sixth one is + // ignored so we pass an invalid value + // again: else { if (cell->neighbor(face_no)->has_children()) @@ -1934,7 +1935,7 @@ namespace Step33 ++subface_no) { const typename DoFHandler::active_cell_iterator - neighbor_child + neighbor_child = cell->neighbor_child_on_subface (face_no, subface_no); Assert (neighbor_child->face(neighbor2) == @@ -1958,29 +1959,29 @@ namespace Step33 } } - // The other possibility we have - // to care for is if the neighbor - // is coarser than the current - // cell (in particular, because - // of the usual restriction of - // only one hanging node per - // face, the neighbor must be - // exactly one level coarser than - // the current cell, something - // that we check with an - // assertion). Again, we then - // integrate over this interface: + // The other possibility we have + // to care for is if the neighbor + // is coarser than the current + // cell (in particular, because + // of the usual restriction of + // only one hanging node per + // face, the neighbor must be + // exactly one level coarser than + // the current cell, something + // that we check with an + // assertion). Again, we then + // integrate over this interface: else if (cell->neighbor(face_no)->level() != cell->level()) { const typename DoFHandler::cell_iterator - neighbor = cell->neighbor(face_no); + neighbor = cell->neighbor(face_no); Assert(neighbor->level() == cell->level()-1, ExcInternalError()); neighbor->get_dof_indices (dof_indices_neighbor); const std::pair - faceno_subfaceno = cell->neighbor_of_coarser_neighbor(face_no); + faceno_subfaceno = cell->neighbor_of_coarser_neighbor(face_no); const unsigned int neighbor_face_no = faceno_subfaceno.first, neighbor_subface_no = faceno_subfaceno.second; @@ -2005,101 +2006,101 @@ namespace Step33 } } - // After all this assembling, notify the - // Trilinos matrix object that the matrix - // is done: + // After all this assembling, notify the + // Trilinos matrix object that the matrix + // is done: system_matrix.compress(); } - // @sect4{ConservationLaw::assemble_cell_term} - // - // This function assembles the cell term by - // computing the cell part of the residual, - // adding its negative to the right hand side - // vector, and adding its derivative with - // respect to the local variables to the - // Jacobian (i.e. the Newton matrix). Recall - // that the cell contributions to the - // residual read $F_i = - // \left(\frac{\mathbf{w}_{n+1} - - // \mathbf{w}_n}{\delta - // t},\mathbf{z}_i\right)_K - - // \left(\mathbf{F}(\tilde{\mathbf{w}}), - // \nabla\mathbf{z}_i\right)_K + - // h^{\eta}(\nabla \mathbf{w} , \nabla - // \mathbf{z}_i)_K - - // (\mathbf{G}(\tilde{\mathbf w}), - // \mathbf{z}_i)_K$ where $\tilde{\mathbf w}$ - // is represented by the variable - // W_theta, $\mathbf{z}_i$ is - // the $i$th test function, and the scalar - // product - // $\left(\mathbf{F}(\tilde{\mathbf{w}}), - // \nabla\mathbf{z}\right)_K$ is understood - // as $\int_K - // \sum_{c=1}^{\text{n\_components}} - // \sum_{d=1}^{\text{dim}} - // \mathbf{F}(\tilde{\mathbf{w}})_{cd} - // \frac{\partial z_c}{x_d}$. - // - // At the top of this function, we do the - // usual housekeeping in terms of allocating - // some local variables that we will need - // later. In particular, we will allocate - // variables that will hold the values of the - // current solution $W_{n+1}^k$ after the - // $k$th Newton iteration (variable - // W), the previous time step's - // solution $W_{n}$ (variable - // W_old), as well as the linear - // combination $\theta W_{n+1}^k + - // (1-\theta)W_n$ that results from choosing - // different time stepping schemes (variable - // W_theta). - // - // In addition to these, we need the - // gradients of the current variables. It is - // a bit of a shame that we have to compute - // these; we almost don't. The nice thing - // about a simple conservation law is that - // the flux doesn't generally involve any - // gradients. We do need these, however, for - // the diffusion stabilization. - // - // The actual format in which we store these - // variables requires some - // explanation. First, we need values at each - // quadrature point for each of the - // EulerEquations::n_components - // components of the solution vector. This - // makes for a two-dimensional table for - // which we use deal.II's Table class (this - // is more efficient than - // std::vector@ - // @> because it only needs to - // allocate memory once, rather than once for - // each element of the outer - // vector). Similarly, the gradient is a - // three-dimensional table, which the Table - // class also supports. - // - // Secondly, we want to use automatic - // differentiation. To this end, we use the - // Sacado::Fad::DFad template for everything - // that is a computed from the variables with - // respect to which we would like to compute - // derivatives. This includes the current - // solution and gradient at the quadrature - // points (which are linear combinations of - // the degrees of freedom) as well as - // everything that is computed from them such - // as the residual, but not the previous time - // step's solution. These variables are all - // found in the first part of the function, - // along with a variable that we will use to - // store the derivatives of a single - // component of the residual: + // @sect4{ConservationLaw::assemble_cell_term} + // + // This function assembles the cell term by + // computing the cell part of the residual, + // adding its negative to the right hand side + // vector, and adding its derivative with + // respect to the local variables to the + // Jacobian (i.e. the Newton matrix). Recall + // that the cell contributions to the + // residual read $F_i = + // \left(\frac{\mathbf{w}_{n+1} - + // \mathbf{w}_n}{\delta + // t},\mathbf{z}_i\right)_K - + // \left(\mathbf{F}(\tilde{\mathbf{w}}), + // \nabla\mathbf{z}_i\right)_K + + // h^{\eta}(\nabla \mathbf{w} , \nabla + // \mathbf{z}_i)_K - + // (\mathbf{G}(\tilde{\mathbf w}), + // \mathbf{z}_i)_K$ where $\tilde{\mathbf w}$ + // is represented by the variable + // W_theta, $\mathbf{z}_i$ is + // the $i$th test function, and the scalar + // product + // $\left(\mathbf{F}(\tilde{\mathbf{w}}), + // \nabla\mathbf{z}\right)_K$ is understood + // as $\int_K + // \sum_{c=1}^{\text{n\_components}} + // \sum_{d=1}^{\text{dim}} + // \mathbf{F}(\tilde{\mathbf{w}})_{cd} + // \frac{\partial z_c}{x_d}$. + // + // At the top of this function, we do the + // usual housekeeping in terms of allocating + // some local variables that we will need + // later. In particular, we will allocate + // variables that will hold the values of the + // current solution $W_{n+1}^k$ after the + // $k$th Newton iteration (variable + // W), the previous time step's + // solution $W_{n}$ (variable + // W_old), as well as the linear + // combination $\theta W_{n+1}^k + + // (1-\theta)W_n$ that results from choosing + // different time stepping schemes (variable + // W_theta). + // + // In addition to these, we need the + // gradients of the current variables. It is + // a bit of a shame that we have to compute + // these; we almost don't. The nice thing + // about a simple conservation law is that + // the flux doesn't generally involve any + // gradients. We do need these, however, for + // the diffusion stabilization. + // + // The actual format in which we store these + // variables requires some + // explanation. First, we need values at each + // quadrature point for each of the + // EulerEquations::n_components + // components of the solution vector. This + // makes for a two-dimensional table for + // which we use deal.II's Table class (this + // is more efficient than + // std::vector@ + // @> because it only needs to + // allocate memory once, rather than once for + // each element of the outer + // vector). Similarly, the gradient is a + // three-dimensional table, which the Table + // class also supports. + // + // Secondly, we want to use automatic + // differentiation. To this end, we use the + // Sacado::Fad::DFad template for everything + // that is a computed from the variables with + // respect to which we would like to compute + // derivatives. This includes the current + // solution and gradient at the quadrature + // points (which are linear combinations of + // the degrees of freedom) as well as + // everything that is computed from them such + // as the residual, but not the previous time + // step's solution. These variables are all + // found in the first part of the function, + // along with a variable that we will use to + // store the derivatives of a single + // component of the residual: template void ConservationLaw:: @@ -2110,79 +2111,79 @@ namespace Step33 const unsigned int n_q_points = fe_v.n_quadrature_points; Table<2,Sacado::Fad::DFad > - W (n_q_points, EulerEquations::n_components); + W (n_q_points, EulerEquations::n_components); Table<2,double> - W_old (n_q_points, EulerEquations::n_components); + W_old (n_q_points, EulerEquations::n_components); Table<2,Sacado::Fad::DFad > - W_theta (n_q_points, EulerEquations::n_components); + W_theta (n_q_points, EulerEquations::n_components); Table<3,Sacado::Fad::DFad > - grad_W (n_q_points, EulerEquations::n_components, dim); + grad_W (n_q_points, EulerEquations::n_components, dim); std::vector residual_derivatives (dofs_per_cell); - // Next, we have to define the independent - // variables that we will try to determine - // by solving a Newton step. These - // independent variables are the values of - // the local degrees of freedom which we - // extract here: + // Next, we have to define the independent + // variables that we will try to determine + // by solving a Newton step. These + // independent variables are the values of + // the local degrees of freedom which we + // extract here: std::vector > independent_local_dof_values(dofs_per_cell); for (unsigned int i=0; iindependent_local_dof_values[i] - // as the $i$th independent variable out of - // a total of dofs_per_cell: + // The next step incorporates all the + // magic: we declare a subset of the + // autodifferentiation variables as + // independent degrees of freedom, whereas + // all the other ones remain dependent + // functions. These are precisely the local + // degrees of freedom just extracted. All + // calculations that reference them (either + // directly or indirectly) will accumulate + // sensitivies with respect to these + // variables. + // + // In order to mark the variables as + // independent, the following does the + // trick, marking + // independent_local_dof_values[i] + // as the $i$th independent variable out of + // a total of dofs_per_cell: for (unsigned int i=0; iW, - // W_old, - // W_theta, and - // grad_W, which we can - // compute from the local DoF values by - // using the formula $W(x_q)=\sum_i \mathbf - // W_i \Phi_i(x_q)$, where $\mathbf W_i$ is - // the $i$th entry of the (local part of - // the) solution vector, and $\Phi_i(x_q)$ - // the value of the $i$th vector-valued - // shape function evaluated at quadrature - // point $x_q$. The gradient can be - // computed in a similar way. - // - // Ideally, we could compute this - // information using a call into something - // like FEValues::get_function_values and - // FEValues::get_function_grads, but since - // (i) we would have to extend the FEValues - // class for this, and (ii) we don't want - // to make the entire - // old_solution vector fad - // types, only the local cell variables, we - // explicitly code the loop above. Before - // this, we add another loop that - // initializes all the fad variables to - // zero: + // After all these declarations, let us + // actually compute something. First, the + // values of W, + // W_old, + // W_theta, and + // grad_W, which we can + // compute from the local DoF values by + // using the formula $W(x_q)=\sum_i \mathbf + // W_i \Phi_i(x_q)$, where $\mathbf W_i$ is + // the $i$th entry of the (local part of + // the) solution vector, and $\Phi_i(x_q)$ + // the value of the $i$th vector-valued + // shape function evaluated at quadrature + // point $x_q$. The gradient can be + // computed in a similar way. + // + // Ideally, we could compute this + // information using a call into something + // like FEValues::get_function_values and + // FEValues::get_function_grads, but since + // (i) we would have to extend the FEValues + // class for this, and (ii) we don't want + // to make the entire + // old_solution vector fad + // types, only the local cell variables, we + // explicitly code the loop above. Before + // this, we add another loop that + // initializes all the fad variables to + // zero: for (unsigned int q=0; q::n_components; ++c) { @@ -2215,17 +2216,17 @@ namespace Step33 } - // Next, in order to compute the cell - // contributions, we need to evaluate - // $F(\tilde{\mathbf w})$ and - // $G(\tilde{\mathbf w})$ at all quadrature - // points. To store these, we also need to - // allocate a bit of memory. Note that we - // compute the flux matrices and right hand - // sides in terms of autodifferentiation - // variables, so that the Jacobian - // contributions can later easily be - // computed from it: + // Next, in order to compute the cell + // contributions, we need to evaluate + // $F(\tilde{\mathbf w})$ and + // $G(\tilde{\mathbf w})$ at all quadrature + // points. To store these, we also need to + // allocate a bit of memory. Note that we + // compute the flux matrices and right hand + // sides in terms of autodifferentiation + // variables, so that the Jacobian + // contributions can later easily be + // computed from it: typedef Sacado::Fad::DFad FluxMatrix[EulerEquations::n_components][dim]; FluxMatrix *flux = new FluxMatrix[n_q_points]; @@ -2239,63 +2240,63 @@ namespace Step33 } - // We now have all of the pieces in place, - // so perform the assembly. We have an - // outer loop through the components of the - // system, and an inner loop over the - // quadrature points, where we accumulate - // contributions to the $i$th residual - // $F_i$. The general formula for this - // residual is given in the introduction - // and at the top of this function. We can, - // however, simplify it a bit taking into - // account that the $i$th (vector-valued) - // test function $\mathbf{z}_i$ has in - // reality only a single nonzero component - // (more on this topic can be found in the - // @ref vector_valued module). It will be - // represented by the variable - // component_i below. With - // this, the residual term can be - // re-written as $F_i = - // \left(\frac{(\mathbf{w}_{n+1} - - // \mathbf{w}_n)_{\text{component\_i}}}{\delta - // t},(\mathbf{z}_i)_{\text{component\_i}}\right)_K$ - // $- \sum_{d=1}^{\text{dim}} - // \left(\mathbf{F} - // (\tilde{\mathbf{w}})_{\text{component\_i},d}, - // \frac{\partial(\mathbf{z}_i)_{\text{component\_i}}} - // {\partial x_d}\right)_K$ $+ - // \sum_{d=1}^{\text{dim}} h^{\eta} - // \left(\frac{\partial - // \mathbf{w}_{\text{component\_i}}}{\partial - // x_d} , \frac{\partial - // (\mathbf{z}_i)_{\text{component\_i}}}{\partial - // x_d} \right)_K$ - // $-(\mathbf{G}(\tilde{\mathbf{w}} - // )_{\text{component\_i}}, - // (\mathbf{z}_i)_{\text{component\_i}})_K$, - // where integrals are understood to be - // evaluated through summation over - // quadrature points. - // - // We initialy sum all contributions of the - // residual in the positive sense, so that - // we don't need to negative the Jacobian - // entries. Then, when we sum into the - // right_hand_side vector, - // we negate this residual. + // We now have all of the pieces in place, + // so perform the assembly. We have an + // outer loop through the components of the + // system, and an inner loop over the + // quadrature points, where we accumulate + // contributions to the $i$th residual + // $F_i$. The general formula for this + // residual is given in the introduction + // and at the top of this function. We can, + // however, simplify it a bit taking into + // account that the $i$th (vector-valued) + // test function $\mathbf{z}_i$ has in + // reality only a single nonzero component + // (more on this topic can be found in the + // @ref vector_valued module). It will be + // represented by the variable + // component_i below. With + // this, the residual term can be + // re-written as $F_i = + // \left(\frac{(\mathbf{w}_{n+1} - + // \mathbf{w}_n)_{\text{component\_i}}}{\delta + // t},(\mathbf{z}_i)_{\text{component\_i}}\right)_K$ + // $- \sum_{d=1}^{\text{dim}} + // \left(\mathbf{F} + // (\tilde{\mathbf{w}})_{\text{component\_i},d}, + // \frac{\partial(\mathbf{z}_i)_{\text{component\_i}}} + // {\partial x_d}\right)_K$ $+ + // \sum_{d=1}^{\text{dim}} h^{\eta} + // \left(\frac{\partial + // \mathbf{w}_{\text{component\_i}}}{\partial + // x_d} , \frac{\partial + // (\mathbf{z}_i)_{\text{component\_i}}}{\partial + // x_d} \right)_K$ + // $-(\mathbf{G}(\tilde{\mathbf{w}} + // )_{\text{component\_i}}, + // (\mathbf{z}_i)_{\text{component\_i}})_K$, + // where integrals are understood to be + // evaluated through summation over + // quadrature points. + // + // We initialy sum all contributions of the + // residual in the positive sense, so that + // we don't need to negative the Jacobian + // entries. Then, when we sum into the + // right_hand_side vector, + // we negate this residual. for (unsigned int i=0; i F_i = 0; const unsigned int - component_i = fe_v.get_fe().system_to_component_index(i).first; + component_i = fe_v.get_fe().system_to_component_index(i).first; - // The residual for each row (i) will be accumulating - // into this fad variable. At the end of the assembly - // for this row, we will query for the sensitivities - // to this variable and add them into the Jacobian. + // The residual for each row (i) will be accumulating + // into this fad variable. At the end of the assembly + // for this row, we will query for the sensitivities + // to this variable and add them into the Jacobian. for (unsigned int point=0; pointF_i.fastAccessDx(k), - // so we store the data in a - // temporary array. This information - // about the whole row of local dofs - // is then added to the Trilinos - // matrix at once (which supports the - // data types we have chosen). + // At the end of the loop, we have to + // add the sensitivities to the + // matrix and subtract the residual + // from the right hand side. Trilinos + // FAD data type gives us access to + // the derivatives using + // F_i.fastAccessDx(k), + // so we store the data in a + // temporary array. This information + // about the whole row of local dofs + // is then added to the Trilinos + // matrix at once (which supports the + // data types we have chosen). for (unsigned int k=0; k void ConservationLaw::assemble_face_term(const unsigned int face_no, @@ -2372,10 +2373,10 @@ namespace Step33 const unsigned int dofs_per_cell = fe_v.dofs_per_cell; std::vector > - independent_local_dof_values (dofs_per_cell), - independent_neighbor_dof_values (external_face == false ? - dofs_per_cell : - 0); + independent_local_dof_values (dofs_per_cell), + independent_neighbor_dof_values (external_face == false ? + dofs_per_cell : + 0); const unsigned int n_independent_variables = (external_face == false ? 2 * dofs_per_cell : @@ -2393,23 +2394,23 @@ namespace Step33 independent_neighbor_dof_values[i] = current_solution(dof_indices_neighbor[i]); independent_neighbor_dof_values[i] - .diff(i+dofs_per_cell, n_independent_variables); + .diff(i+dofs_per_cell, n_independent_variables); } - // Next, we need to define the values of - // the conservative variables $\tilde - // {\mathbf W}$ on this side of the face - // ($\tilde {\mathbf W}^+$) and on the - // opposite side ($\tilde {\mathbf - // W}^-$). The former can be computed in - // exactly the same way as in the previous - // function, but note that the - // fe_v variable now is of - // type FEFaceValues or FESubfaceValues: + // Next, we need to define the values of + // the conservative variables $\tilde + // {\mathbf W}$ on this side of the face + // ($\tilde {\mathbf W}^+$) and on the + // opposite side ($\tilde {\mathbf + // W}^-$). The former can be computed in + // exactly the same way as in the previous + // function, but note that the + // fe_v variable now is of + // type FEFaceValues or FESubfaceValues: Table<2,Sacado::Fad::DFad > - Wplus (n_q_points, EulerEquations::n_components), - Wminus (n_q_points, EulerEquations::n_components); + Wplus (n_q_points, EulerEquations::n_components), + Wminus (n_q_points, EulerEquations::n_components); for (unsigned int q=0; q::max_n_boundaries, @@ -2476,10 +2477,10 @@ namespace Step33 Parameters::AllParameters::max_n_boundaries)); std::vector > - boundary_values(n_q_points, Vector(EulerEquations::n_components)); + boundary_values(n_q_points, Vector(EulerEquations::n_components)); parameters.boundary_conditions[boundary_id] - .values.vector_value_list(fe_v.get_quadrature_points(), - boundary_values); + .values.vector_value_list(fe_v.get_quadrature_points(), + boundary_values); for (unsigned int q = 0; q < n_q_points; q++) EulerEquations::compute_Wminus (parameters.boundary_conditions[boundary_id].kind, @@ -2490,30 +2491,30 @@ namespace Step33 } - // Now that we have $\mathbf w^+$ and - // $\mathbf w^-$, we can go about computing - // the numerical flux function $\mathbf - // H(\mathbf w^+,\mathbf w^-, \mathbf n)$ - // for each quadrature point. Before - // calling the function that does so, we - // also need to determine the - // Lax-Friedrich's stability parameter: + // Now that we have $\mathbf w^+$ and + // $\mathbf w^-$, we can go about computing + // the numerical flux function $\mathbf + // H(\mathbf w^+,\mathbf w^-, \mathbf n)$ + // for each quadrature point. Before + // calling the function that does so, we + // also need to determine the + // Lax-Friedrich's stability parameter: typedef Sacado::Fad::DFad NormalFlux[EulerEquations::n_components]; NormalFlux *normal_fluxes = new NormalFlux[n_q_points]; double alpha; - switch(parameters.stabilization_kind) + switch (parameters.stabilization_kind) { - case Parameters::Flux::constant: - alpha = parameters.stabilization_value; - break; - case Parameters::Flux::mesh_dependent: - alpha = face_diameter/(2.0*parameters.time_step); - break; - default: - Assert (false, ExcNotImplemented()); - alpha = 1; + case Parameters::Flux::constant: + alpha = parameters.stabilization_value; + break; + case Parameters::Flux::mesh_dependent: + alpha = face_diameter/(2.0*parameters.time_step); + break; + default: + Assert (false, ExcNotImplemented()); + alpha = 1; } for (unsigned int q=0; q residual_derivatives (dofs_per_cell); for (unsigned int i=0; i std::pair @@ -2582,126 +2583,126 @@ namespace Step33 { switch (parameters.solver) { - // If the parameter file specified - // that a direct solver shall be - // used, then we'll get here. The - // process is straightforward, since - // deal.II provides a wrapper class - // to the Amesos direct solver within - // Trilinos. All we have to do is to - // create a solver control object - // (which is just a dummy object - // here, since we won't perform any - // iterations), and then create the - // direct solver object. When - // actually doing the solve, note - // that we don't pass a - // preconditioner. That wouldn't make - // much sense for a direct solver - // anyway. At the end we return the - // solver control statistics — - // which will tell that no iterations - // have been performed and that the - // final linear residual is zero, - // absent any better information that - // may be provided here: - case Parameters::Solver::direct: - { - SolverControl solver_control (1,0); - TrilinosWrappers::SolverDirect direct (solver_control, - parameters.output == - Parameters::Solver::verbose); + // If the parameter file specified + // that a direct solver shall be + // used, then we'll get here. The + // process is straightforward, since + // deal.II provides a wrapper class + // to the Amesos direct solver within + // Trilinos. All we have to do is to + // create a solver control object + // (which is just a dummy object + // here, since we won't perform any + // iterations), and then create the + // direct solver object. When + // actually doing the solve, note + // that we don't pass a + // preconditioner. That wouldn't make + // much sense for a direct solver + // anyway. At the end we return the + // solver control statistics — + // which will tell that no iterations + // have been performed and that the + // final linear residual is zero, + // absent any better information that + // may be provided here: + case Parameters::Solver::direct: + { + SolverControl solver_control (1,0); + TrilinosWrappers::SolverDirect direct (solver_control, + parameters.output == + Parameters::Solver::verbose); - direct.solve (system_matrix, newton_update, right_hand_side); + direct.solve (system_matrix, newton_update, right_hand_side); - return std::pair (solver_control.last_step(), - solver_control.last_value()); - } + return std::pair (solver_control.last_step(), + solver_control.last_value()); + } - // Likewise, if we are to use an - // iterative solver, we use Aztec's - // GMRES solver. We could use the - // Trilinos wrapper classes for - // iterative solvers and - // preconditioners here as well, but - // we choose to use an Aztec solver - // directly. For the given problem, - // Aztec's internal preconditioner - // implementations are superior over - // the ones deal.II has wrapper - // classes to, so we use ILU-T - // preconditioning within the AztecOO - // solver and set a bunch of options - // that can be changed from the - // parameter file. - // - // There are two more practicalities: - // Since we have built our right hand - // side and solution vector as - // deal.II Vector objects (as opposed - // to the matrix, which is a Trilinos - // object), we must hand the solvers - // Trilinos Epetra vectors. Luckily, - // they support the concept of a - // 'view', so we just send in a - // pointer to our deal.II vectors. We - // have to provide an Epetra_Map for - // the vector that sets the parallel - // distribution, which is just a - // dummy object in serial. The - // easiest way is to ask the matrix - // for its map, and we're going to be - // ready for matrix-vector products - // with it. - // - // Secondly, the Aztec solver wants - // us to pass a Trilinos - // Epetra_CrsMatrix in, not the - // deal.II wrapper class itself. So - // we access to the actual Trilinos - // matrix in the Trilinos wrapper - // class by the command - // trilinos_matrix(). Trilinos wants - // the matrix to be non-constant, so - // we have to manually remove the - // constantness using a const_cast. - case Parameters::Solver::gmres: - { - Epetra_Vector x(View, system_matrix.domain_partitioner(), - newton_update.begin()); - Epetra_Vector b(View, system_matrix.range_partitioner(), - right_hand_side.begin()); - - AztecOO solver; - solver.SetAztecOption(AZ_output, - (parameters.output == - Parameters::Solver::quiet - ? - AZ_none - : - AZ_all)); - solver.SetAztecOption(AZ_solver, AZ_gmres); - solver.SetRHS(&b); - solver.SetLHS(&x); - - solver.SetAztecOption(AZ_precond, AZ_dom_decomp); - solver.SetAztecOption(AZ_subdomain_solve, AZ_ilut); - solver.SetAztecOption(AZ_overlap, 0); - solver.SetAztecOption(AZ_reorder, 0); - - solver.SetAztecParam(AZ_drop, parameters.ilut_drop); - solver.SetAztecParam(AZ_ilut_fill, parameters.ilut_fill); - solver.SetAztecParam(AZ_athresh, parameters.ilut_atol); - solver.SetAztecParam(AZ_rthresh, parameters.ilut_rtol); - - solver.SetUserMatrix(const_cast - (&system_matrix.trilinos_matrix())); - - solver.Iterate(parameters.max_iterations, parameters.linear_residual); - - return std::pair (solver.NumIters(), - solver.TrueResidual()); - } + // Likewise, if we are to use an + // iterative solver, we use Aztec's + // GMRES solver. We could use the + // Trilinos wrapper classes for + // iterative solvers and + // preconditioners here as well, but + // we choose to use an Aztec solver + // directly. For the given problem, + // Aztec's internal preconditioner + // implementations are superior over + // the ones deal.II has wrapper + // classes to, so we use ILU-T + // preconditioning within the AztecOO + // solver and set a bunch of options + // that can be changed from the + // parameter file. + // + // There are two more practicalities: + // Since we have built our right hand + // side and solution vector as + // deal.II Vector objects (as opposed + // to the matrix, which is a Trilinos + // object), we must hand the solvers + // Trilinos Epetra vectors. Luckily, + // they support the concept of a + // 'view', so we just send in a + // pointer to our deal.II vectors. We + // have to provide an Epetra_Map for + // the vector that sets the parallel + // distribution, which is just a + // dummy object in serial. The + // easiest way is to ask the matrix + // for its map, and we're going to be + // ready for matrix-vector products + // with it. + // + // Secondly, the Aztec solver wants + // us to pass a Trilinos + // Epetra_CrsMatrix in, not the + // deal.II wrapper class itself. So + // we access to the actual Trilinos + // matrix in the Trilinos wrapper + // class by the command + // trilinos_matrix(). Trilinos wants + // the matrix to be non-constant, so + // we have to manually remove the + // constantness using a const_cast. + case Parameters::Solver::gmres: + { + Epetra_Vector x(View, system_matrix.domain_partitioner(), + newton_update.begin()); + Epetra_Vector b(View, system_matrix.range_partitioner(), + right_hand_side.begin()); + + AztecOO solver; + solver.SetAztecOption(AZ_output, + (parameters.output == + Parameters::Solver::quiet + ? + AZ_none + : + AZ_all)); + solver.SetAztecOption(AZ_solver, AZ_gmres); + solver.SetRHS(&b); + solver.SetLHS(&x); + + solver.SetAztecOption(AZ_precond, AZ_dom_decomp); + solver.SetAztecOption(AZ_subdomain_solve, AZ_ilut); + solver.SetAztecOption(AZ_overlap, 0); + solver.SetAztecOption(AZ_reorder, 0); + + solver.SetAztecParam(AZ_drop, parameters.ilut_drop); + solver.SetAztecParam(AZ_ilut_fill, parameters.ilut_fill); + solver.SetAztecParam(AZ_athresh, parameters.ilut_atol); + solver.SetAztecParam(AZ_rthresh, parameters.ilut_rtol); + + solver.SetUserMatrix(const_cast + (&system_matrix.trilinos_matrix())); + + solver.Iterate(parameters.max_iterations, parameters.linear_residual); + + return std::pair (solver.NumIters(), + solver.TrueResidual()); + } } Assert (false, ExcNotImplemented()); @@ -2709,15 +2710,15 @@ namespace Step33 } - // @sect4{ConservationLaw::compute_refinement_indicators} + // @sect4{ConservationLaw::compute_refinement_indicators} - // This function is real simple: We don't - // pretend that we know here what a good - // refinement indicator would be. Rather, we - // assume that the EulerEquation - // class would know about this, and so we - // simply defer to the respective function - // we've implemented there: + // This function is real simple: We don't + // pretend that we know here what a good + // refinement indicator would be. Rather, we + // assume that the EulerEquation + // class would know about this, and so we + // simply defer to the respective function + // we've implemented there: template void ConservationLaw:: @@ -2731,20 +2732,20 @@ namespace Step33 - // @sect4{ConservationLaw::refine_grid} + // @sect4{ConservationLaw::refine_grid} - // Here, we use the refinement indicators - // computed before and refine the mesh. At - // the beginning, we loop over all cells and - // mark those that we think should be - // refined: + // Here, we use the refinement indicators + // computed before and refine the mesh. At + // the beginning, we loop over all cells and + // mark those that we think should be + // refined: template void ConservationLaw::refine_grid (const Vector &refinement_indicators) { typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no) { @@ -2754,25 +2755,24 @@ namespace Step33 if ((cell->level() < parameters.shock_levels) && (std::fabs(refinement_indicators(cell_no)) > parameters.shock_val)) cell->set_refine_flag(); - else - if ((cell->level() > 0) && - (std::fabs(refinement_indicators(cell_no)) < 0.75*parameters.shock_val)) - cell->set_coarsen_flag(); + else if ((cell->level() > 0) && + (std::fabs(refinement_indicators(cell_no)) < 0.75*parameters.shock_val)) + cell->set_coarsen_flag(); } - // Then we need to transfer the - // various solution vectors from - // the old to the new grid while we - // do the refinement. The - // SolutionTransfer class is our - // friend here; it has a fairly - // extensive documentation, - // including examples, so we won't - // comment much on the following - // code. The last three lines - // simply re-set the sizes of some - // other vectors to the now correct - // size: + // Then we need to transfer the + // various solution vectors from + // the old to the new grid while we + // do the refinement. The + // SolutionTransfer class is our + // friend here; it has a fairly + // extensive documentation, + // including examples, so we won't + // comment much on the following + // code. The last three lines + // simply re-set the sizes of some + // other vectors to the now correct + // size: std::vector > transfer_in; std::vector > transfer_out; @@ -2813,28 +2813,28 @@ namespace Step33 } - // @sect4{ConservationLaw::output_results} - - // This function now is rather - // straightforward. All the magic, including - // transforming data from conservative - // variables to physical ones has been - // abstracted and moved into the - // EulerEquations class so that it can be - // replaced in case we want to solve some - // other hyperbolic conservation law. - // - // Note that the number of the output file is - // determined by keeping a counter in the - // form of a static variable that is set to - // zero the first time we come to this - // function and is incremented by one at the - // end of each invokation. + // @sect4{ConservationLaw::output_results} + + // This function now is rather + // straightforward. All the magic, including + // transforming data from conservative + // variables to physical ones has been + // abstracted and moved into the + // EulerEquations class so that it can be + // replaced in case we want to solve some + // other hyperbolic conservation law. + // + // Note that the number of the output file is + // determined by keeping a counter in the + // form of a static variable that is set to + // zero the first time we come to this + // function and is incremented by one at the + // end of each invokation. template void ConservationLaw::output_results () const { typename EulerEquations::Postprocessor - postprocessor (parameters.schlieren_plot); + postprocessor (parameters.schlieren_plot); DataOut data_out; data_out.attach_dof_handler (dof_handler); @@ -2861,22 +2861,22 @@ namespace Step33 - // @sect4{ConservationLaw::run} + // @sect4{ConservationLaw::run} - // This function contains the top-level logic - // of this program: initialization, the time - // loop, and the inner Newton iteration. - // - // At the beginning, we read the mesh file - // specified by the parameter file, setup the - // DoFHandler and various vectors, and then - // interpolate the given initial conditions - // on this mesh. We then perform a number of - // mesh refinements, based on the initial - // conditions, to obtain a mesh that is - // already well adapted to the starting - // solution. At the end of this process, we - // output the initial solution. + // This function contains the top-level logic + // of this program: initialization, the time + // loop, and the inner Newton iteration. + // + // At the beginning, we read the mesh file + // specified by the parameter file, setup the + // DoFHandler and various vectors, and then + // interpolate the given initial conditions + // on this mesh. We then perform a number of + // mesh refinements, based on the initial + // conditions, to obtain a mesh that is + // already well adapted to the starting + // solution. At the end of this process, we + // output the initial solution. template void ConservationLaw::run () { @@ -2893,7 +2893,7 @@ namespace Step33 dof_handler.clear(); dof_handler.distribute_dofs (fe); - // Size all of the fields. + // Size all of the fields. old_solution.reinit (dof_handler.n_dofs()); current_solution.reinit (dof_handler.n_dofs()); predictor.reinit (dof_handler.n_dofs()); @@ -2924,13 +2924,13 @@ namespace Step33 output_results (); - // We then enter into the main time - // stepping loop. At the top we simply - // output some status information so one - // can keep track of where a computation - // is, as well as the header for a table - // that indicates progress of the nonlinear - // inner iteration: + // We then enter into the main time + // stepping loop. At the top we simply + // output some status information so one + // can keep track of where a computation + // is, as well as the header for a table + // that indicates progress of the nonlinear + // inner iteration: Vector newton_update (dof_handler.n_dofs()); double time = 0; @@ -2951,50 +2951,50 @@ namespace Step33 std::cout << " NonLin Res Lin Iter Lin Res" << std::endl << " _____________________________________" << std::endl; - // Then comes the inner Newton - // iteration to solve the nonlinear - // problem in each time step. The way - // it works is to reset matrix and - // right hand side to zero, then - // assemble the linear system. If the - // norm of the right hand side is small - // enough, then we declare that the - // Newton iteration has - // converged. Otherwise, we solve the - // linear system, update the current - // solution with the Newton increment, - // and output convergence - // information. At the end, we check - // that the number of Newton iterations - // is not beyond a limit of 10 -- if it - // is, it appears likely that - // iterations are diverging and further - // iterations would do no good. If that - // happens, we throw an exception that - // will be caught in - // main() with status - // information being displayed before - // the program aborts. - // - // Note that the way we write the - // AssertThrow macro below is by and - // large equivalent to writing - // something like if - // (!(nonlin_iter @<= 10)) throw - // ExcMessage ("No convergence in - // nonlinear solver");. The only - // significant difference is that - // AssertThrow also makes sure that the - // exception being thrown carries with - // it information about the location - // (file name and line number) where it - // was generated. This is not overly - // critical here, because there is only - // a single place where this sort of - // exception can happen; however, it is - // generally a very useful tool when - // one wants to find out where an error - // occurred. + // Then comes the inner Newton + // iteration to solve the nonlinear + // problem in each time step. The way + // it works is to reset matrix and + // right hand side to zero, then + // assemble the linear system. If the + // norm of the right hand side is small + // enough, then we declare that the + // Newton iteration has + // converged. Otherwise, we solve the + // linear system, update the current + // solution with the Newton increment, + // and output convergence + // information. At the end, we check + // that the number of Newton iterations + // is not beyond a limit of 10 -- if it + // is, it appears likely that + // iterations are diverging and further + // iterations would do no good. If that + // happens, we throw an exception that + // will be caught in + // main() with status + // information being displayed before + // the program aborts. + // + // Note that the way we write the + // AssertThrow macro below is by and + // large equivalent to writing + // something like if + // (!(nonlin_iter @<= 10)) throw + // ExcMessage ("No convergence in + // nonlinear solver");. The only + // significant difference is that + // AssertThrow also makes sure that the + // exception being thrown carries with + // it information about the location + // (file name and line number) where it + // was generated. This is not overly + // critical here, because there is only + // a single place where this sort of + // exception can happen; however, it is + // generally a very useful tool when + // one wants to find out where an error + // occurred. unsigned int nonlin_iter = 0; current_solution = predictor; while (true) @@ -3028,38 +3028,38 @@ namespace Step33 ExcMessage ("No convergence in nonlinear solver")); } - // We only get to this point if the - // Newton iteration has converged, so - // do various post convergence tasks - // here: - // - // First, we update the time - // and produce graphical output - // if so desired. Then we - // update a predictor for the - // solution at the next time - // step by approximating - // $\mathbf w^{n+1}\approx - // \mathbf w^n + \delta t - // \frac{\partial \mathbf - // w}{\partial t} \approx - // \mathbf w^n + \delta t \; - // \frac{\mathbf w^n-\mathbf - // w^{n-1}}{\delta t} = 2 - // \mathbf w^n - \mathbf - // w^{n-1}$ to try and make - // adaptivity work better. The - // idea is to try and refine - // ahead of a front, rather - // than stepping into a coarse - // set of elements and smearing - // the old_solution. This - // simple time extrapolator - // does the job. With this, we - // then refine the mesh if so - // desired by the user, and - // finally continue on with the - // next time step: + // We only get to this point if the + // Newton iteration has converged, so + // do various post convergence tasks + // here: + // + // First, we update the time + // and produce graphical output + // if so desired. Then we + // update a predictor for the + // solution at the next time + // step by approximating + // $\mathbf w^{n+1}\approx + // \mathbf w^n + \delta t + // \frac{\partial \mathbf + // w}{\partial t} \approx + // \mathbf w^n + \delta t \; + // \frac{\mathbf w^n-\mathbf + // w^{n-1}}{\delta t} = 2 + // \mathbf w^n - \mathbf + // w^{n-1}$ to try and make + // adaptivity work better. The + // idea is to try and refine + // ahead of a front, rather + // than stepping into a coarse + // set of elements and smearing + // the old_solution. This + // simple time extrapolator + // does the job. With this, we + // then refine the mesh if so + // desired by the user, and + // finally continue on with the + // next time step: time += parameters.time_step; if (parameters.output_step < 0) @@ -3089,14 +3089,14 @@ namespace Step33 } } - // @sect3{main()} +// @sect3{main()} - // The following ``main'' function is - // similar to previous examples and - // need not to be commented on. Note - // that the program aborts if no input - // file name is given on the command - // line. +// The following ``main'' function is +// similar to previous examples and +// need not to be commented on. Note +// that the program aborts if no input +// file name is given on the command +// line. int main (int argc, char *argv[]) { try diff --git a/deal.II/examples/step-34/step-34.cc b/deal.II/examples/step-34/step-34.cc index 30e12e7997..dca7cc9139 100644 --- a/deal.II/examples/step-34/step-34.cc +++ b/deal.II/examples/step-34/step-34.cc @@ -9,13 +9,13 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // The program starts with including a bunch - // of include files that we will use in the - // various parts of the program. Most of them - // have been discussed in previous tutorials - // already: +// The program starts with including a bunch +// of include files that we will use in the +// various parts of the program. Most of them +// have been discussed in previous tutorials +// already: #include #include #include @@ -48,52 +48,52 @@ #include #include - // And here are a few C++ standard header - // files that we will need: +// And here are a few C++ standard header +// files that we will need: #include #include #include #include - // The last part of this preamble is to - // import everything in the dealii namespace - // into the one into which everything in this - // program will go: +// The last part of this preamble is to +// import everything in the dealii namespace +// into the one into which everything in this +// program will go: namespace Step34 { using namespace dealii; - // @sect3{Single and double layer operator kernels} + // @sect3{Single and double layer operator kernels} - // First, let us define a bit of the - // boundary integral equation - // machinery. + // First, let us define a bit of the + // boundary integral equation + // machinery. - // The following two functions are - // the actual calculations of the - // single and double layer potential - // kernels, that is $G$ and $\nabla - // G$. They are well defined only if - // the vector $R = - // \mathbf{y}-\mathbf{x}$ is - // different from zero. + // The following two functions are + // the actual calculations of the + // single and double layer potential + // kernels, that is $G$ and $\nabla + // G$. They are well defined only if + // the vector $R = + // \mathbf{y}-\mathbf{x}$ is + // different from zero. namespace LaplaceKernel { template double single_layer(const Point &R) { - switch(dim) + switch (dim) { - case 2: - return (-std::log(R.norm()) / (2*numbers::PI) ); + case 2: + return (-std::log(R.norm()) / (2*numbers::PI) ); - case 3: - return (1./( R.norm()*4*numbers::PI ) ); + case 3: + return (1./( R.norm()*4*numbers::PI ) ); - default: - Assert(false, ExcInternalError()); - return 0.; + default: + Assert(false, ExcInternalError()); + return 0.; } } @@ -102,376 +102,376 @@ namespace Step34 template Point double_layer(const Point &R) { - switch(dim) + switch (dim) { - case 2: - return R / ( -2*numbers::PI * R.square()); - case 3: - return R / ( -4*numbers::PI * R.square() * R.norm() ); - - default: - Assert(false, ExcInternalError()); - return Point(); + case 2: + return R / ( -2*numbers::PI * R.square()); + case 3: + return R / ( -4*numbers::PI * R.square() * R.norm() ); + + default: + Assert(false, ExcInternalError()); + return Point(); } } } - // @sect3{The BEMProblem class} - - // The structure of a boundary - // element method code is very - // similar to the structure of a - // finite element code, and so the - // member functions of this class are - // like those of most of the other - // tutorial programs. In particular, - // by now you should be familiar with - // reading parameters from an - // external file, and with the - // splitting of the different tasks - // into different modules. The same - // applies to boundary element - // methods, and we won't comment too - // much on them, except on the - // differences. + // @sect3{The BEMProblem class} + + // The structure of a boundary + // element method code is very + // similar to the structure of a + // finite element code, and so the + // member functions of this class are + // like those of most of the other + // tutorial programs. In particular, + // by now you should be familiar with + // reading parameters from an + // external file, and with the + // splitting of the different tasks + // into different modules. The same + // applies to boundary element + // methods, and we won't comment too + // much on them, except on the + // differences. template class BEMProblem { - public: - BEMProblem(const unsigned int fe_degree = 1, - const unsigned int mapping_degree = 1); - - void run(); - - private: - - void read_parameters (const std::string &filename); - - void read_domain(); - - void refine_and_resize(); - - // The only really different - // function that we find here is - // the assembly routine. We wrote - // this function in the most - // possible general way, in order - // to allow for easy - // generalization to higher order - // methods and to different - // fundamental solutions (e.g., - // Stokes or Maxwell). - // - // The most noticeable difference - // is the fact that the final - // matrix is full, and that we - // have a nested loop inside the - // usual loop on cells that - // visits all support points of - // the degrees of freedom. - // Moreover, when the support - // point lies inside the cell - // which we are visiting, then - // the integral we perform - // becomes singular. - // - // The practical consequence is - // that we have two sets of - // quadrature formulas, finite - // element values and temporary - // storage, one for standard - // integration and one for the - // singular integration, which - // are used where necessary. - void assemble_system(); - - // There are two options for the - // solution of this problem. The - // first is to use a direct - // solver, and the second is to - // use an iterative solver. We - // opt for the second option. - // - // The matrix that we assemble is - // not symmetric, and we opt to - // use the GMRES method; however - // the construction of an - // efficient preconditioner for - // boundary element methods is - // not a trivial issue. Here we - // use a non preconditioned GMRES - // solver. The options for the - // iterative solver, such as the - // tolerance, the maximum number - // of iterations, are selected - // through the parameter file. - void solve_system(); - - // Once we obtained the solution, - // we compute the $L^2$ error of - // the computed potential as well - // as the $L^\infty$ error of the - // approximation of the solid - // angle. The mesh we are using - // is an approximation of a - // smooth curve, therefore the - // computed diagonal matrix of - // fraction of angles or solid - // angles $\alpha(\mathbf{x})$ - // should be constantly equal to - // $\frac 12$. In this routine we - // output the error on the - // potential and the error in the - // approximation of the computed - // angle. Notice that the latter - // error is actually not the - // error in the computation of - // the angle, but a measure of - // how well we are approximating - // the sphere and the circle. - // - // Experimenting a little with - // the computation of the angles - // gives very accurate results - // for simpler geometries. To - // verify this you can comment - // out, in the read_domain() - // method, the - // tria.set_boundary(1, boundary) - // line, and check the alpha that - // is generated by the - // program. By removing this - // call, whenever the mesh is - // refined new nodes will be - // placed along the straight - // lines that made up the coarse - // mesh, rather than be pulled - // onto the surface that we - // really want to approximate. In - // the three dimensional case, - // the coarse grid of the sphere - // is obtained starting from a - // cube, and the obtained values - // of alphas are exactly $\frac - // 12$ on the nodes of the faces, - // $\frac 34$ on the nodes of the - // edges and $\frac 78$ on the 8 - // nodes of the vertices. - void compute_errors(const unsigned int cycle); - - // Once we obtained a solution on - // the codimension one domain, we - // want to interpolate it to the - // rest of the space. This is - // done by performing again the - // convolution of the solution - // with the kernel in the - // compute_exterior_solution() - // function. - // - // We would like to plot the - // velocity variable which is the - // gradient of the potential - // solution. The potential - // solution is only known on the - // boundary, but we use the - // convolution with the - // fundamental solution to - // interpolate it on a standard - // dim dimensional continuous - // finite element space. The plot - // of the gradient of the - // extrapolated solution will - // give us the velocity we want. - // - // In addition to the solution on - // the exterior domain, we also - // output the solution on the - // domain's boundary in the - // output_results() function, of - // course. - void compute_exterior_solution(); - - void output_results(const unsigned int cycle); - - // To allow for dimension - // independent programming, we - // specialize this single - // function to extract the - // singular quadrature formula - // needed to integrate the - // singular kernels in the - // interior of the cells. - const Quadrature & get_singular_quadrature( - const typename DoFHandler::active_cell_iterator &cell, - const unsigned int index) const; - - - // The usual deal.II classes can - // be used for boundary element - // methods by specifying the - // "codimension" of the - // problem. This is done by - // setting the optional second - // template arguments to - // Triangulation, FiniteElement - // and DoFHandler to the - // dimension of the embedding - // space. In our case we generate - // either 1 or 2 dimensional - // meshes embedded in 2 or 3 - // dimensional spaces. - // - // The optional argument by - // default is equal to the first - // argument, and produces the - // usual finite element classes - // that we saw in all previous - // examples. - // - // The class is constructed in a - // way to allow for arbitrary - // order of approximation of both - // the domain (through high order - // mapping) and the finite - // element space. The order of - // the finite element space and - // of the mapping can be selected - // in the constructor of the class. - - Triangulation tria; - FE_Q fe; - DoFHandler dh; - MappingQ mapping; - - // In BEM methods, the matrix - // that is generated is - // dense. Depending on the size - // of the problem, the final - // system might be solved by - // direct LU decomposition, or by - // iterative methods. In this - // example we use an - // unpreconditioned GMRES - // method. Building a - // preconditioner for BEM method - // is non trivial, and we don't - // treat this subject here. - - FullMatrix system_matrix; - Vector system_rhs; - - // The next two variables will - // denote the solution $\phi$ as - // well as a vector that will - // hold the values of - // $\alpha(\mathbf x)$ (the - // fraction of $\Omega$ visible - // from a point $\mathbf x$) at - // the support points of our - // shape functions. - - Vector phi; - Vector alpha; - - // The convergence table is used - // to output errors in the exact - // solution and in the computed - // alphas. - - ConvergenceTable convergence_table; - - // The following variables are - // the ones that we fill through - // a parameter file. The new - // objects that we use in this - // example are the - // Functions::ParsedFunction - // object and the - // QuadratureSelector object. - // - // The Functions::ParsedFunction - // class allows us to easily and - // quickly define new function - // objects via parameter files, - // with custom definitions which - // can be very complex (see the - // documentation of that class - // for all the available - // options). - // - // We will allocate the - // quadrature object using the - // QuadratureSelector class that - // allows us to generate - // quadrature formulas based on - // an identifying string and on - // the possible degree of the - // formula itself. We used this - // to allow custom selection of - // the quadrature formulas for - // the standard integration, and - // to define the order of the - // singular quadrature rule. - // - // We also define a couple of - // parameters which are used in - // case we wanted to extend the - // solution to the entire domain. - - Functions::ParsedFunction wind; - Functions::ParsedFunction exact_solution; - - unsigned int singular_quadrature_order; - std_cxx1x::shared_ptr > quadrature; - - SolverControl solver_control; - - unsigned int n_cycles; - unsigned int external_refinement; - - bool run_in_this_dimension; - bool extend_solution; + public: + BEMProblem(const unsigned int fe_degree = 1, + const unsigned int mapping_degree = 1); + + void run(); + + private: + + void read_parameters (const std::string &filename); + + void read_domain(); + + void refine_and_resize(); + + // The only really different + // function that we find here is + // the assembly routine. We wrote + // this function in the most + // possible general way, in order + // to allow for easy + // generalization to higher order + // methods and to different + // fundamental solutions (e.g., + // Stokes or Maxwell). + // + // The most noticeable difference + // is the fact that the final + // matrix is full, and that we + // have a nested loop inside the + // usual loop on cells that + // visits all support points of + // the degrees of freedom. + // Moreover, when the support + // point lies inside the cell + // which we are visiting, then + // the integral we perform + // becomes singular. + // + // The practical consequence is + // that we have two sets of + // quadrature formulas, finite + // element values and temporary + // storage, one for standard + // integration and one for the + // singular integration, which + // are used where necessary. + void assemble_system(); + + // There are two options for the + // solution of this problem. The + // first is to use a direct + // solver, and the second is to + // use an iterative solver. We + // opt for the second option. + // + // The matrix that we assemble is + // not symmetric, and we opt to + // use the GMRES method; however + // the construction of an + // efficient preconditioner for + // boundary element methods is + // not a trivial issue. Here we + // use a non preconditioned GMRES + // solver. The options for the + // iterative solver, such as the + // tolerance, the maximum number + // of iterations, are selected + // through the parameter file. + void solve_system(); + + // Once we obtained the solution, + // we compute the $L^2$ error of + // the computed potential as well + // as the $L^\infty$ error of the + // approximation of the solid + // angle. The mesh we are using + // is an approximation of a + // smooth curve, therefore the + // computed diagonal matrix of + // fraction of angles or solid + // angles $\alpha(\mathbf{x})$ + // should be constantly equal to + // $\frac 12$. In this routine we + // output the error on the + // potential and the error in the + // approximation of the computed + // angle. Notice that the latter + // error is actually not the + // error in the computation of + // the angle, but a measure of + // how well we are approximating + // the sphere and the circle. + // + // Experimenting a little with + // the computation of the angles + // gives very accurate results + // for simpler geometries. To + // verify this you can comment + // out, in the read_domain() + // method, the + // tria.set_boundary(1, boundary) + // line, and check the alpha that + // is generated by the + // program. By removing this + // call, whenever the mesh is + // refined new nodes will be + // placed along the straight + // lines that made up the coarse + // mesh, rather than be pulled + // onto the surface that we + // really want to approximate. In + // the three dimensional case, + // the coarse grid of the sphere + // is obtained starting from a + // cube, and the obtained values + // of alphas are exactly $\frac + // 12$ on the nodes of the faces, + // $\frac 34$ on the nodes of the + // edges and $\frac 78$ on the 8 + // nodes of the vertices. + void compute_errors(const unsigned int cycle); + + // Once we obtained a solution on + // the codimension one domain, we + // want to interpolate it to the + // rest of the space. This is + // done by performing again the + // convolution of the solution + // with the kernel in the + // compute_exterior_solution() + // function. + // + // We would like to plot the + // velocity variable which is the + // gradient of the potential + // solution. The potential + // solution is only known on the + // boundary, but we use the + // convolution with the + // fundamental solution to + // interpolate it on a standard + // dim dimensional continuous + // finite element space. The plot + // of the gradient of the + // extrapolated solution will + // give us the velocity we want. + // + // In addition to the solution on + // the exterior domain, we also + // output the solution on the + // domain's boundary in the + // output_results() function, of + // course. + void compute_exterior_solution(); + + void output_results(const unsigned int cycle); + + // To allow for dimension + // independent programming, we + // specialize this single + // function to extract the + // singular quadrature formula + // needed to integrate the + // singular kernels in the + // interior of the cells. + const Quadrature & get_singular_quadrature( + const typename DoFHandler::active_cell_iterator &cell, + const unsigned int index) const; + + + // The usual deal.II classes can + // be used for boundary element + // methods by specifying the + // "codimension" of the + // problem. This is done by + // setting the optional second + // template arguments to + // Triangulation, FiniteElement + // and DoFHandler to the + // dimension of the embedding + // space. In our case we generate + // either 1 or 2 dimensional + // meshes embedded in 2 or 3 + // dimensional spaces. + // + // The optional argument by + // default is equal to the first + // argument, and produces the + // usual finite element classes + // that we saw in all previous + // examples. + // + // The class is constructed in a + // way to allow for arbitrary + // order of approximation of both + // the domain (through high order + // mapping) and the finite + // element space. The order of + // the finite element space and + // of the mapping can be selected + // in the constructor of the class. + + Triangulation tria; + FE_Q fe; + DoFHandler dh; + MappingQ mapping; + + // In BEM methods, the matrix + // that is generated is + // dense. Depending on the size + // of the problem, the final + // system might be solved by + // direct LU decomposition, or by + // iterative methods. In this + // example we use an + // unpreconditioned GMRES + // method. Building a + // preconditioner for BEM method + // is non trivial, and we don't + // treat this subject here. + + FullMatrix system_matrix; + Vector system_rhs; + + // The next two variables will + // denote the solution $\phi$ as + // well as a vector that will + // hold the values of + // $\alpha(\mathbf x)$ (the + // fraction of $\Omega$ visible + // from a point $\mathbf x$) at + // the support points of our + // shape functions. + + Vector phi; + Vector alpha; + + // The convergence table is used + // to output errors in the exact + // solution and in the computed + // alphas. + + ConvergenceTable convergence_table; + + // The following variables are + // the ones that we fill through + // a parameter file. The new + // objects that we use in this + // example are the + // Functions::ParsedFunction + // object and the + // QuadratureSelector object. + // + // The Functions::ParsedFunction + // class allows us to easily and + // quickly define new function + // objects via parameter files, + // with custom definitions which + // can be very complex (see the + // documentation of that class + // for all the available + // options). + // + // We will allocate the + // quadrature object using the + // QuadratureSelector class that + // allows us to generate + // quadrature formulas based on + // an identifying string and on + // the possible degree of the + // formula itself. We used this + // to allow custom selection of + // the quadrature formulas for + // the standard integration, and + // to define the order of the + // singular quadrature rule. + // + // We also define a couple of + // parameters which are used in + // case we wanted to extend the + // solution to the entire domain. + + Functions::ParsedFunction wind; + Functions::ParsedFunction exact_solution; + + unsigned int singular_quadrature_order; + std_cxx1x::shared_ptr > quadrature; + + SolverControl solver_control; + + unsigned int n_cycles; + unsigned int external_refinement; + + bool run_in_this_dimension; + bool extend_solution; }; - // @sect4{BEMProblem::BEMProblem and BEMProblem::read_parameters} - - // The constructor initializes the - // variuous object in much the same - // way as done in the finite element - // programs such as step-4 or - // step-6. The only new ingredient - // here is the ParsedFunction object, - // which needs, at construction time, - // the specification of the number of - // components. - // - // For the exact solution the number - // of vector components is one, and - // no action is required since one is - // the default value for a - // ParsedFunction object. The wind, - // however, requires dim components - // to be specified. Notice that when - // declaring entries in a parameter - // file for the expression of the - // Functions::ParsedFunction, we need - // to specify the number of - // components explicitly, since the - // function - // Functions::ParsedFunction::declare_parameters - // is static, and has no knowledge of - // the number of components. + // @sect4{BEMProblem::BEMProblem and BEMProblem::read_parameters} + + // The constructor initializes the + // variuous object in much the same + // way as done in the finite element + // programs such as step-4 or + // step-6. The only new ingredient + // here is the ParsedFunction object, + // which needs, at construction time, + // the specification of the number of + // components. + // + // For the exact solution the number + // of vector components is one, and + // no action is required since one is + // the default value for a + // ParsedFunction object. The wind, + // however, requires dim components + // to be specified. Notice that when + // declaring entries in a parameter + // file for the expression of the + // Functions::ParsedFunction, we need + // to specify the number of + // components explicitly, since the + // function + // Functions::ParsedFunction::declare_parameters + // is static, and has no knowledge of + // the number of components. template BEMProblem::BEMProblem(const unsigned int fe_degree, const unsigned int mapping_degree) - : - fe(fe_degree), - dh(tria), - mapping(mapping_degree, true), - wind(dim) + : + fe(fe_degree), + dh(tria), + mapping(mapping_degree, true), + wind(dim) {} @@ -503,61 +503,61 @@ namespace Step34 } prm.leave_subsection(); - // For both two and three - // dimensions, we set the default - // input data to be such that the - // solution is $x+y$ or - // $x+y+z$. The actually computed - // solution will have value zero at - // infinity. In this case, this - // coincide with the exact - // solution, and no additional - // corrections are needed, but you - // should be aware of the fact that - // we arbitrarily set - // $\phi_\infty$, and the exact - // solution we pass to the program - // needs to have the same value at - // infinity for the error to be - // computed correctly. - // - // The use of the - // Functions::ParsedFunction object - // is pretty straight forward. The - // Functions::ParsedFunction::declare_parameters - // function takes an additional - // integer argument that specifies - // the number of components of the - // given function. Its default - // value is one. When the - // corresponding - // Functions::ParsedFunction::parse_parameters - // method is called, the calling - // object has to have the same - // number of components defined - // here, otherwise an exception is - // thrown. - // - // When declaring entries, we - // declare both 2 and three - // dimensional functions. However - // only the dim-dimensional one is - // ultimately parsed. This allows - // us to have only one parameter - // file for both 2 and 3 - // dimensional problems. - // - // Notice that from a mathematical - // point of view, the wind function - // on the boundary should satisfy - // the condition - // $\int_{\partial\Omega} - // \mathbf{v}\cdot \mathbf{n} d - // \Gamma = 0$, for the problem to - // have a solution. If this - // condition is not satisfied, then - // no solution can be found, and - // the solver will not converge. + // For both two and three + // dimensions, we set the default + // input data to be such that the + // solution is $x+y$ or + // $x+y+z$. The actually computed + // solution will have value zero at + // infinity. In this case, this + // coincide with the exact + // solution, and no additional + // corrections are needed, but you + // should be aware of the fact that + // we arbitrarily set + // $\phi_\infty$, and the exact + // solution we pass to the program + // needs to have the same value at + // infinity for the error to be + // computed correctly. + // + // The use of the + // Functions::ParsedFunction object + // is pretty straight forward. The + // Functions::ParsedFunction::declare_parameters + // function takes an additional + // integer argument that specifies + // the number of components of the + // given function. Its default + // value is one. When the + // corresponding + // Functions::ParsedFunction::parse_parameters + // method is called, the calling + // object has to have the same + // number of components defined + // here, otherwise an exception is + // thrown. + // + // When declaring entries, we + // declare both 2 and three + // dimensional functions. However + // only the dim-dimensional one is + // ultimately parsed. This allows + // us to have only one parameter + // file for both 2 and 3 + // dimensional problems. + // + // Notice that from a mathematical + // point of view, the wind function + // on the boundary should satisfy + // the condition + // $\int_{\partial\Omega} + // \mathbf{v}\cdot \mathbf{n} d + // \Gamma = 0$, for the problem to + // have a solution. If this + // condition is not satisfied, then + // no solution can be found, and + // the solver will not converge. prm.enter_subsection("Wind function 2d"); { Functions::ParsedFunction<2>::declare_parameters(prm, 2); @@ -587,23 +587,23 @@ namespace Step34 prm.leave_subsection(); - // In the solver section, we set - // all SolverControl - // parameters. The object will then - // be fed to the GMRES solver in - // the solve_system() function. + // In the solver section, we set + // all SolverControl + // parameters. The object will then + // be fed to the GMRES solver in + // the solve_system() function. prm.enter_subsection("Solver"); SolverControl::declare_parameters(prm); prm.leave_subsection(); - // After declaring all these - // parameters to the - // ParameterHandler object, let's - // read an input file that will - // give the parameters their - // values. We then proceed to - // extract these values from the - // ParameterHandler object: + // After declaring all these + // parameters to the + // ParameterHandler object, let's + // read an input file that will + // give the parameters their + // values. We then proceed to + // extract these values from the + // ParameterHandler object: prm.read_input(filename); n_cycles = prm.get_integer("Number of cycles"); @@ -639,74 +639,74 @@ namespace Step34 prm.leave_subsection(); - // Finally, here's another example - // of how to use parameter files in - // dimension independent - // programming. If we wanted to - // switch off one of the two - // simulations, we could do this by - // setting the corresponding "Run - // 2d simulation" or "Run 3d - // simulation" flag to false: + // Finally, here's another example + // of how to use parameter files in + // dimension independent + // programming. If we wanted to + // switch off one of the two + // simulations, we could do this by + // setting the corresponding "Run + // 2d simulation" or "Run 3d + // simulation" flag to false: run_in_this_dimension = prm.get_bool("Run " + Utilities::int_to_string(dim) + "d simulation"); } - // @sect4{BEMProblem::read_domain} - - // A boundary element method - // triangulation is basically the - // same as a (dim-1) dimensional - // triangulation, with the difference - // that the vertices belong to a - // (dim) dimensional space. - // - // Some of the mesh formats supported - // in deal.II use by default three - // dimensional points to describe - // meshes. These are the formats - // which are compatible with the - // boundary element method - // capabilities of deal.II. In - // particular we can use either UCD - // or GMSH formats. In both cases, we - // have to be particularly careful - // with the orientation of the mesh, - // because, unlike in the standard - // finite element case, no reordering - // or compatibility check is - // performed here. All meshes are - // considered as oriented, because - // they are embedded in a higher - // dimensional space. (See the - // documentation of the GridIn and of - // the Triangulation for further - // details on orientation of cells in - // a triangulation.) In our case, the - // normals to the mesh are external - // to both the circle in 2d or the - // sphere in 3d. - // - // The other detail that is required - // for appropriate refinement of the - // boundary element mesh, is an - // accurate description of the - // manifold that the mesh is - // approximating. We already saw this - // several times for the boundary of - // standard finite element meshes - // (for example in step-5 and - // step-6), and here the principle - // and usage is the same, except that - // the HyperBallBoundary class takes - // an additional template parameter - // that specifies the embedding space - // dimension. The function object - // still has to be static to live at - // least as long as the triangulation - // object to which it is attached. + // @sect4{BEMProblem::read_domain} + + // A boundary element method + // triangulation is basically the + // same as a (dim-1) dimensional + // triangulation, with the difference + // that the vertices belong to a + // (dim) dimensional space. + // + // Some of the mesh formats supported + // in deal.II use by default three + // dimensional points to describe + // meshes. These are the formats + // which are compatible with the + // boundary element method + // capabilities of deal.II. In + // particular we can use either UCD + // or GMSH formats. In both cases, we + // have to be particularly careful + // with the orientation of the mesh, + // because, unlike in the standard + // finite element case, no reordering + // or compatibility check is + // performed here. All meshes are + // considered as oriented, because + // they are embedded in a higher + // dimensional space. (See the + // documentation of the GridIn and of + // the Triangulation for further + // details on orientation of cells in + // a triangulation.) In our case, the + // normals to the mesh are external + // to both the circle in 2d or the + // sphere in 3d. + // + // The other detail that is required + // for appropriate refinement of the + // boundary element mesh, is an + // accurate description of the + // manifold that the mesh is + // approximating. We already saw this + // several times for the boundary of + // standard finite element meshes + // (for example in step-5 and + // step-6), and here the principle + // and usage is the same, except that + // the HyperBallBoundary class takes + // an additional template parameter + // that specifies the embedding space + // dimension. The function object + // still has to be static to live at + // least as long as the triangulation + // object to which it is attached. template void BEMProblem::read_domain() @@ -717,16 +717,16 @@ namespace Step34 std::ifstream in; switch (dim) { - case 2: - in.open ("coarse_circle.inp"); - break; + case 2: + in.open ("coarse_circle.inp"); + break; - case 3: - in.open ("coarse_sphere.inp"); - break; + case 3: + in.open ("coarse_sphere.inp"); + break; - default: - Assert (false, ExcNotImplemented()); + default: + Assert (false, ExcNotImplemented()); } GridIn gi; @@ -737,12 +737,12 @@ namespace Step34 } - // @sect4{BEMProblem::refine_and_resize} + // @sect4{BEMProblem::refine_and_resize} - // This function globally refines the - // mesh, distributes degrees of - // freedom, and resizes matrices and - // vectors. + // This function globally refines the + // mesh, distributes degrees of + // freedom, and resizes matrices and + // vectors. template void BEMProblem::refine_and_resize() @@ -761,26 +761,26 @@ namespace Step34 } - // @sect4{BEMProblem::assemble_system} + // @sect4{BEMProblem::assemble_system} - // The following is the main function - // of this program, assembling the - // matrix that corresponds to the - // boundary integral equation. + // The following is the main function + // of this program, assembling the + // matrix that corresponds to the + // boundary integral equation. template void BEMProblem::assemble_system() { - // First we initialize an FEValues - // object with the quadrature - // formula for the integration of - // the kernel in non singular - // cells. This quadrature is - // selected with the parameter - // file, and needs to be quite - // precise, since the functions we - // are integrating are not - // polynomial functions. + // First we initialize an FEValues + // object with the quadrature + // formula for the integration of + // the kernel in non singular + // cells. This quadrature is + // selected with the parameter + // file, and needs to be quite + // precise, since the functions we + // are integrating are not + // polynomial functions. FEValues fe_v(mapping, fe, *quadrature, update_values | update_cell_normal_vectors | @@ -794,49 +794,49 @@ namespace Step34 std::vector > cell_wind(n_q_points, Vector(dim) ); double normal_wind; - // Unlike in finite element - // methods, if we use a collocation - // boundary element method, then in - // each assembly loop we only - // assemble the information that - // refers to the coupling between - // one degree of freedom (the - // degree associated with support - // point $i$) and the current - // cell. This is done using a - // vector of fe.dofs_per_cell - // elements, which will then be - // distributed to the matrix in the - // global row $i$. The following - // object will hold this - // information: + // Unlike in finite element + // methods, if we use a collocation + // boundary element method, then in + // each assembly loop we only + // assemble the information that + // refers to the coupling between + // one degree of freedom (the + // degree associated with support + // point $i$) and the current + // cell. This is done using a + // vector of fe.dofs_per_cell + // elements, which will then be + // distributed to the matrix in the + // global row $i$. The following + // object will hold this + // information: Vector local_matrix_row_i(fe.dofs_per_cell); - // The index $i$ runs on the - // collocation points, which are - // the support points of the $i$th - // basis function, while $j$ runs - // on inner integration points. + // The index $i$ runs on the + // collocation points, which are + // the support points of the $i$th + // basis function, while $j$ runs + // on inner integration points. - // We construct a vector - // of support points which will be - // used in the local integrations: + // We construct a vector + // of support points which will be + // used in the local integrations: std::vector > support_points(dh.n_dofs()); DoFTools::map_dofs_to_support_points( mapping, dh, support_points); - // After doing so, we can start the - // integration loop over all cells, - // where we first initialize the - // FEValues object and get the - // values of $\mathbf{\tilde v}$ at - // the quadrature points (this - // vector field should be constant, - // but it doesn't hurt to be more - // general): + // After doing so, we can start the + // integration loop over all cells, + // where we first initialize the + // FEValues object and get the + // values of $\mathbf{\tilde v}$ at + // the quadrature points (this + // vector field should be constant, + // but it doesn't hurt to be more + // general): typename DoFHandler::active_cell_iterator - cell = dh.begin_active(), - endc = dh.end(); + cell = dh.begin_active(), + endc = dh.end(); for (cell = dh.begin_active(); cell != endc; ++cell) { @@ -847,22 +847,22 @@ namespace Step34 const std::vector > &normals = fe_v.get_cell_normal_vectors(); wind.vector_value_list(q_points, cell_wind); - // We then form the integral over - // the current cell for all - // degrees of freedom (note that - // this includes degrees of - // freedom not located on the - // current cell, a deviation from - // the usual finite element - // integrals). The integral that - // we need to perform is singular - // if one of the local degrees of - // freedom is the same as the - // support point $i$. A the - // beginning of the loop we - // therefore check wether this is - // the case, and we store which - // one is the singular index: + // We then form the integral over + // the current cell for all + // degrees of freedom (note that + // this includes degrees of + // freedom not located on the + // current cell, a deviation from + // the usual finite element + // integrals). The integral that + // we need to perform is singular + // if one of the local degrees of + // freedom is the same as the + // support point $i$. A the + // beginning of the loop we + // therefore check wether this is + // the case, and we store which + // one is the singular index: for (unsigned int i=0; i & singular_quadrature = - get_singular_quadrature(cell, singular_index); - - FEValues fe_v_singular (mapping, fe, singular_quadrature, - update_jacobians | - update_values | - update_cell_normal_vectors | - update_quadrature_points ); - - fe_v_singular.reinit(cell); - - std::vector > singular_cell_wind( singular_quadrature.size(), - Vector(dim) ); - - const std::vector > &singular_normals = fe_v_singular.get_cell_normal_vectors(); - const std::vector > &singular_q_points = fe_v_singular.get_quadrature_points(); - - wind.vector_value_list(singular_q_points, singular_cell_wind); - - for (unsigned int q=0; q R = singular_q_points[q] - support_points[i]; - double normal_wind = 0; - for (unsigned int d=0; d & singular_quadrature = + get_singular_quadrature(cell, singular_index); + + FEValues fe_v_singular (mapping, fe, singular_quadrature, + update_jacobians | + update_values | + update_cell_normal_vectors | + update_quadrature_points ); + + fe_v_singular.reinit(cell); + + std::vector > singular_cell_wind( singular_quadrature.size(), + Vector(dim) ); + + const std::vector > &singular_normals = fe_v_singular.get_cell_normal_vectors(); + const std::vector > &singular_q_points = fe_v_singular.get_quadrature_points(); + + wind.vector_value_list(singular_q_points, singular_cell_wind); + + for (unsigned int q=0; q R = singular_q_points[q] - support_points[i]; + double normal_wind = 0; + for (unsigned int d=0; d ones(dh.n_dofs()); ones.add(-1.); @@ -1030,10 +1033,10 @@ namespace Step34 } - // @sect4{BEMProblem::solve_system} + // @sect4{BEMProblem::solve_system} - // The next function simply solves - // the linear system. + // The next function simply solves + // the linear system. template void BEMProblem::solve_system() { @@ -1042,15 +1045,15 @@ namespace Step34 } - // @sect4{BEMProblem::compute_errors} + // @sect4{BEMProblem::compute_errors} - // The computation of the errors is - // exactly the same in all other - // example programs, and we won't - // comment too much. Notice how the - // same methods that are used in the - // finite element methods can be used - // here. + // The computation of the errors is + // exactly the same in all other + // example programs, and we won't + // comment too much. Notice how the + // same methods that are used in the + // finite element methods can be used + // here. template void BEMProblem::compute_errors(const unsigned int cycle) { @@ -1063,16 +1066,16 @@ namespace Step34 const double L2_error = difference_per_cell.l2_norm(); - // The error in the alpha vector - // can be computed directly using - // the Vector::linfty_norm() - // function, since on each node, - // the value should be $\frac - // 12$. All errors are then output - // and appended to our - // ConvergenceTable object for - // later computation of convergence - // rates: + // The error in the alpha vector + // can be computed directly using + // the Vector::linfty_norm() + // function, since on each node, + // the value should be $\frac + // 12$. All errors are then output + // and appended to our + // ConvergenceTable object for + // later computation of convergence + // rates: Vector difference_per_node(alpha); difference_per_node.add(-.5); @@ -1097,126 +1100,126 @@ namespace Step34 } - // Singular integration requires a - // careful selection of the - // quadrature rules. In particular - // the deal.II library provides - // quadrature rules which are - // taylored for logarithmic - // singularities (QGaussLog, - // QGaussLogR), as well as for 1/R - // singularities (QGaussOneOverR). - // - // Singular integration is typically - // obtained by constructing weighted - // quadrature formulas with singular - // weights, so that it is possible to - // write - // - // \f[ - // \int_K f(x) s(x) dx = \sum_{i=1}^N w_i f(q_i) - // \f] - // - // where $s(x)$ is a given - // singularity, and the weights and - // quadrature points $w_i,q_i$ are - // carefully selected to make the - // formula above an equality for a - // certain class of functions $f(x)$. - // - // In all the finite element examples - // we have seen so far, the weight of - // the quadrature itself (namely, the - // function $s(x)$), was always - // constantly equal to 1. For - // singular integration, we have two - // choices: we can use the definition - // above, factoring out the - // singularity from the integrand - // (i.e., integrating $f(x)$ with the - // special quadrature rule), or we - // can ask the quadrature rule to - // "normalize" the weights $w_i$ with - // $s(q_i)$: - // - // \f[ - // \int_K f(x) s(x) dx = - // \int_K g(x) dx = \sum_{i=1}^N \frac{w_i}{s(q_i)} g(q_i) - // \f] - // - // We use this second option, through - // the @p factor_out_singularity - // parameter of both QGaussLogR and - // QGaussOneOverR. - // - // These integrals are somewhat - // delicate, especially in two - // dimensions, due to the - // transformation from the real to - // the reference cell, where the - // variable of integration is scaled - // with the determinant of the - // transformation. - // - // In two dimensions this process - // does not result only in a factor - // appearing as a constant factor on - // the entire integral, but also on - // an additional integral altogether - // that needs to be evaluated: - // - // \f[ - // \int_0^1 f(x)\ln(x/\alpha) dx = - // \int_0^1 f(x)\ln(x) dx - \int_0^1 f(x) \ln(\alpha) dx. - // \f] - // - // This process is taken care of by - // the constructor of the QGaussLogR - // class, which adds additional - // quadrature points and weights to - // take into consideration also the - // second part of the integral. - // - // A similar reasoning should be done - // in the three dimensional case, - // since the singular quadrature is - // taylored on the inverse of the - // radius $r$ in the reference cell, - // while our singular function lives - // in real space, however in the - // three dimensional case everything - // is simpler because the singularity - // scales linearly with the - // determinant of the - // transformation. This allows us to - // build the singular two dimensional - // quadrature rules only once and, - // reuse them over all cells. - // - // In the one dimensional singular - // integration this is not possible, - // since we need to know the scaling - // parameter for the quadrature, - // which is not known a priori. Here, - // the quadrature rule itself depends - // also on the size of the current - // cell. For this reason, it is - // necessary to create a new - // quadrature for each singular - // integration. - // - // The different quadrature rules are - // built inside the - // get_singular_quadrature, which is - // specialized for dim=2 and dim=3, - // and they are retrieved inside the - // assemble_system function. The - // index given as an argument is the - // index of the unit support point - // where the singularity is located. + // Singular integration requires a + // careful selection of the + // quadrature rules. In particular + // the deal.II library provides + // quadrature rules which are + // taylored for logarithmic + // singularities (QGaussLog, + // QGaussLogR), as well as for 1/R + // singularities (QGaussOneOverR). + // + // Singular integration is typically + // obtained by constructing weighted + // quadrature formulas with singular + // weights, so that it is possible to + // write + // + // \f[ + // \int_K f(x) s(x) dx = \sum_{i=1}^N w_i f(q_i) + // \f] + // + // where $s(x)$ is a given + // singularity, and the weights and + // quadrature points $w_i,q_i$ are + // carefully selected to make the + // formula above an equality for a + // certain class of functions $f(x)$. + // + // In all the finite element examples + // we have seen so far, the weight of + // the quadrature itself (namely, the + // function $s(x)$), was always + // constantly equal to 1. For + // singular integration, we have two + // choices: we can use the definition + // above, factoring out the + // singularity from the integrand + // (i.e., integrating $f(x)$ with the + // special quadrature rule), or we + // can ask the quadrature rule to + // "normalize" the weights $w_i$ with + // $s(q_i)$: + // + // \f[ + // \int_K f(x) s(x) dx = + // \int_K g(x) dx = \sum_{i=1}^N \frac{w_i}{s(q_i)} g(q_i) + // \f] + // + // We use this second option, through + // the @p factor_out_singularity + // parameter of both QGaussLogR and + // QGaussOneOverR. + // + // These integrals are somewhat + // delicate, especially in two + // dimensions, due to the + // transformation from the real to + // the reference cell, where the + // variable of integration is scaled + // with the determinant of the + // transformation. + // + // In two dimensions this process + // does not result only in a factor + // appearing as a constant factor on + // the entire integral, but also on + // an additional integral altogether + // that needs to be evaluated: + // + // \f[ + // \int_0^1 f(x)\ln(x/\alpha) dx = + // \int_0^1 f(x)\ln(x) dx - \int_0^1 f(x) \ln(\alpha) dx. + // \f] + // + // This process is taken care of by + // the constructor of the QGaussLogR + // class, which adds additional + // quadrature points and weights to + // take into consideration also the + // second part of the integral. + // + // A similar reasoning should be done + // in the three dimensional case, + // since the singular quadrature is + // taylored on the inverse of the + // radius $r$ in the reference cell, + // while our singular function lives + // in real space, however in the + // three dimensional case everything + // is simpler because the singularity + // scales linearly with the + // determinant of the + // transformation. This allows us to + // build the singular two dimensional + // quadrature rules only once and, + // reuse them over all cells. + // + // In the one dimensional singular + // integration this is not possible, + // since we need to know the scaling + // parameter for the quadrature, + // which is not known a priori. Here, + // the quadrature rule itself depends + // also on the size of the current + // cell. For this reason, it is + // necessary to create a new + // quadrature for each singular + // integration. + // + // The different quadrature rules are + // built inside the + // get_singular_quadrature, which is + // specialized for dim=2 and dim=3, + // and they are retrieved inside the + // assemble_system function. The + // index given as an argument is the + // index of the unit support point + // where the singularity is located. template<> - const Quadrature<2> & BEMProblem<3>::get_singular_quadrature( + const Quadrature<2> &BEMProblem<3>::get_singular_quadrature( const DoFHandler<2,3>::active_cell_iterator &, const unsigned int index) const { @@ -1234,14 +1237,14 @@ namespace Step34 template<> - const Quadrature<1> & BEMProblem<2>::get_singular_quadrature( + const Quadrature<1> &BEMProblem<2>::get_singular_quadrature( const DoFHandler<1,2>::active_cell_iterator &cell, const unsigned int index) const { Assert(index < fe.dofs_per_cell, ExcIndexRange(0, fe.dofs_per_cell, index)); - static Quadrature<1> * q_pointer = NULL; + static Quadrature<1> *q_pointer = NULL; if (q_pointer) delete q_pointer; q_pointer = new QGaussLogR<1>(singular_quadrature_order, @@ -1252,36 +1255,36 @@ namespace Step34 - // @sect4{BEMProblem::compute_exterior_solution} - - // We'd like to also know something - // about the value of the potential - // $\phi$ in the exterior domain: - // after all our motivation to - // consider the boundary integral - // problem was that we wanted to know - // the velocity in the exterior - // domain! - // - // To this end, let us assume here - // that the boundary element domain - // is contained in the box - // $[-2,2]^{\text{dim}}$, and we - // extrapolate the actual solution - // inside this box using the - // convolution with the fundamental - // solution. The formula for this is - // given in the introduction. - // - // The reconstruction of the solution - // in the entire space is done on a - // continuous finite element grid of - // dimension dim. These are the usual - // ones, and we don't comment any - // further on them. At the end of the - // function, we output this exterior - // solution in, again, much the usual - // way. + // @sect4{BEMProblem::compute_exterior_solution} + + // We'd like to also know something + // about the value of the potential + // $\phi$ in the exterior domain: + // after all our motivation to + // consider the boundary integral + // problem was that we wanted to know + // the velocity in the exterior + // domain! + // + // To this end, let us assume here + // that the boundary element domain + // is contained in the box + // $[-2,2]^{\text{dim}}$, and we + // extrapolate the actual solution + // inside this box using the + // convolution with the fundamental + // solution. The formula for this is + // given in the introduction. + // + // The reconstruction of the solution + // in the entire space is done on a + // continuous finite element grid of + // dimension dim. These are the usual + // ones, and we don't comment any + // further on them. At the end of the + // function, we output this exterior + // solution in, again, much the usual + // way. template void BEMProblem::compute_exterior_solution() { @@ -1297,8 +1300,8 @@ namespace Step34 external_phi.reinit(external_dh.n_dofs()); typename DoFHandler::active_cell_iterator - cell = dh.begin_active(), - endc = dh.end(); + cell = dh.begin_active(), + endc = dh.end(); FEValues fe_v(mapping, fe, *quadrature, @@ -1331,11 +1334,12 @@ namespace Step34 wind.vector_value_list(q_points, local_wind); - for (unsigned int q=0; q void BEMProblem::output_results(const unsigned int cycle) { @@ -1403,20 +1407,20 @@ namespace Step34 convergence_table.set_scientific("Linfty(alpha)", true); convergence_table - .evaluate_convergence_rates("L2(phi)", ConvergenceTable::reduction_rate_log2); + .evaluate_convergence_rates("L2(phi)", ConvergenceTable::reduction_rate_log2); convergence_table - .evaluate_convergence_rates("Linfty(alpha)", ConvergenceTable::reduction_rate_log2); + .evaluate_convergence_rates("Linfty(alpha)", ConvergenceTable::reduction_rate_log2); deallog << std::endl; convergence_table.write_text(std::cout); } } - // @sect4{BEMProblem::run} + // @sect4{BEMProblem::run} - // This is the main function. It - // should be self explanatory in its - // briefness: + // This is the main function. It + // should be self explanatory in its + // briefness: template void BEMProblem::run() { @@ -1448,11 +1452,11 @@ namespace Step34 } - // @sect3{The main() function} +// @sect3{The main() function} - // This is the main function of this - // program. It is exactly like all previous - // tutorial programs: +// This is the main function of this +// program. It is exactly like all previous +// tutorial programs: int main () { try diff --git a/deal.II/examples/step-35/step-35.cc b/deal.II/examples/step-35/step-35.cc index cd57ab99b7..109da8c0dd 100644 --- a/deal.II/examples/step-35/step-35.cc +++ b/deal.II/examples/step-35/step-35.cc @@ -10,13 +10,13 @@ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // We start by including all the necessary - // deal.II header files and some C++ related - // ones. Each one of them has been discussed - // in previous tutorial programs, so we will - // not get into details here. +// We start by including all the necessary +// deal.II header files and some C++ related +// ones. Each one of them has been discussed +// in previous tutorial programs, so we will +// not get into details here. #include #include #include @@ -63,142 +63,142 @@ #include #include - // Finally this is as in all previous - // programs: +// Finally this is as in all previous +// programs: namespace Step35 { using namespace dealii; - // @sect3{Run time parameters} - // - // Since our method has several - // parameters that can be fine-tuned - // we put them into an external file, - // so that they can be determined at - // run-time. - // - // This includes, in particular, the - // formulation of the equation for - // the auxiliary variable $\phi$, for - // which we declare an - // enum. Next, we - // declare a class that is going to - // read and store all the parameters - // that our program needs to run. + // @sect3{Run time parameters} + // + // Since our method has several + // parameters that can be fine-tuned + // we put them into an external file, + // so that they can be determined at + // run-time. + // + // This includes, in particular, the + // formulation of the equation for + // the auxiliary variable $\phi$, for + // which we declare an + // enum. Next, we + // declare a class that is going to + // read and store all the parameters + // that our program needs to run. namespace RunTimeParameters { enum MethodFormulation { - METHOD_STANDARD, - METHOD_ROTATIONAL + METHOD_STANDARD, + METHOD_ROTATIONAL }; class Data_Storage { - public: - Data_Storage(); - ~Data_Storage(); - void read_data (const char *filename); - MethodFormulation form; - double initial_time, - final_time, - Reynolds; - double dt; - unsigned int n_global_refines, - pressure_degree; - unsigned int vel_max_iterations, - vel_Krylov_size, - vel_off_diagonals, - vel_update_prec; - double vel_eps, - vel_diag_strength; - bool verbose; - unsigned int output_interval; - protected: - ParameterHandler prm; + public: + Data_Storage(); + ~Data_Storage(); + void read_data (const char *filename); + MethodFormulation form; + double initial_time, + final_time, + Reynolds; + double dt; + unsigned int n_global_refines, + pressure_degree; + unsigned int vel_max_iterations, + vel_Krylov_size, + vel_off_diagonals, + vel_update_prec; + double vel_eps, + vel_diag_strength; + bool verbose; + unsigned int output_interval; + protected: + ParameterHandler prm; }; - // In the constructor of this class - // we declare all the - // parameters. The details of how - // this works have been discussed - // elsewhere, for example in - // step-19 and step-29. + // In the constructor of this class + // we declare all the + // parameters. The details of how + // this works have been discussed + // elsewhere, for example in + // step-19 and step-29. Data_Storage::Data_Storage() { prm.declare_entry ("Method_Form", "rotational", - Patterns::Selection ("rotational|standard"), - " Used to select the type of method that we are going " - "to use. "); + Patterns::Selection ("rotational|standard"), + " Used to select the type of method that we are going " + "to use. "); prm.enter_subsection ("Physical data"); { - prm.declare_entry ("initial_time", "0.", - Patterns::Double (0.), - " The initial time of the simulation. "); - prm.declare_entry ("final_time", "1.", - Patterns::Double (0.), - " The final time of the simulation. "); - prm.declare_entry ("Reynolds", "1.", - Patterns::Double (0.), - " The Reynolds number. "); + prm.declare_entry ("initial_time", "0.", + Patterns::Double (0.), + " The initial time of the simulation. "); + prm.declare_entry ("final_time", "1.", + Patterns::Double (0.), + " The final time of the simulation. "); + prm.declare_entry ("Reynolds", "1.", + Patterns::Double (0.), + " The Reynolds number. "); } prm.leave_subsection(); prm.enter_subsection ("Time step data"); { - prm.declare_entry ("dt", "5e-4", - Patterns::Double (0.), - " The time step size. "); + prm.declare_entry ("dt", "5e-4", + Patterns::Double (0.), + " The time step size. "); } prm.leave_subsection(); prm.enter_subsection ("Space discretization"); { - prm.declare_entry ("n_of_refines", "0", - Patterns::Integer (0, 15), - " The number of global refines we do on the mesh. "); - prm.declare_entry ("pressure_fe_degree", "1", - Patterns::Integer (1, 5), - " The polynomial degree for the pressure space. "); + prm.declare_entry ("n_of_refines", "0", + Patterns::Integer (0, 15), + " The number of global refines we do on the mesh. "); + prm.declare_entry ("pressure_fe_degree", "1", + Patterns::Integer (1, 5), + " The polynomial degree for the pressure space. "); } prm.leave_subsection(); prm.enter_subsection ("Data solve velocity"); { - prm.declare_entry ("max_iterations", "1000", - Patterns::Integer (1, 1000), - " The maximal number of iterations GMRES must make. "); - prm.declare_entry ("eps", "1e-12", - Patterns::Double (0.), - " The stopping criterion. "); - prm.declare_entry ("Krylov_size", "30", - Patterns::Integer(1), - " The size of the Krylov subspace to be used. "); - prm.declare_entry ("off_diagonals", "60", - Patterns::Integer(0), - " The number of off-diagonal elements ILU must " - "compute. "); - prm.declare_entry ("diag_strength", "0.01", - Patterns::Double (0.), - " Diagonal strengthening coefficient. "); - prm.declare_entry ("update_prec", "15", - Patterns::Integer(1), - " This number indicates how often we need to " - "update the preconditioner"); + prm.declare_entry ("max_iterations", "1000", + Patterns::Integer (1, 1000), + " The maximal number of iterations GMRES must make. "); + prm.declare_entry ("eps", "1e-12", + Patterns::Double (0.), + " The stopping criterion. "); + prm.declare_entry ("Krylov_size", "30", + Patterns::Integer(1), + " The size of the Krylov subspace to be used. "); + prm.declare_entry ("off_diagonals", "60", + Patterns::Integer(0), + " The number of off-diagonal elements ILU must " + "compute. "); + prm.declare_entry ("diag_strength", "0.01", + Patterns::Double (0.), + " Diagonal strengthening coefficient. "); + prm.declare_entry ("update_prec", "15", + Patterns::Integer(1), + " This number indicates how often we need to " + "update the preconditioner"); } prm.leave_subsection(); prm.declare_entry ("verbose", "true", - Patterns::Bool(), - " This indicates whether the output of the solution " - "process should be verbose. "); + Patterns::Bool(), + " This indicates whether the output of the solution " + "process should be verbose. "); prm.declare_entry ("output_interval", "1", - Patterns::Integer(1), - " This indicates between how many time steps we print " - "the solution. "); + Patterns::Integer(1), + " This indicates between how many time steps we print " + "the solution. "); } @@ -216,39 +216,39 @@ namespace Step35 prm.read_input (file); if (prm.get ("Method_Form") == std::string ("rotational")) - form = METHOD_ROTATIONAL; + form = METHOD_ROTATIONAL; else - form = METHOD_STANDARD; + form = METHOD_STANDARD; prm.enter_subsection ("Physical data"); { - initial_time = prm.get_double ("initial_time"); - final_time = prm.get_double ("final_time"); - Reynolds = prm.get_double ("Reynolds"); + initial_time = prm.get_double ("initial_time"); + final_time = prm.get_double ("final_time"); + Reynolds = prm.get_double ("Reynolds"); } prm.leave_subsection(); prm.enter_subsection ("Time step data"); { - dt = prm.get_double ("dt"); + dt = prm.get_double ("dt"); } prm.leave_subsection(); prm.enter_subsection ("Space discretization"); { - n_global_refines = prm.get_integer ("n_of_refines"); - pressure_degree = prm.get_integer ("pressure_fe_degree"); + n_global_refines = prm.get_integer ("n_of_refines"); + pressure_degree = prm.get_integer ("pressure_fe_degree"); } prm.leave_subsection(); prm.enter_subsection ("Data solve velocity"); { - vel_max_iterations = prm.get_integer ("max_iterations"); - vel_eps = prm.get_double ("eps"); - vel_Krylov_size = prm.get_integer ("Krylov_size"); - vel_off_diagonals = prm.get_integer ("off_diagonals"); - vel_diag_strength = prm.get_double ("diag_strength"); - vel_update_prec = prm.get_integer ("update_prec"); + vel_max_iterations = prm.get_integer ("max_iterations"); + vel_eps = prm.get_double ("eps"); + vel_Krylov_size = prm.get_integer ("Krylov_size"); + vel_off_diagonals = prm.get_integer ("off_diagonals"); + vel_diag_strength = prm.get_double ("diag_strength"); + vel_update_prec = prm.get_integer ("update_prec"); } prm.leave_subsection(); @@ -260,44 +260,44 @@ namespace Step35 - // @sect3{Equation data} + // @sect3{Equation data} - // In the next namespace, we declare - // the initial and boundary - // conditions: + // In the next namespace, we declare + // the initial and boundary + // conditions: namespace EquationData { - // As we have chosen a completely - // decoupled formulation, we will - // not take advantage of deal.II's - // capabilities to handle vector - // valued problems. We do, however, - // want to use an interface for the - // equation data that is somehow - // dimension independent. To be - // able to do that, our functions - // should be able to know on which - // spatial component we are - // currently working, and we should - // be able to have a common - // interface to do that. The - // following class is an attempt in - // that direction. + // As we have chosen a completely + // decoupled formulation, we will + // not take advantage of deal.II's + // capabilities to handle vector + // valued problems. We do, however, + // want to use an interface for the + // equation data that is somehow + // dimension independent. To be + // able to do that, our functions + // should be able to know on which + // spatial component we are + // currently working, and we should + // be able to have a common + // interface to do that. The + // following class is an attempt in + // that direction. template class MultiComponentFunction: public Function { - public: - MultiComponentFunction (const double initial_time = 0.); - void set_component (const unsigned int d); - protected: - unsigned int comp; + public: + MultiComponentFunction (const double initial_time = 0.); + void set_component (const unsigned int d); + protected: + unsigned int comp; }; template MultiComponentFunction:: MultiComponentFunction (const double initial_time) - : - Function (1, initial_time), comp(0) + : + Function (1, initial_time), comp(0) {} @@ -309,57 +309,57 @@ namespace Step35 } - // With this class defined, we - // declare classes that describe - // the boundary conditions for - // velocity and pressure: + // With this class defined, we + // declare classes that describe + // the boundary conditions for + // velocity and pressure: template class Velocity : public MultiComponentFunction { - public: - Velocity (const double initial_time = 0.0); + public: + Velocity (const double initial_time = 0.0); - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void value_list (const std::vector< Point > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void value_list (const std::vector< Point > &points, + std::vector &values, + const unsigned int component = 0) const; }; template Velocity::Velocity (const double initial_time) - : - MultiComponentFunction (initial_time) + : + MultiComponentFunction (initial_time) {} template void Velocity::value_list (const std::vector > &points, - std::vector &values, - const unsigned int) const + std::vector &values, + const unsigned int) const { const unsigned int n_points = points.size(); Assert (values.size() == n_points, - ExcDimensionMismatch (values.size(), n_points)); + ExcDimensionMismatch (values.size(), n_points)); for (unsigned int i=0; i::value (points[i]); + values[i] = Velocity::value (points[i]); } template double Velocity::value (const Point &p, - const unsigned int) const + const unsigned int) const { if (this->comp == 0) - { - const double Um = 1.5; - const double H = 4.1; - return 4.*Um*p(1)*(H - p(1))/(H*H); - } + { + const double Um = 1.5; + const double H = 4.1; + return 4.*Um*p(1)*(H - p(1))/(H*H); + } else - return 0.; + return 0.; } @@ -367,35 +367,35 @@ namespace Step35 template class Pressure: public Function { - public: - Pressure (const double initial_time = 0.0); + public: + Pressure (const double initial_time = 0.0); - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void value_list (const std::vector< Point > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void value_list (const std::vector< Point > &points, + std::vector &values, + const unsigned int component = 0) const; }; template Pressure::Pressure (const double initial_time) - : - Function (1, initial_time) + : + Function (1, initial_time) {} template double Pressure::value (const Point &p, - const unsigned int) const + const unsigned int) const { return 25.-p(0); } template void Pressure::value_list (const std::vector > &points, - std::vector &values, - const unsigned int) const + std::vector &values, + const unsigned int) const { const unsigned int n_points = points.size(); Assert (values.size() == n_points, ExcDimensionMismatch (values.size(), n_points)); @@ -406,318 +406,318 @@ namespace Step35 - // @sect3{The NavierStokesProjection class} + // @sect3{The NavierStokesProjection class} - // Now for the main class of the program. It - // implements the various versions of the - // projection method for Navier-Stokes - // equations. The names for all the methods - // and member variables should be - // self-explanatory, taking into account the - // implementation details given in the - // introduction. + // Now for the main class of the program. It + // implements the various versions of the + // projection method for Navier-Stokes + // equations. The names for all the methods + // and member variables should be + // self-explanatory, taking into account the + // implementation details given in the + // introduction. template class NavierStokesProjection { - public: - NavierStokesProjection (const RunTimeParameters::Data_Storage &data); - - void run (const bool verbose = false, - const unsigned int n_plots = 10); - protected: - RunTimeParameters::MethodFormulation type; - - const unsigned int deg; - const double dt; - const double t_0, T, Re; - - EquationData::Velocity vel_exact; - std::map boundary_values; - std::vector boundary_indicators; - - Triangulation triangulation; - - FE_Q fe_velocity; - FE_Q fe_pressure; - - DoFHandler dof_handler_velocity; - DoFHandler dof_handler_pressure; - - QGauss quadrature_pressure; - QGauss quadrature_velocity; - - SparsityPattern sparsity_pattern_velocity; - SparsityPattern sparsity_pattern_pressure; - SparsityPattern sparsity_pattern_pres_vel; - - SparseMatrix vel_Laplace_plus_Mass; - SparseMatrix vel_it_matrix[dim]; - SparseMatrix vel_Mass; - SparseMatrix vel_Laplace; - SparseMatrix vel_Advection; - SparseMatrix pres_Laplace; - SparseMatrix pres_Mass; - SparseMatrix pres_Diff[dim]; - SparseMatrix pres_iterative; - - Vector pres_n; - Vector pres_n_minus_1; - Vector phi_n; - Vector phi_n_minus_1; - Vector u_n[dim]; - Vector u_n_minus_1[dim]; - Vector u_star[dim]; - Vector force[dim]; - Vector v_tmp; - Vector pres_tmp; - Vector rot_u; - - SparseILU prec_velocity[dim]; - SparseILU prec_pres_Laplace; - SparseDirectUMFPACK prec_mass; - SparseDirectUMFPACK prec_vel_mass; - - DeclException2 (ExcInvalidTimeStep, - double, double, - << " The time step " << arg1 << " is out of range." - << std::endl - << " The permitted range is (0," << arg2 << "]"); - - void create_triangulation_and_dofs (const unsigned int n_refines); - - void initialize(); - - void interpolate_velocity (); - - void diffusion_step (const bool reinit_prec); - - void projection_step (const bool reinit_prec); - - void update_pressure (const bool reinit_prec); - - private: - unsigned int vel_max_its; - unsigned int vel_Krylov_size; - unsigned int vel_off_diagonals; - unsigned int vel_update_prec; - double vel_eps; - double vel_diag_strength; - - void initialize_velocity_matrices(); - - void initialize_pressure_matrices(); - - // The next few structures and functions - // are for doing various things in - // parallel. They follow the scheme laid - // out in @ref threads, using the - // WorkStream class. As explained there, - // this requires us to declare two - // structures for each of the assemblers, - // a per-task data and a scratch data - // structure. These are then handed over - // to functions that assemble local - // contributions and that copy these - // local contributions to the global - // objects. - // - // One of the things that are specific to - // this program is that we don't just - // have a single DoFHandler object that - // represents both the velocities and the - // pressure, but we use individual - // DoFHandler objects for these two kinds - // of variables. We pay for this - // optimization when we want to assemble - // terms that involve both variables, - // such as the divergence of the velocity - // and the gradient of the pressure, - // times the respective test - // functions. When doing so, we can't - // just anymore use a single FEValues - // object, but rather we need two, and - // they need to be initialized with cell - // iterators that point to the same cell - // in the triangulation but different - // DoFHandlers. - // - // To do this in practice, we declare a - // "synchronous" iterator -- an object - // that internally consists of several - // (in our case two) iterators, and each - // time the synchronous iteration is - // moved up one step, each of the - // iterators stored internally is moved - // up one step as well, thereby always - // staying in sync. As it so happens, - // there is a deal.II class that - // facilitates this sort of thing. - typedef std_cxx1x::tuple< typename DoFHandler::active_cell_iterator, - typename DoFHandler::active_cell_iterator - > IteratorTuple; - - typedef SynchronousIterators IteratorPair; - - void initialize_gradient_operator(); - - struct InitGradPerTaskData - { - unsigned int d; - unsigned int vel_dpc; - unsigned int pres_dpc; - FullMatrix local_grad; - std::vector vel_local_dof_indices; - std::vector pres_local_dof_indices; - - InitGradPerTaskData (const unsigned int dd, - const unsigned int vdpc, - const unsigned int pdpc) - : - d(dd), - vel_dpc (vdpc), - pres_dpc (pdpc), - local_grad (vdpc, pdpc), - vel_local_dof_indices (vdpc), - pres_local_dof_indices (pdpc) - {} - }; + public: + NavierStokesProjection (const RunTimeParameters::Data_Storage &data); + + void run (const bool verbose = false, + const unsigned int n_plots = 10); + protected: + RunTimeParameters::MethodFormulation type; + + const unsigned int deg; + const double dt; + const double t_0, T, Re; + + EquationData::Velocity vel_exact; + std::map boundary_values; + std::vector boundary_indicators; + + Triangulation triangulation; + + FE_Q fe_velocity; + FE_Q fe_pressure; + + DoFHandler dof_handler_velocity; + DoFHandler dof_handler_pressure; + + QGauss quadrature_pressure; + QGauss quadrature_velocity; + + SparsityPattern sparsity_pattern_velocity; + SparsityPattern sparsity_pattern_pressure; + SparsityPattern sparsity_pattern_pres_vel; + + SparseMatrix vel_Laplace_plus_Mass; + SparseMatrix vel_it_matrix[dim]; + SparseMatrix vel_Mass; + SparseMatrix vel_Laplace; + SparseMatrix vel_Advection; + SparseMatrix pres_Laplace; + SparseMatrix pres_Mass; + SparseMatrix pres_Diff[dim]; + SparseMatrix pres_iterative; + + Vector pres_n; + Vector pres_n_minus_1; + Vector phi_n; + Vector phi_n_minus_1; + Vector u_n[dim]; + Vector u_n_minus_1[dim]; + Vector u_star[dim]; + Vector force[dim]; + Vector v_tmp; + Vector pres_tmp; + Vector rot_u; + + SparseILU prec_velocity[dim]; + SparseILU prec_pres_Laplace; + SparseDirectUMFPACK prec_mass; + SparseDirectUMFPACK prec_vel_mass; + + DeclException2 (ExcInvalidTimeStep, + double, double, + << " The time step " << arg1 << " is out of range." + << std::endl + << " The permitted range is (0," << arg2 << "]"); + + void create_triangulation_and_dofs (const unsigned int n_refines); + + void initialize(); + + void interpolate_velocity (); + + void diffusion_step (const bool reinit_prec); + + void projection_step (const bool reinit_prec); + + void update_pressure (const bool reinit_prec); + + private: + unsigned int vel_max_its; + unsigned int vel_Krylov_size; + unsigned int vel_off_diagonals; + unsigned int vel_update_prec; + double vel_eps; + double vel_diag_strength; + + void initialize_velocity_matrices(); + + void initialize_pressure_matrices(); + + // The next few structures and functions + // are for doing various things in + // parallel. They follow the scheme laid + // out in @ref threads, using the + // WorkStream class. As explained there, + // this requires us to declare two + // structures for each of the assemblers, + // a per-task data and a scratch data + // structure. These are then handed over + // to functions that assemble local + // contributions and that copy these + // local contributions to the global + // objects. + // + // One of the things that are specific to + // this program is that we don't just + // have a single DoFHandler object that + // represents both the velocities and the + // pressure, but we use individual + // DoFHandler objects for these two kinds + // of variables. We pay for this + // optimization when we want to assemble + // terms that involve both variables, + // such as the divergence of the velocity + // and the gradient of the pressure, + // times the respective test + // functions. When doing so, we can't + // just anymore use a single FEValues + // object, but rather we need two, and + // they need to be initialized with cell + // iterators that point to the same cell + // in the triangulation but different + // DoFHandlers. + // + // To do this in practice, we declare a + // "synchronous" iterator -- an object + // that internally consists of several + // (in our case two) iterators, and each + // time the synchronous iteration is + // moved up one step, each of the + // iterators stored internally is moved + // up one step as well, thereby always + // staying in sync. As it so happens, + // there is a deal.II class that + // facilitates this sort of thing. + typedef std_cxx1x::tuple< typename DoFHandler::active_cell_iterator, + typename DoFHandler::active_cell_iterator + > IteratorTuple; + + typedef SynchronousIterators IteratorPair; + + void initialize_gradient_operator(); + + struct InitGradPerTaskData + { + unsigned int d; + unsigned int vel_dpc; + unsigned int pres_dpc; + FullMatrix local_grad; + std::vector vel_local_dof_indices; + std::vector pres_local_dof_indices; + + InitGradPerTaskData (const unsigned int dd, + const unsigned int vdpc, + const unsigned int pdpc) + : + d(dd), + vel_dpc (vdpc), + pres_dpc (pdpc), + local_grad (vdpc, pdpc), + vel_local_dof_indices (vdpc), + pres_local_dof_indices (pdpc) + {} + }; - struct InitGradScratchData - { - unsigned int nqp; - FEValues fe_val_vel; - FEValues fe_val_pres; - InitGradScratchData (const FE_Q &fe_v, - const FE_Q &fe_p, - const QGauss &quad, - const UpdateFlags flags_v, - const UpdateFlags flags_p) - : - nqp (quad.size()), - fe_val_vel (fe_v, quad, flags_v), - fe_val_pres (fe_p, quad, flags_p) - {} - InitGradScratchData (const InitGradScratchData &data) - : - nqp (data.nqp), - fe_val_vel (data.fe_val_vel.get_fe(), - data.fe_val_vel.get_quadrature(), - data.fe_val_vel.get_update_flags()), - fe_val_pres (data.fe_val_pres.get_fe(), - data.fe_val_pres.get_quadrature(), - data.fe_val_pres.get_update_flags()) - {} - }; + struct InitGradScratchData + { + unsigned int nqp; + FEValues fe_val_vel; + FEValues fe_val_pres; + InitGradScratchData (const FE_Q &fe_v, + const FE_Q &fe_p, + const QGauss &quad, + const UpdateFlags flags_v, + const UpdateFlags flags_p) + : + nqp (quad.size()), + fe_val_vel (fe_v, quad, flags_v), + fe_val_pres (fe_p, quad, flags_p) + {} + InitGradScratchData (const InitGradScratchData &data) + : + nqp (data.nqp), + fe_val_vel (data.fe_val_vel.get_fe(), + data.fe_val_vel.get_quadrature(), + data.fe_val_vel.get_update_flags()), + fe_val_pres (data.fe_val_pres.get_fe(), + data.fe_val_pres.get_quadrature(), + data.fe_val_pres.get_update_flags()) + {} + }; - void assemble_one_cell_of_gradient (const IteratorPair &SI, - InitGradScratchData &scratch, - InitGradPerTaskData &data); + void assemble_one_cell_of_gradient (const IteratorPair &SI, + InitGradScratchData &scratch, + InitGradPerTaskData &data); - void copy_gradient_local_to_global (const InitGradPerTaskData &data); + void copy_gradient_local_to_global (const InitGradPerTaskData &data); - // The same general layout also applies - // to the following classes and functions - // implementing the assembly of the - // advection term: - void assemble_advection_term(); + // The same general layout also applies + // to the following classes and functions + // implementing the assembly of the + // advection term: + void assemble_advection_term(); - struct AdvectionPerTaskData - { - FullMatrix local_advection; - std::vector local_dof_indices; - AdvectionPerTaskData (const unsigned int dpc) - : - local_advection (dpc, dpc), - local_dof_indices (dpc) - {} - }; + struct AdvectionPerTaskData + { + FullMatrix local_advection; + std::vector local_dof_indices; + AdvectionPerTaskData (const unsigned int dpc) + : + local_advection (dpc, dpc), + local_dof_indices (dpc) + {} + }; - struct AdvectionScratchData - { - unsigned int nqp; - unsigned int dpc; - std::vector< Point > u_star_local; - std::vector< Tensor<1,dim> > grad_u_star; - std::vector u_star_tmp; - FEValues fe_val; - AdvectionScratchData (const FE_Q &fe, - const QGauss &quad, - const UpdateFlags flags) - : - nqp (quad.size()), - dpc (fe.dofs_per_cell), - u_star_local (nqp), - grad_u_star (nqp), - u_star_tmp (nqp), - fe_val (fe, quad, flags) - {} - - AdvectionScratchData (const AdvectionScratchData &data) - : - nqp (data.nqp), - dpc (data.dpc), - u_star_local (nqp), - grad_u_star (nqp), - u_star_tmp (nqp), - fe_val (data.fe_val.get_fe(), - data.fe_val.get_quadrature(), - data.fe_val.get_update_flags()) - {} - }; + struct AdvectionScratchData + { + unsigned int nqp; + unsigned int dpc; + std::vector< Point > u_star_local; + std::vector< Tensor<1,dim> > grad_u_star; + std::vector u_star_tmp; + FEValues fe_val; + AdvectionScratchData (const FE_Q &fe, + const QGauss &quad, + const UpdateFlags flags) + : + nqp (quad.size()), + dpc (fe.dofs_per_cell), + u_star_local (nqp), + grad_u_star (nqp), + u_star_tmp (nqp), + fe_val (fe, quad, flags) + {} + + AdvectionScratchData (const AdvectionScratchData &data) + : + nqp (data.nqp), + dpc (data.dpc), + u_star_local (nqp), + grad_u_star (nqp), + u_star_tmp (nqp), + fe_val (data.fe_val.get_fe(), + data.fe_val.get_quadrature(), + data.fe_val.get_update_flags()) + {} + }; - void assemble_one_cell_of_advection (const typename DoFHandler::active_cell_iterator &cell, - AdvectionScratchData &scratch, - AdvectionPerTaskData &data); + void assemble_one_cell_of_advection (const typename DoFHandler::active_cell_iterator &cell, + AdvectionScratchData &scratch, + AdvectionPerTaskData &data); - void copy_advection_local_to_global (const AdvectionPerTaskData &data); + void copy_advection_local_to_global (const AdvectionPerTaskData &data); - // The final few functions implement the - // diffusion solve as well as - // postprocessing the output, including - // computing the curl of the velocity: - void diffusion_component_solve (const unsigned int d); + // The final few functions implement the + // diffusion solve as well as + // postprocessing the output, including + // computing the curl of the velocity: + void diffusion_component_solve (const unsigned int d); - void output_results (const unsigned int step); + void output_results (const unsigned int step); - void assemble_vorticity (const bool reinit_prec); + void assemble_vorticity (const bool reinit_prec); }; - // @sect4{ NavierStokesProjection::NavierStokesProjection } + // @sect4{ NavierStokesProjection::NavierStokesProjection } - // In the constructor, we just read - // all the data from the - // Data_Storage object - // that is passed as an argument, - // verify that the data we read is - // reasonable and, finally, create - // the triangulation and load the - // initial data. + // In the constructor, we just read + // all the data from the + // Data_Storage object + // that is passed as an argument, + // verify that the data we read is + // reasonable and, finally, create + // the triangulation and load the + // initial data. template NavierStokesProjection::NavierStokesProjection(const RunTimeParameters::Data_Storage &data) - : - type (data.form), - deg (data.pressure_degree), - dt (data.dt), - t_0 (data.initial_time), - T (data.final_time), - Re (data.Reynolds), - vel_exact (data.initial_time), - fe_velocity (deg+1), - fe_pressure (deg), - dof_handler_velocity (triangulation), - dof_handler_pressure (triangulation), - quadrature_pressure (deg+1), - quadrature_velocity (deg+2), - vel_max_its (data.vel_max_iterations), - vel_Krylov_size (data.vel_Krylov_size), - vel_off_diagonals (data.vel_off_diagonals), - vel_update_prec (data.vel_update_prec), - vel_eps (data.vel_eps), - vel_diag_strength (data.vel_diag_strength) + : + type (data.form), + deg (data.pressure_degree), + dt (data.dt), + t_0 (data.initial_time), + T (data.final_time), + Re (data.Reynolds), + vel_exact (data.initial_time), + fe_velocity (deg+1), + fe_pressure (deg), + dof_handler_velocity (triangulation), + dof_handler_pressure (triangulation), + quadrature_pressure (deg+1), + quadrature_velocity (deg+2), + vel_max_its (data.vel_max_iterations), + vel_Krylov_size (data.vel_Krylov_size), + vel_off_diagonals (data.vel_off_diagonals), + vel_update_prec (data.vel_update_prec), + vel_eps (data.vel_eps), + vel_diag_strength (data.vel_diag_strength) { - if(deg < 1) + if (deg < 1) std::cout << " WARNING: The chosen pair of finite element spaces is not stable." << std::endl << " The obtained results will be nonsense" @@ -730,17 +730,17 @@ namespace Step35 } - // @sect4{ NavierStokesProjection::create_triangulation_and_dofs } + // @sect4{ NavierStokesProjection::create_triangulation_and_dofs } - // The method that creates the - // triangulation and refines it the - // needed number of times. After - // creating the triangulation, it - // creates the mesh dependent data, - // i.e. it distributes degrees of - // freedom and renumbers them, and - // initializes the matrices and - // vectors that we will use. + // The method that creates the + // triangulation and refines it the + // needed number of times. After + // creating the triangulation, it + // creates the mesh dependent data, + // i.e. it distributes degrees of + // freedom and renumbers them, and + // initializes the matrices and + // vectors that we will use. template void NavierStokesProjection:: @@ -778,7 +778,7 @@ namespace Step35 phi_n.reinit (dof_handler_pressure.n_dofs()); phi_n_minus_1.reinit (dof_handler_pressure.n_dofs()); pres_tmp.reinit (dof_handler_pressure.n_dofs()); - for(unsigned int d=0; dNavierStokesProjection::initialize } + // @sect4{ NavierStokesProjection::initialize } - // This method creates the constant - // matrices and loads the initial - // data + // This method creates the constant + // matrices and loads the initial + // data template void NavierStokesProjection::initialize() @@ -817,7 +817,7 @@ namespace Step35 VectorTools::interpolate (dof_handler_pressure, pres, pres_n); phi_n = 0.; phi_n_minus_1 = 0.; - for(unsigned int d=0; dNavierStokesProjection::initialize_*_matrices methods } - - // In this set of methods we initialize the - // sparsity patterns, the constraints (if - // any) and assemble the matrices that do not - // depend on the timestep - // dt. Note that for the Laplace - // and mass matrices, we can use functions in - // the library that do this. Because the - // expensive operations of this function -- - // creating the two matrices -- are entirely - // independent, we could in principle mark - // them as tasks that can be worked on in - // %parallel using the Threads::new_task - // functions. We won't do that here since - // these functions internally already are - // parallelized, and in particular because - // the current function is only called once - // per program run and so does not incur a - // cost in each time step. The necessary - // modifications would be quite - // straightforward, however. + // @sect4{ The NavierStokesProjection::initialize_*_matrices methods } + + // In this set of methods we initialize the + // sparsity patterns, the constraints (if + // any) and assemble the matrices that do not + // depend on the timestep + // dt. Note that for the Laplace + // and mass matrices, we can use functions in + // the library that do this. Because the + // expensive operations of this function -- + // creating the two matrices -- are entirely + // independent, we could in principle mark + // them as tasks that can be worked on in + // %parallel using the Threads::new_task + // functions. We won't do that here since + // these functions internally already are + // parallelized, and in particular because + // the current function is only called once + // per program run and so does not incur a + // cost in each time step. The necessary + // modifications would be quite + // straightforward, however. template void NavierStokesProjection::initialize_velocity_matrices() @@ -876,9 +876,9 @@ namespace Step35 vel_Laplace); } - // The initialization of the matrices - // that act on the pressure space is similar - // to the ones that act on the velocity space. + // The initialization of the matrices + // that act on the pressure space is similar + // to the ones that act on the velocity space. template void NavierStokesProjection::initialize_pressure_matrices() @@ -902,20 +902,20 @@ namespace Step35 } - // For the gradient operator, we - // start by initializing the sparsity - // pattern and compressing it. It is - // important to notice here that the - // gradient operator acts from the - // pressure space into the velocity - // space, so we have to deal with two - // different finite element - // spaces. To keep the loops - // synchronized, we use the - // typedef's that we - // have defined before, namely - // PairedIterators and - // IteratorPair. + // For the gradient operator, we + // start by initializing the sparsity + // pattern and compressing it. It is + // important to notice here that the + // gradient operator acts from the + // pressure space into the velocity + // space, so we have to deal with two + // different finite element + // spaces. To keep the loops + // synchronized, we use the + // typedef's that we + // have defined before, namely + // PairedIterators and + // IteratorPair. template void NavierStokesProjection::initialize_gradient_operator() @@ -942,18 +942,18 @@ namespace Step35 per_task_data.d = d; WorkStream::run (IteratorPair (IteratorTuple (dof_handler_velocity.begin_active(), dof_handler_pressure.begin_active() - ) - ), + ) + ), IteratorPair (IteratorTuple (dof_handler_velocity.end(), dof_handler_pressure.end() - ) - ), + ) + ), *this, &NavierStokesProjection::assemble_one_cell_of_gradient, &NavierStokesProjection::copy_gradient_local_to_global, scratch_data, per_task_data - ); + ); } } @@ -994,40 +994,40 @@ namespace Step35 } - // @sect4{ NavierStokesProjection::run } - - // This is the time marching - // function, which starting at - // t_0 advances in time - // using the projection method with - // time step dt until - // T. - // - // Its second parameter, verbose - // indicates whether the function should - // output information what it is doing at any - // given moment: for example, it will say - // whether we are working on the diffusion, - // projection substep; updating - // preconditioners etc. Rather than - // implementing this output using code like - // @code - // if (verbose) - // std::cout << "something"; - // @endcode - // we use the ConditionalOStream class to - // do that for us. That class takes an - // output stream and a condition that - // indicates whether the things you pass - // to it should be passed through to the - // given output stream, or should just - // be ignored. This way, above code - // simply becomes - // @code - // verbose_cout << "something"; - // @endcode - // and does the right thing in either - // case. + // @sect4{ NavierStokesProjection::run } + + // This is the time marching + // function, which starting at + // t_0 advances in time + // using the projection method with + // time step dt until + // T. + // + // Its second parameter, verbose + // indicates whether the function should + // output information what it is doing at any + // given moment: for example, it will say + // whether we are working on the diffusion, + // projection substep; updating + // preconditioners etc. Rather than + // implementing this output using code like + // @code + // if (verbose) + // std::cout << "something"; + // @endcode + // we use the ConditionalOStream class to + // do that for us. That class takes an + // output stream and a condition that + // indicates whether the things you pass + // to it should be passed through to the + // given output stream, or should just + // be ignored. This way, above code + // simply becomes + // @code + // verbose_cout << "something"; + // @endcode + // and does the right thing in either + // case. template void NavierStokesProjection::run (const bool verbose, @@ -1074,28 +1074,28 @@ namespace Step35 } - // @sect4{NavierStokesProjection::diffusion_step} - - // The implementation of a diffusion - // step. Note that the expensive operation is - // the diffusion solve at the end of the - // function, which we have to do once for - // each velocity component. To accellerate - // things a bit, we allow to do this in - // %parallel, using the Threads::new_task - // function which makes sure that the - // dim solves are all taken care - // of and are scheduled to available - // processors: if your machine has more than - // one processor core and no other parts of - // this program are using resources - // currently, then the diffusion solves will - // run in %parallel. On the other hand, if - // your system has only one processor core - // then running things in %parallel would be - // inefficient (since it leads, for example, - // to cache congestion) and things will be - // executed sequentially. + // @sect4{NavierStokesProjection::diffusion_step} + + // The implementation of a diffusion + // step. Note that the expensive operation is + // the diffusion solve at the end of the + // function, which we have to do once for + // each velocity component. To accellerate + // things a bit, we allow to do this in + // %parallel, using the Threads::new_task + // function which makes sure that the + // dim solves are all taken care + // of and are scheduled to available + // processors: if your machine has more than + // one processor core and no other parts of + // this program are using resources + // currently, then the diffusion solves will + // run in %parallel. On the other hand, if + // your system has only one processor core + // then running things in %parallel would be + // inefficient (since it leads, for example, + // to cache congestion) and things will be + // executed sequentially. template void NavierStokesProjection::diffusion_step (const bool reinit_prec) @@ -1119,43 +1119,43 @@ namespace Step35 vel_exact.set_component(d); boundary_values.clear(); for (std::vector::const_iterator - boundaries = boundary_indicators.begin(); + boundaries = boundary_indicators.begin(); boundaries != boundary_indicators.end(); ++boundaries) { switch (*boundaries) { - case 1: - VectorTools:: - interpolate_boundary_values (dof_handler_velocity, - *boundaries, - ZeroFunction(), - boundary_values); - break; - case 2: - VectorTools:: - interpolate_boundary_values (dof_handler_velocity, - *boundaries, - vel_exact, - boundary_values); - break; - case 3: - if (d != 0) - VectorTools:: - interpolate_boundary_values (dof_handler_velocity, - *boundaries, - ZeroFunction(), - boundary_values); - break; - case 4: - VectorTools:: - interpolate_boundary_values (dof_handler_velocity, - *boundaries, - ZeroFunction(), - boundary_values); - break; - default: - Assert (false, ExcNotImplemented()); + case 1: + VectorTools:: + interpolate_boundary_values (dof_handler_velocity, + *boundaries, + ZeroFunction(), + boundary_values); + break; + case 2: + VectorTools:: + interpolate_boundary_values (dof_handler_velocity, + *boundaries, + vel_exact, + boundary_values); + break; + case 3: + if (d != 0) + VectorTools:: + interpolate_boundary_values (dof_handler_velocity, + *boundaries, + ZeroFunction(), + boundary_values); + break; + case 4: + VectorTools:: + interpolate_boundary_values (dof_handler_velocity, + *boundaries, + ZeroFunction(), + boundary_values); + break; + default: + Assert (false, ExcNotImplemented()); } } MatrixTools::apply_boundary_values (boundary_values, @@ -1166,7 +1166,7 @@ namespace Step35 Threads::TaskGroup tasks; - for(unsigned int d=0; dNavierStokesProjection::assemble_advection_term method and related} + // @sect4{ The NavierStokesProjection::assemble_advection_term method and related} - // The following few functions deal with - // assembling the advection terms, which is the part of the - // system matrix for the diffusion step that changes - // at every time step. As mentioned above, we - // will run the assembly loop over all cells - // in %parallel, using the WorkStream class - // and other facilities as described in the - // documentation module on @ref threads. + // The following few functions deal with + // assembling the advection terms, which is the part of the + // system matrix for the diffusion step that changes + // at every time step. As mentioned above, we + // will run the assembly loop over all cells + // in %parallel, using the WorkStream class + // and other facilities as described in the + // documentation module on @ref threads. template void NavierStokesProjection::assemble_advection_term() @@ -1282,9 +1282,9 @@ namespace Step35 - // @sect4{NavierStokesProjection::projection_step} + // @sect4{NavierStokesProjection::projection_step} - // This implements the projection step: + // This implements the projection step: template void NavierStokesProjection::projection_step (const bool reinit_prec) @@ -1307,7 +1307,7 @@ namespace Step35 if (reinit_prec) prec_pres_Laplace.initialize(pres_iterative, SparseILU::AdditionalData (vel_diag_strength, - vel_off_diagonals) ); + vel_off_diagonals) ); SolverControl solvercontrol (vel_max_its, vel_eps*pres_tmp.l2_norm()); SolverCG<> cg (solvercontrol); @@ -1317,19 +1317,19 @@ namespace Step35 } - // @sect4{ NavierStokesProjection::update_pressure } + // @sect4{ NavierStokesProjection::update_pressure } - // This is the pressure update step - // of the projection method. It - // implements the standard - // formulation of the method, that is - // @f[ - // p^{n+1} = p^n + \phi^{n+1}, - // @f] - // or the rotational form, which is - // @f[ - // p^{n+1} = p^n + \phi^{n+1} - \frac{1}{Re} \nabla\cdot u^{n+1}. - // @f] + // This is the pressure update step + // of the projection method. It + // implements the standard + // formulation of the method, that is + // @f[ + // p^{n+1} = p^n + \phi^{n+1}, + // @f] + // or the rotational form, which is + // @f[ + // p^{n+1} = p^n + \phi^{n+1} - \frac{1}{Re} \nabla\cdot u^{n+1}. + // @f] template void NavierStokesProjection::update_pressure (const bool reinit_prec) @@ -1337,56 +1337,56 @@ namespace Step35 pres_n_minus_1 = pres_n; switch (type) { - case RunTimeParameters::METHOD_STANDARD: - pres_n += phi_n; - break; - case RunTimeParameters::METHOD_ROTATIONAL: - if (reinit_prec) - prec_mass.initialize (pres_Mass); - pres_n = pres_tmp; - prec_mass.solve (pres_n); - pres_n.sadd(1./Re, 1., pres_n_minus_1, 1., phi_n); - break; - default: - Assert (false, ExcNotImplemented()); + case RunTimeParameters::METHOD_STANDARD: + pres_n += phi_n; + break; + case RunTimeParameters::METHOD_ROTATIONAL: + if (reinit_prec) + prec_mass.initialize (pres_Mass); + pres_n = pres_tmp; + prec_mass.solve (pres_n); + pres_n.sadd(1./Re, 1., pres_n_minus_1, 1., phi_n); + break; + default: + Assert (false, ExcNotImplemented()); }; } - // @sect4{ NavierStokesProjection::output_results } - - // This method plots the current - // solution. The main difficulty is that we - // want to create a single output file that - // contains the data for all velocity - // components, the pressure, and also the - // vorticity of the flow. On the other hand, - // velocities and the pressure live on - // separate DoFHandler objects, and so can't - // be written to the same file using a single - // DataOut object. As a consequence, we have - // to work a bit harder to get the various - // pieces of data into a single DoFHandler - // object, and then use that to drive - // graphical output. - // - // We will not elaborate on this process - // here, but rather refer to step-31 and - // step-32, where a similar procedure is used - // (and is documented) to create a joint - // DoFHandler object for all variables. - // - // Let us also note that we here compute the - // vorticity as a scalar quantity in a - // separate function, using the $L^2$ - // projection of the quantity $\text{curl} u$ - // onto the finite element space used for the - // components of the velocity. In principle, - // however, we could also have computed as a - // pointwise quantity from the velocity, and - // do so through the DataPostprocessor - // mechanism discussed in step-29 and - // step-33. + // @sect4{ NavierStokesProjection::output_results } + + // This method plots the current + // solution. The main difficulty is that we + // want to create a single output file that + // contains the data for all velocity + // components, the pressure, and also the + // vorticity of the flow. On the other hand, + // velocities and the pressure live on + // separate DoFHandler objects, and so can't + // be written to the same file using a single + // DataOut object. As a consequence, we have + // to work a bit harder to get the various + // pieces of data into a single DoFHandler + // object, and then use that to drive + // graphical output. + // + // We will not elaborate on this process + // here, but rather refer to step-31 and + // step-32, where a similar procedure is used + // (and is documented) to create a joint + // DoFHandler object for all variables. + // + // Let us also note that we here compute the + // vorticity as a scalar quantity in a + // separate function, using the $L^2$ + // projection of the quantity $\text{curl} u$ + // onto the finite element space used for the + // components of the velocity. In principle, + // however, we could also have computed as a + // pointwise quantity from the velocity, and + // do so through the DataPostprocessor + // mechanism discussed in step-29 and + // step-33. template void NavierStokesProjection::output_results (const unsigned int step) { @@ -1402,42 +1402,42 @@ namespace Step35 ExcInternalError()); static Vector joint_solution (joint_dof_handler.n_dofs()); std::vector loc_joint_dof_indices (joint_fe.dofs_per_cell), - loc_vel_dof_indices (fe_velocity.dofs_per_cell), - loc_pres_dof_indices (fe_pressure.dofs_per_cell); + loc_vel_dof_indices (fe_velocity.dofs_per_cell), + loc_pres_dof_indices (fe_pressure.dofs_per_cell); typename DoFHandler::active_cell_iterator - joint_cell = joint_dof_handler.begin_active(), - joint_endc = joint_dof_handler.end(), - vel_cell = dof_handler_velocity.begin_active(), - pres_cell = dof_handler_pressure.begin_active(); + joint_cell = joint_dof_handler.begin_active(), + joint_endc = joint_dof_handler.end(), + vel_cell = dof_handler_velocity.begin_active(), + pres_cell = dof_handler_pressure.begin_active(); for (; joint_cell != joint_endc; ++joint_cell, ++vel_cell, ++pres_cell) { joint_cell->get_dof_indices (loc_joint_dof_indices); vel_cell->get_dof_indices (loc_vel_dof_indices), - pres_cell->get_dof_indices (loc_pres_dof_indices); + pres_cell->get_dof_indices (loc_pres_dof_indices); for (unsigned int i=0; i joint_solution_names (dim, "v"); @@ -1446,8 +1446,8 @@ namespace Step35 DataOut data_out; data_out.attach_dof_handler (joint_dof_handler); std::vector< DataComponentInterpretation::DataComponentInterpretation > - component_interpretation (dim+2, - DataComponentInterpretation::component_is_part_of_vector); + component_interpretation (dim+2, + DataComponentInterpretation::component_is_part_of_vector); component_interpretation[dim] = DataComponentInterpretation::component_is_scalar; component_interpretation[dim+1] @@ -1465,20 +1465,20 @@ namespace Step35 - // Following is the helper function that - // computes the vorticity by projecting the - // term $\text{curl} u$ onto the finite - // element space used for the components of - // the velocity. The function is only called - // whenever we generate graphical output, so - // not very often, and as a consequence we - // didn't bother parallelizing it using the - // WorkStream concept as we do for the other - // assembly functions. That should not be - // overly complicated, however, if - // needed. Moreover, the implementation that - // we have here only works for 2d, so we bail - // if that is not the case. + // Following is the helper function that + // computes the vorticity by projecting the + // term $\text{curl} u$ onto the finite + // element space used for the components of + // the velocity. The function is only called + // whenever we generate graphical output, so + // not very often, and as a consequence we + // didn't bother parallelizing it using the + // WorkStream concept as we do for the other + // assembly functions. That should not be + // overly complicated, however, if + // needed. Moreover, the implementation that + // we have here only works for 2d, so we bail + // if that is not the case. template void NavierStokesProjection::assemble_vorticity (const bool reinit_prec) { @@ -1499,8 +1499,8 @@ namespace Step35 rot_u = 0.; typename DoFHandler::active_cell_iterator - cell = dof_handler_velocity.begin_active(), - end = dof_handler_velocity.end(); + cell = dof_handler_velocity.begin_active(), + end = dof_handler_velocity.end(); for (; cell != end; ++cell) { fe_val_vel.reinit (cell); @@ -1523,11 +1523,11 @@ namespace Step35 } - // @sect3{ The main function } +// @sect3{ The main function } - // The main function looks very much like in - // all the other tutorial programs, so there - // is little to comment on here: +// The main function looks very much like in +// all the other tutorial programs, so there +// is little to comment on here: int main() { try diff --git a/deal.II/examples/step-36/step-36.cc b/deal.II/examples/step-36/step-36.cc index 932c5990ae..0e32932b43 100644 --- a/deal.II/examples/step-36/step-36.cc +++ b/deal.II/examples/step-36/step-36.cc @@ -9,15 +9,15 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} - - // As mentioned in the introduction, this - // program is essentially only a slightly - // revised version of step-4. As a - // consequence, most of the following include - // files are as used there, or at least as - // used already in previous tutorial - // programs: +// @sect3{Include files} + +// As mentioned in the introduction, this +// program is essentially only a slightly +// revised version of step-4. As a +// consequence, most of the following include +// files are as used there, or at least as +// used already in previous tutorial +// programs: #include #include #include @@ -38,103 +38,103 @@ #include #include - // PETSc appears here because SLEPc - // depends on this library: +// PETSc appears here because SLEPc +// depends on this library: #include #include - // And then we need to actually - // import the interfaces for solvers - // that SLEPc provides: +// And then we need to actually +// import the interfaces for solvers +// that SLEPc provides: #include - // We also need some standard C++: +// We also need some standard C++: #include #include - // Finally, as in previous programs, we - // import all the deal.II class and function - // names into the namespace into which - // everything in this program will go: +// Finally, as in previous programs, we +// import all the deal.II class and function +// names into the namespace into which +// everything in this program will go: namespace Step36 { using namespace dealii; - // @sect3{The EigenvalueProblem class template} + // @sect3{The EigenvalueProblem class template} - // Following is the class declaration - // for the main class template. It - // looks pretty much exactly like - // what has already been shown in - // step-4: + // Following is the class declaration + // for the main class template. It + // looks pretty much exactly like + // what has already been shown in + // step-4: template class EigenvalueProblem { - public: - EigenvalueProblem (const std::string &prm_file); - void run (); - - private: - void make_grid_and_dofs (); - void assemble_system (); - void solve (); - void output_results () const; - - Triangulation triangulation; - FE_Q fe; - DoFHandler dof_handler; - - // With these exceptions: For our - // eigenvalue problem, we need - // both a stiffness matrix for - // the left hand side as well as - // a mass matrix for the right - // hand side. We also need not - // just one solution function, - // but a whole set of these for - // the eigenfunctions we want to - // compute, along with the - // corresponding eigenvalues: - PETScWrappers::SparseMatrix stiffness_matrix, mass_matrix; - std::vector eigenfunctions; - std::vector eigenvalues; - - // And then we need an object - // that will store several - // run-time parameters that we - // will specify in an input file: - ParameterHandler parameters; - - // Finally, we will have an - // object that contains - // "constraints" on our degrees - // of freedom. This could include - // hanging node constraints if we - // had adaptively refined meshes - // (which we don't have in the - // current program). Here, we - // will store the constraints for - // boundary nodes $U_i=0$. - ConstraintMatrix constraints; + public: + EigenvalueProblem (const std::string &prm_file); + void run (); + + private: + void make_grid_and_dofs (); + void assemble_system (); + void solve (); + void output_results () const; + + Triangulation triangulation; + FE_Q fe; + DoFHandler dof_handler; + + // With these exceptions: For our + // eigenvalue problem, we need + // both a stiffness matrix for + // the left hand side as well as + // a mass matrix for the right + // hand side. We also need not + // just one solution function, + // but a whole set of these for + // the eigenfunctions we want to + // compute, along with the + // corresponding eigenvalues: + PETScWrappers::SparseMatrix stiffness_matrix, mass_matrix; + std::vector eigenfunctions; + std::vector eigenvalues; + + // And then we need an object + // that will store several + // run-time parameters that we + // will specify in an input file: + ParameterHandler parameters; + + // Finally, we will have an + // object that contains + // "constraints" on our degrees + // of freedom. This could include + // hanging node constraints if we + // had adaptively refined meshes + // (which we don't have in the + // current program). Here, we + // will store the constraints for + // boundary nodes $U_i=0$. + ConstraintMatrix constraints; }; - // @sect3{Implementation of the EigenvalueProblem class} + // @sect3{Implementation of the EigenvalueProblem class} - // @sect4{EigenvalueProblem::EigenvalueProblem} + // @sect4{EigenvalueProblem::EigenvalueProblem} - // First up, the constructor. The - // main new part is handling the - // run-time input parameters. We need - // to declare their existence first, - // and then read their values from - // the input file whose name is - // specified as an argument to this - // function: + // First up, the constructor. The + // main new part is handling the + // run-time input parameters. We need + // to declare their existence first, + // and then read their values from + // the input file whose name is + // specified as an argument to this + // function: template EigenvalueProblem::EigenvalueProblem (const std::string &prm_file) - : - fe (1), - dof_handler (triangulation) + : + fe (1), + dof_handler (triangulation) { parameters.declare_entry ("Global mesh refinement steps", "5", Patterns::Integer (0, 20), @@ -152,71 +152,71 @@ namespace Step36 } - // @sect4{EigenvalueProblem::make_grid_and_dofs} - - // The next function creates a mesh - // on the domain $[-1,1]^d$, refines - // it as many times as the input file - // calls for, and then attaches a - // DoFHandler to it and initializes - // the matrices and vectors to their - // correct sizes. We also build the - // constraints that correspond to the - // boundary values - // $u|_{\partial\Omega}=0$. - // - // For the matrices, we use the PETSc - // wrappers. These have the ability - // to allocate memory as necessary as - // non-zero entries are added. This - // seems inefficient: we could as - // well first compute the sparsity - // pattern, initialize the matrices - // with it, and as we then insert - // entries we can be sure that we do - // not need to re-allocate memory and - // free the one used previously. One - // way to do that would be to use - // code like this: - // @code - // CompressedSimpleSparsityPattern - // csp (dof_handler.n_dofs(), - // dof_handler.n_dofs()); - // DoFTools::make_sparsity_pattern (dof_handler, csp); - // csp.compress (); - // stiffness_matrix.reinit (csp); - // mass_matrix.reinit (csp); - // @endcode - // instead of the two - // reinit() calls for - // the stiffness and mass matrices - // below. - // - // This doesn't quite work, - // unfortunately. The code above may - // lead to a few entries in the - // non-zero pattern to which we only - // ever write zero entries; most - // notably, this holds true for - // off-diagonal entries for those - // rows and columns that belong to - // boundary nodes. This shouldn't be - // a problem, but for whatever - // reason, PETSc's ILU - // preconditioner, which we use to - // solve linear systems in the - // eigenvalue solver, doesn't like - // these extra entries and aborts - // with an error message. - // - // In the absence of any obvious way - // to avoid this, we simply settle - // for the second best option, which - // is have PETSc allocate memory as - // necessary. That said, since this - // is not a time critical part, this - // whole affair is of no further - // importance. + // @sect4{EigenvalueProblem::make_grid_and_dofs} + + // The next function creates a mesh + // on the domain $[-1,1]^d$, refines + // it as many times as the input file + // calls for, and then attaches a + // DoFHandler to it and initializes + // the matrices and vectors to their + // correct sizes. We also build the + // constraints that correspond to the + // boundary values + // $u|_{\partial\Omega}=0$. + // + // For the matrices, we use the PETSc + // wrappers. These have the ability + // to allocate memory as necessary as + // non-zero entries are added. This + // seems inefficient: we could as + // well first compute the sparsity + // pattern, initialize the matrices + // with it, and as we then insert + // entries we can be sure that we do + // not need to re-allocate memory and + // free the one used previously. One + // way to do that would be to use + // code like this: + // @code + // CompressedSimpleSparsityPattern + // csp (dof_handler.n_dofs(), + // dof_handler.n_dofs()); + // DoFTools::make_sparsity_pattern (dof_handler, csp); + // csp.compress (); + // stiffness_matrix.reinit (csp); + // mass_matrix.reinit (csp); + // @endcode + // instead of the two + // reinit() calls for + // the stiffness and mass matrices + // below. + // + // This doesn't quite work, + // unfortunately. The code above may + // lead to a few entries in the + // non-zero pattern to which we only + // ever write zero entries; most + // notably, this holds true for + // off-diagonal entries for those + // rows and columns that belong to + // boundary nodes. This shouldn't be + // a problem, but for whatever + // reason, PETSc's ILU + // preconditioner, which we use to + // solve linear systems in the + // eigenvalue solver, doesn't like + // these extra entries and aborts + // with an error message. + // + // In the absence of any obvious way + // to avoid this, we simply settle + // for the second best option, which + // is have PETSc allocate memory as + // necessary. That said, since this + // is not a time critical part, this + // whole affair is of no further + // importance. template void EigenvalueProblem::make_grid_and_dofs () { @@ -234,16 +234,16 @@ namespace Step36 dof_handler.n_dofs(), dof_handler.max_couplings_between_dofs()); - // The next step is to take care of - // the eigenspectrum. In this case, - // the outputs are eigenvalues and - // eigenfunctions, so we set the - // size of the list of - // eigenfunctions and eigenvalues - // to be as large as we asked for - // in the input file: + // The next step is to take care of + // the eigenspectrum. In this case, + // the outputs are eigenvalues and + // eigenfunctions, so we set the + // size of the list of + // eigenfunctions and eigenvalues + // to be as large as we asked for + // in the input file: eigenfunctions - .resize (parameters.get_integer ("Number of eigenvalues/eigenfunctions")); + .resize (parameters.get_integer ("Number of eigenvalues/eigenfunctions")); for (unsigned int i=0; i void EigenvalueProblem::assemble_system () { @@ -304,8 +304,8 @@ namespace Step36 typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active (), - endc = dof_handler.end (); + cell = dof_handler.begin_active (), + endc = dof_handler.end (); for (; cell!=endc; ++cell) { fe_values.reinit (cell); @@ -320,160 +320,160 @@ namespace Step36 for (unsigned int j=0; jget_dof_indices (local_dof_indices); constraints - .distribute_local_to_global (cell_stiffness_matrix, - local_dof_indices, - stiffness_matrix); + .distribute_local_to_global (cell_stiffness_matrix, + local_dof_indices, + stiffness_matrix); constraints - .distribute_local_to_global (cell_mass_matrix, - local_dof_indices, - mass_matrix); + .distribute_local_to_global (cell_mass_matrix, + local_dof_indices, + mass_matrix); } - // At the end of the function, we - // tell PETSc that the matrices - // have now been fully assembled - // and that the sparse matrix - // representation can now be - // compressed as no more entries - // will be added: + // At the end of the function, we + // tell PETSc that the matrices + // have now been fully assembled + // and that the sparse matrix + // representation can now be + // compressed as no more entries + // will be added: stiffness_matrix.compress (); mass_matrix.compress (); } - // @sect4{EigenvalueProblem::solve} - - // This is the key new functionality - // of the program. Now that the - // system is set up, here is a good - // time to actually solve the - // problem: As with other examples - // this is done using a "solve" - // routine. Essentially, it works as - // in other programs: you set up a - // SolverControl object that - // describes the accuracy to which we - // want to solve the linear systems, - // and then we select the kind of - // solver we want. Here we choose the - // Krylov-Schur solver of SLEPc, a - // pretty fast and robust choice for - // this kind of problem: + // @sect4{EigenvalueProblem::solve} + + // This is the key new functionality + // of the program. Now that the + // system is set up, here is a good + // time to actually solve the + // problem: As with other examples + // this is done using a "solve" + // routine. Essentially, it works as + // in other programs: you set up a + // SolverControl object that + // describes the accuracy to which we + // want to solve the linear systems, + // and then we select the kind of + // solver we want. Here we choose the + // Krylov-Schur solver of SLEPc, a + // pretty fast and robust choice for + // this kind of problem: template void EigenvalueProblem::solve () { - // We start here, as we normally do, - // by assigning convergence control - // we want: + // We start here, as we normally do, + // by assigning convergence control + // we want: SolverControl solver_control (dof_handler.n_dofs(), 1e-9); SLEPcWrappers::SolverKrylovSchur eigensolver (solver_control); - // Before we actually solve for the - // eigenfunctions and -values, we - // have to also select which set of - // eigenvalues to solve for. Lets - // select those eigenvalues and - // corresponding eigenfunctions - // with the smallest real part (in - // fact, the problem we solve here - // is symmetric and so the - // eigenvalues are purely - // real). After that, we can - // actually let SLEPc do its work: + // Before we actually solve for the + // eigenfunctions and -values, we + // have to also select which set of + // eigenvalues to solve for. Lets + // select those eigenvalues and + // corresponding eigenfunctions + // with the smallest real part (in + // fact, the problem we solve here + // is symmetric and so the + // eigenvalues are purely + // real). After that, we can + // actually let SLEPc do its work: eigensolver.set_which_eigenpairs (EPS_SMALLEST_REAL); eigensolver.solve (stiffness_matrix, mass_matrix, eigenvalues, eigenfunctions, eigenfunctions.size()); - // The output of the call above is - // a set of vectors and values. In - // eigenvalue problems, the - // eigenfunctions are only - // determined up to a constant that - // can be fixed pretty - // arbitrarily. Knowing nothing - // about the origin of the - // eigenvalue problem, SLEPc has no - // other choice than to normalize - // the eigenvectors to one in the - // $l_2$ (vector) - // norm. Unfortunately this norm - // has little to do with any norm - // we may be interested from a - // eigenfunction perspective: the - // $L_2(\Omega)$ norm, or maybe the - // $L_\infty(\Omega)$ norm. - // - // Let us choose the latter and - // rescale eigenfunctions so that - // they have $\|\phi_i(\mathbf - // x)\|_{L^\infty(\Omega)}=1$ - // instead of $\|\Phi\|_{l_2}=1$ - // (where $\phi_i$ is the $i$th - // eigenfunction and - // $\Phi_i$ the corresponding - // vector of nodal values). For the - // $Q_1$ elements chosen here, we - // know that the maximum of the - // function $\phi_i(\mathbf x)$ is - // attained at one of the nodes, so - // $\max_{\mathbf x}\phi_i(\mathbf - // x)=\max_j (\Phi_i)_j$, making - // the normalization in the - // $L_\infty$ norm trivial. Note - // that this doesn't work as easily - // if we had chosen $Q_k$ elements - // with $k>1$: there, the maximum - // of a function does not - // necessarily have to be attained - // at a node, and so $\max_{\mathbf - // x}\phi_i(\mathbf x)\ge\max_j - // (\Phi_i)_j$ (although the - // equality is usually nearly - // true). + // The output of the call above is + // a set of vectors and values. In + // eigenvalue problems, the + // eigenfunctions are only + // determined up to a constant that + // can be fixed pretty + // arbitrarily. Knowing nothing + // about the origin of the + // eigenvalue problem, SLEPc has no + // other choice than to normalize + // the eigenvectors to one in the + // $l_2$ (vector) + // norm. Unfortunately this norm + // has little to do with any norm + // we may be interested from a + // eigenfunction perspective: the + // $L_2(\Omega)$ norm, or maybe the + // $L_\infty(\Omega)$ norm. + // + // Let us choose the latter and + // rescale eigenfunctions so that + // they have $\|\phi_i(\mathbf + // x)\|_{L^\infty(\Omega)}=1$ + // instead of $\|\Phi\|_{l_2}=1$ + // (where $\phi_i$ is the $i$th + // eigenfunction and + // $\Phi_i$ the corresponding + // vector of nodal values). For the + // $Q_1$ elements chosen here, we + // know that the maximum of the + // function $\phi_i(\mathbf x)$ is + // attained at one of the nodes, so + // $\max_{\mathbf x}\phi_i(\mathbf + // x)=\max_j (\Phi_i)_j$, making + // the normalization in the + // $L_\infty$ norm trivial. Note + // that this doesn't work as easily + // if we had chosen $Q_k$ elements + // with $k>1$: there, the maximum + // of a function does not + // necessarily have to be attained + // at a node, and so $\max_{\mathbf + // x}\phi_i(\mathbf x)\ge\max_j + // (\Phi_i)_j$ (although the + // equality is usually nearly + // true). for (unsigned int i=0; i void EigenvalueProblem::output_results () const { @@ -486,22 +486,22 @@ namespace Step36 std::string("eigenfunction_") + Utilities::int_to_string(i)); - // The only thing worth discussing - // may be that because the potential - // is specified as a function - // expression in the input file, it - // would be nice to also have it as a - // graphical representation along - // with the eigenfunctions. The - // process to achieve this is - // relatively straightforward: we - // build an object that represents - // $V(\mathbf x)$ and then we - // interpolate this continuous - // function onto the finite element - // space. The result we also attach - // to the DataOut object for - // visualization. + // The only thing worth discussing + // may be that because the potential + // is specified as a function + // expression in the input file, it + // would be nice to also have it as a + // graphical representation along + // with the eigenfunctions. The + // process to achieve this is + // relatively straightforward: we + // build an object that represents + // $V(\mathbf x)$ and then we + // interpolate this continuous + // function onto the finite element + // space. The result we also attach + // to the DataOut object for + // visualization. Vector projected_potential (dof_handler.n_dofs()); { FunctionParser potential; @@ -519,12 +519,12 @@ namespace Step36 } - // @sect4{EigenvalueProblem::run} + // @sect4{EigenvalueProblem::run} - // This is the function which has the - // top-level control over - // everything. It is almost exactly - // the same as in step-4: + // This is the function which has the + // top-level control over + // everything. It is almost exactly + // the same as in step-4: template void EigenvalueProblem::run () { @@ -549,21 +549,21 @@ namespace Step36 } } - // @sect3{The main function} +// @sect3{The main function} int main (int argc, char **argv) { try { - // Here is another difference - // from other steps: We - // initialize the SLEPc work - // space which inherently - // initializes the PETSc work - // space, then go ahead run the - // whole program. After that is - // done, we finalize the - // SLEPc-PETSc work. + // Here is another difference + // from other steps: We + // initialize the SLEPc work + // space which inherently + // initializes the PETSc work + // space, then go ahead run the + // whole program. After that is + // done, we finalize the + // SLEPc-PETSc work. SlepcInitialize (&argc, &argv, 0, 0); { @@ -579,10 +579,10 @@ int main (int argc, char **argv) SlepcFinalize (); } - // All the while, we are watching - // out if any exceptions should - // have been generated. If that is - // so, we panic... + // All the while, we are watching + // out if any exceptions should + // have been generated. If that is + // so, we panic... catch (std::exception &exc) { std::cerr << std::endl << std::endl @@ -608,10 +608,10 @@ int main (int argc, char **argv) return 1; } - // If no exceptions are thrown, - // then we tell the program to stop - // monkeying around and exit - // nicely: + // If no exceptions are thrown, + // then we tell the program to stop + // monkeying around and exit + // nicely: std::cout << std::endl << "Job done." << std::endl; diff --git a/deal.II/examples/step-37/step-37.cc b/deal.II/examples/step-37/step-37.cc index a664f42dcc..080c0d9277 100644 --- a/deal.II/examples/step-37/step-37.cc +++ b/deal.II/examples/step-37/step-37.cc @@ -10,8 +10,8 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // First include the necessary files - // from the deal.II library. +// First include the necessary files +// from the deal.II library. #include #include #include @@ -43,10 +43,10 @@ #include #include - // This includes the data structures for the - // efficient implementation of matrix-free - // methods or more generic finite element - // operators with the class MatrixFree. +// This includes the data structures for the +// efficient implementation of matrix-free +// methods or more generic finite element +// operators with the class MatrixFree. #include #include @@ -59,169 +59,169 @@ namespace Step37 using namespace dealii; - // To be efficient, the operations - // performed in the matrix-free - // implementation require knowledge of loop - // lengths at compile time, which are given - // by the degree of the finite - // element. Hence, we collect the values of - // the two template parameters that can be - // changed at one place in the code. Of - // course, one could make the degree of the - // finite element a run-time parameter by - // compiling the computational kernels for - // all degrees that are likely (say, - // between 1 and 6) and selecting the - // appropriate kernel at run time. Here, we - // simply choose second order $Q_2$ - // elements and choose dimension 3 as - // standard. + // To be efficient, the operations + // performed in the matrix-free + // implementation require knowledge of loop + // lengths at compile time, which are given + // by the degree of the finite + // element. Hence, we collect the values of + // the two template parameters that can be + // changed at one place in the code. Of + // course, one could make the degree of the + // finite element a run-time parameter by + // compiling the computational kernels for + // all degrees that are likely (say, + // between 1 and 6) and selecting the + // appropriate kernel at run time. Here, we + // simply choose second order $Q_2$ + // elements and choose dimension 3 as + // standard. const unsigned int degree_finite_element = 2; const unsigned int dimension = 3; - // @sect3{Equation data} - - // We define a variable coefficient function - // for the Poisson problem. It is similar to - // the function in step-5 but we use the form - // $a(\mathbf x)=\frac{1}{0.05 + 2\|\bf - // x\|^2}$ instead of a discontinuous one. It - // is merely to demonstrate the possibilities - // of this implementation, rather than making - // much sense physically. We define the - // coefficient in the same way as functions - // in earlier tutorial programs. There is one - // new function, namely a @p value method - // with template argument @p number. + // @sect3{Equation data} + + // We define a variable coefficient function + // for the Poisson problem. It is similar to + // the function in step-5 but we use the form + // $a(\mathbf x)=\frac{1}{0.05 + 2\|\bf + // x\|^2}$ instead of a discontinuous one. It + // is merely to demonstrate the possibilities + // of this implementation, rather than making + // much sense physically. We define the + // coefficient in the same way as functions + // in earlier tutorial programs. There is one + // new function, namely a @p value method + // with template argument @p number. template class Coefficient : public Function { - public: - Coefficient () : Function() {} + public: + Coefficient () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - template - number value (const Point &p, - const unsigned int component = 0) const; + template + number value (const Point &p, + const unsigned int component = 0) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; - // This is the new function mentioned - // above: Evaluate the coefficient for - // abstract type @p number: It might be - // just a usual double, but it can also be - // a somewhat more complicated type that we - // call VectorizedArray. This data type is - // essentially a short array of doubles - // whose length depends on the particular - // computer system in use. For example, - // systems based on x86-64 support the - // streaming SIMD extensions (SSE), where - // the processor's vector units can process - // two doubles (or four single-precision - // floats) by one CPU instruction. Newer - // processors with support for the - // so-called advanced vector extensions - // (AVX) with 256 bit operands can use four - // doubles and eight floats, - // respectively. Vectorization is a - // single-instruct/multiple-data (SIMD) - // concept, that is, one CPU instruction is - // used to process multiple data values at - // once. Often, finite element programs do - // not use vectorization explicitly as the - // benefits of this concept are only in - // arithmetic intensive operations. The - // bulk of typical finite element workloads - // are memory bandwidth limited (operations - // on sparse matrices and vectors) where - // the additional computational power is - // useless. - // - // Behind the scenes, optimized BLAS - // packages might heavily rely on - // vectorization, though. Also, optimizing - // compilers might automatically transform - // loops involving standard code into more - // efficient vectorized form. However, the - // data flow must be very regular in order - // for compilers to produce efficient - // code. For example, already the automatic - // vectorization of the prototype operation - // that benefits from vectorization, - // matrix-matrix products, fails on most - // compilers (as of writing this tutorial - // in early 2012, neither gcc-4.6 nor the - // Intel compiler v. 12 manage to produce - // useful vectorized code for the - // FullMatrix::mmult function, and not even - // on the more simpler case where the - // matrix bounds are compile-time constants - // instead of run-time constants as in - // FullMatrix::mmult). The main reason for - // this is that the information to be - // processed at the innermost loop (that is - // where vectorization is applied) is not - // necessarily a multiple of the vector - // length, leaving parts of the resources - // unused. Moreover, the data that can - // potentially be processed together might - // not be laid out in a contiguous way in - // memory or not with the necessary - // alignment to address boundaries that are - // needed by the processor. Or the compiler - // might not be able to prove that. - // - // In the matrix-free implementation in - // deal.II, we have therefore chosen to - // apply vectorization at the level which - // is most appropriate for finite element - // computations: The cell-wise computations - // are typically exactly the same for all - // cells (except for reading from and - // writing to vectors), and hence SIMD can - // be used to process several cells at - // once. In all what follows, you can think - // of a VectorizedArray to hold data from - // several cells. For example, we evaluate - // the coefficient shown here not on a - // simple point as usually done, but we - // hand it a - // Point > - // point, which is actually a collection of - // two points in the case of SSE2. Do not - // confuse the entries in - // VectorizedArray with the - // different coordinates of the - // point. Indeed, the data is laid out such - // that p[0] returns a - // VectorizedArray, which in turn - // contains the x-coordinate for the first - // point and the second point. You may - // access the coordinates individually - // using e.g. p[0][j], j=0,1, - // but it is recommended to define - // operations on a VectorizedArray as much - // as possible in order to make use of - // vectorized operations. - // - // In the function implementation, we - // assume that the number type overloads - // basic arithmetic operations, so we just - // write the code as usual. The standard - // functions @p value and value_list that - // are virtual functions contained in the - // base class are then computed from the - // templated function with double type, in - // order to avoid duplicating code. + // This is the new function mentioned + // above: Evaluate the coefficient for + // abstract type @p number: It might be + // just a usual double, but it can also be + // a somewhat more complicated type that we + // call VectorizedArray. This data type is + // essentially a short array of doubles + // whose length depends on the particular + // computer system in use. For example, + // systems based on x86-64 support the + // streaming SIMD extensions (SSE), where + // the processor's vector units can process + // two doubles (or four single-precision + // floats) by one CPU instruction. Newer + // processors with support for the + // so-called advanced vector extensions + // (AVX) with 256 bit operands can use four + // doubles and eight floats, + // respectively. Vectorization is a + // single-instruct/multiple-data (SIMD) + // concept, that is, one CPU instruction is + // used to process multiple data values at + // once. Often, finite element programs do + // not use vectorization explicitly as the + // benefits of this concept are only in + // arithmetic intensive operations. The + // bulk of typical finite element workloads + // are memory bandwidth limited (operations + // on sparse matrices and vectors) where + // the additional computational power is + // useless. + // + // Behind the scenes, optimized BLAS + // packages might heavily rely on + // vectorization, though. Also, optimizing + // compilers might automatically transform + // loops involving standard code into more + // efficient vectorized form. However, the + // data flow must be very regular in order + // for compilers to produce efficient + // code. For example, already the automatic + // vectorization of the prototype operation + // that benefits from vectorization, + // matrix-matrix products, fails on most + // compilers (as of writing this tutorial + // in early 2012, neither gcc-4.6 nor the + // Intel compiler v. 12 manage to produce + // useful vectorized code for the + // FullMatrix::mmult function, and not even + // on the more simpler case where the + // matrix bounds are compile-time constants + // instead of run-time constants as in + // FullMatrix::mmult). The main reason for + // this is that the information to be + // processed at the innermost loop (that is + // where vectorization is applied) is not + // necessarily a multiple of the vector + // length, leaving parts of the resources + // unused. Moreover, the data that can + // potentially be processed together might + // not be laid out in a contiguous way in + // memory or not with the necessary + // alignment to address boundaries that are + // needed by the processor. Or the compiler + // might not be able to prove that. + // + // In the matrix-free implementation in + // deal.II, we have therefore chosen to + // apply vectorization at the level which + // is most appropriate for finite element + // computations: The cell-wise computations + // are typically exactly the same for all + // cells (except for reading from and + // writing to vectors), and hence SIMD can + // be used to process several cells at + // once. In all what follows, you can think + // of a VectorizedArray to hold data from + // several cells. For example, we evaluate + // the coefficient shown here not on a + // simple point as usually done, but we + // hand it a + // Point > + // point, which is actually a collection of + // two points in the case of SSE2. Do not + // confuse the entries in + // VectorizedArray with the + // different coordinates of the + // point. Indeed, the data is laid out such + // that p[0] returns a + // VectorizedArray, which in turn + // contains the x-coordinate for the first + // point and the second point. You may + // access the coordinates individually + // using e.g. p[0][j], j=0,1, + // but it is recommended to define + // operations on a VectorizedArray as much + // as possible in order to make use of + // vectorized operations. + // + // In the function implementation, we + // assume that the number type overloads + // basic arithmetic operations, so we just + // write the code as usual. The standard + // functions @p value and value_list that + // are virtual functions contained in the + // base class are then computed from the + // templated function with double type, in + // order to avoid duplicating code. template template number Coefficient::value (const Point &p, @@ -258,221 +258,221 @@ namespace Step37 - // @sect3{Matrix-free implementation} - - // The following class, called - // LaplaceOperator, - // implements the differential - // operator. For all practical - // purposes, it is a matrix, i.e., - // you can ask it for its size - // (member functions m(), - // n()) and you can apply it - // to a vector (the various - // variants of the - // vmult() - // function). The difference to a - // real matrix of course lies in - // the fact that this class doesn't - // actually store the - // elements of the matrix, - // but only knows how to compute - // the action of the operator when - // applied to a vector. - - // In this program, we want to make use of - // the data cache for finite element operator - // application that is integrated in - // deal.II. The main class that collects all - // data is called MatrixFree. It contains - // mapping information (Jacobians) and index - // relations between local and global degrees - // of freedom. It also contains constraints - // like the ones from Dirichlet boundary - // conditions (or hanging nodes, if we had - // any). Moreover, it can issue a loop over - // all cells in %parallel, where it makes - // sure that only cells are worked on that do - // not share any degree of freedom (this - // makes the loop thread-safe when writing - // into destination vectors). This is a more - // advanced strategy compared to the - // WorkStream class described in the @ref - // threads module that serializes operations - // that might not be thread-safe. Of course, - // to not destroy thread-safety, we have to - // be careful when writing into class-global - // structures. - // - // First comes the implementation of the - // matrix-free class. It provides some - // standard information we expect for - // matrices (like returning the dimensions of - // the matrix), it implements matrix-vector - // multiplications in several forms - // (transposed and untransposed), and it - // provides functions for initializing the - // structure with data. The class has three - // template arguments, one for the dimension - // (as many deal.II classes carry), one for the - // degree of the finite element (which we - // need to enable efficient computations - // through the FEEvaluation class), and one - // for the underlying scalar type. We want to use - // double numbers - // (i.e., double precision, 64-bit - // floating point) for the final - // matrix, but floats (single - // precision, 32-bit floating point - // numbers) for the multigrid level - // matrices (as that is only a - // preconditioner, and floats can - // be worked with twice as fast). - // - // In this class, we store the actual MatrixFree - // object, the variable - // coefficient that is evaluated at all - // quadrature points (so that we don't have - // to recompute it during matrix-vector - // products), and a vector that contains the - // diagonal of the matrix that we need for - // the multigrid smoother. We choose to let - // the user provide the diagonal in this - // program, but we could also integrate a - // function in this class to evaluate the - // diagonal. Unfortunately, this forces us to - // define matrix entries at two places, - // once when we evaluate the product and once - // for the diagonal, but the work is still - // much less than when we compute sparse - // matrices. - // - // As a sidenote, if we implemented - // several different operations on - // the same grid and degrees of - // freedom (like a mass matrix and - // a Laplace matrix), we would have - // to have two classes like the - // current one for each of the - // operators (maybe with a common - // base class). However, in that - // case, we would not store a - // MatrixFree object in this - // class to avoid doing the - // expensive work of pre-computing - // everything MatrixFree stores - // twice. Rather, we would keep - // this object in the main class - // and simply store a reference. - // - // @note Observe how we store the values - // for the coefficient: We use a vector - // type - // AlignedVector - // > structure. One would think that - // one can use - // std::vector - // > as well, but there are some - // technicalities with vectorization: A - // certain alignment of the data with the - // memory address boundaries is required - // (essentially, a VectorizedArray of 16 - // bytes length as in SSE needs to start at - // a memory address that is divisible by - // 16). The chosen class makes sure that - // this alignment is respected, whereas - // std::vector can in general not, which - // may lead to segmentation faults at - // strange places for some systems or - // suboptimal performance for other - // systems. + // @sect3{Matrix-free implementation} + + // The following class, called + // LaplaceOperator, + // implements the differential + // operator. For all practical + // purposes, it is a matrix, i.e., + // you can ask it for its size + // (member functions m(), + // n()) and you can apply it + // to a vector (the various + // variants of the + // vmult() + // function). The difference to a + // real matrix of course lies in + // the fact that this class doesn't + // actually store the + // elements of the matrix, + // but only knows how to compute + // the action of the operator when + // applied to a vector. + + // In this program, we want to make use of + // the data cache for finite element operator + // application that is integrated in + // deal.II. The main class that collects all + // data is called MatrixFree. It contains + // mapping information (Jacobians) and index + // relations between local and global degrees + // of freedom. It also contains constraints + // like the ones from Dirichlet boundary + // conditions (or hanging nodes, if we had + // any). Moreover, it can issue a loop over + // all cells in %parallel, where it makes + // sure that only cells are worked on that do + // not share any degree of freedom (this + // makes the loop thread-safe when writing + // into destination vectors). This is a more + // advanced strategy compared to the + // WorkStream class described in the @ref + // threads module that serializes operations + // that might not be thread-safe. Of course, + // to not destroy thread-safety, we have to + // be careful when writing into class-global + // structures. + // + // First comes the implementation of the + // matrix-free class. It provides some + // standard information we expect for + // matrices (like returning the dimensions of + // the matrix), it implements matrix-vector + // multiplications in several forms + // (transposed and untransposed), and it + // provides functions for initializing the + // structure with data. The class has three + // template arguments, one for the dimension + // (as many deal.II classes carry), one for the + // degree of the finite element (which we + // need to enable efficient computations + // through the FEEvaluation class), and one + // for the underlying scalar type. We want to use + // double numbers + // (i.e., double precision, 64-bit + // floating point) for the final + // matrix, but floats (single + // precision, 32-bit floating point + // numbers) for the multigrid level + // matrices (as that is only a + // preconditioner, and floats can + // be worked with twice as fast). + // + // In this class, we store the actual MatrixFree + // object, the variable + // coefficient that is evaluated at all + // quadrature points (so that we don't have + // to recompute it during matrix-vector + // products), and a vector that contains the + // diagonal of the matrix that we need for + // the multigrid smoother. We choose to let + // the user provide the diagonal in this + // program, but we could also integrate a + // function in this class to evaluate the + // diagonal. Unfortunately, this forces us to + // define matrix entries at two places, + // once when we evaluate the product and once + // for the diagonal, but the work is still + // much less than when we compute sparse + // matrices. + // + // As a sidenote, if we implemented + // several different operations on + // the same grid and degrees of + // freedom (like a mass matrix and + // a Laplace matrix), we would have + // to have two classes like the + // current one for each of the + // operators (maybe with a common + // base class). However, in that + // case, we would not store a + // MatrixFree object in this + // class to avoid doing the + // expensive work of pre-computing + // everything MatrixFree stores + // twice. Rather, we would keep + // this object in the main class + // and simply store a reference. + // + // @note Observe how we store the values + // for the coefficient: We use a vector + // type + // AlignedVector + // > structure. One would think that + // one can use + // std::vector + // > as well, but there are some + // technicalities with vectorization: A + // certain alignment of the data with the + // memory address boundaries is required + // (essentially, a VectorizedArray of 16 + // bytes length as in SSE needs to start at + // a memory address that is divisible by + // 16). The chosen class makes sure that + // this alignment is respected, whereas + // std::vector can in general not, which + // may lead to segmentation faults at + // strange places for some systems or + // suboptimal performance for other + // systems. template class LaplaceOperator : public Subscriptor { - public: - LaplaceOperator (); + public: + LaplaceOperator (); - void clear(); + void clear(); - void reinit (const MGDoFHandler &dof_handler, - const ConstraintMatrix &constraints, - const unsigned int level = numbers::invalid_unsigned_int); + void reinit (const MGDoFHandler &dof_handler, + const ConstraintMatrix &constraints, + const unsigned int level = numbers::invalid_unsigned_int); - unsigned int m () const; - unsigned int n () const; + unsigned int m () const; + unsigned int n () const; - void vmult (Vector &dst, - const Vector &src) const; - void Tvmult (Vector &dst, - const Vector &src) const; - void vmult_add (Vector &dst, - const Vector &src) const; - void Tvmult_add (Vector &dst, - const Vector &src) const; + void vmult (Vector &dst, + const Vector &src) const; + void Tvmult (Vector &dst, + const Vector &src) const; + void vmult_add (Vector &dst, + const Vector &src) const; + void Tvmult_add (Vector &dst, + const Vector &src) const; - number el (const unsigned int row, - const unsigned int col) const; - void set_diagonal (const Vector &diagonal); + number el (const unsigned int row, + const unsigned int col) const; + void set_diagonal (const Vector &diagonal); - std::size_t memory_consumption () const; + std::size_t memory_consumption () const; - private: - void local_apply (const MatrixFree &data, - Vector &dst, - const Vector &src, - const std::pair &cell_range) const; + private: + void local_apply (const MatrixFree &data, + Vector &dst, + const Vector &src, + const std::pair &cell_range) const; - void evaluate_coefficient(const Coefficient &function); + void evaluate_coefficient(const Coefficient &function); - MatrixFree data; - AlignedVector > coefficient; + MatrixFree data; + AlignedVector > coefficient; - Vector diagonal_values; - bool diagonal_is_available; + Vector diagonal_values; + bool diagonal_is_available; }; - // This is the constructor of the @p - // LaplaceOperator class. All it does is to - // subscribe to the general deal.II @p - // Subscriptor scheme that makes sure that we - // do not delete an object of this class as - // long as it used somewhere else, e.g. in a - // preconditioner. + // This is the constructor of the @p + // LaplaceOperator class. All it does is to + // subscribe to the general deal.II @p + // Subscriptor scheme that makes sure that we + // do not delete an object of this class as + // long as it used somewhere else, e.g. in a + // preconditioner. template LaplaceOperator::LaplaceOperator () - : - Subscriptor() + : + Subscriptor() {} - // The next functions return the - // number of rows and columns of - // the global matrix (i.e. the - // dimensions of the operator this - // class represents, the point of - // this tutorial program was, after - // all, that we don't actually - // store the elements of the rows - // and columns of this - // operator). Since the matrix is - // square, the returned numbers are - // the same. We get the number from - // the vector partitioner stored in - // the data field (a partitioner - // distributes elements of a vector - // onto a number of different - // machines if programs are run in - // %parallel; since this program is - // written to run on only a single - // machine, the partitioner will - // simply say that all elements of - // the vector -- or, in the current - // case, all rows and columns of a - // matrix -- are stored on the - // current machine). + // The next functions return the + // number of rows and columns of + // the global matrix (i.e. the + // dimensions of the operator this + // class represents, the point of + // this tutorial program was, after + // all, that we don't actually + // store the elements of the rows + // and columns of this + // operator). Since the matrix is + // square, the returned numbers are + // the same. We get the number from + // the vector partitioner stored in + // the data field (a partitioner + // distributes elements of a vector + // onto a number of different + // machines if programs are run in + // %parallel; since this program is + // written to run on only a single + // machine, the partitioner will + // simply say that all elements of + // the vector -- or, in the current + // case, all rows and columns of a + // matrix -- are stored on the + // current machine). template unsigned int LaplaceOperator::m () const @@ -501,79 +501,79 @@ namespace Step37 } - // @sect4{Initialization} - - // Once we have created the multi-grid - // dof_handler and the constraints, we can - // call the reinit function for each level - // of the multi-grid routine (and the - // active cells). The main purpose of the - // reinit function is to setup the - // MatrixFree instance for the - // problem. Also, the coefficient is - // evaluated. For this, we need to activate - // the update flag in the AdditionalData - // field of MatrixFree that enables the - // storage of quadrature point coordinates - // in real space (by default, it only - // caches data for gradients (inverse - // transposed Jacobians) and JxW - // values). Note that if we call the reinit - // function without specifying the level - // (i.e., giving level = - // numbers::invalid_unsigned_int), - // we have told the class to loop over the - // active cells. - // - // We also set one option regarding - // task parallelism. We choose to - // use the @p partition_color - // strategy, which is based on - // subdivision of cells into - // partitions where cells in - // partition $k$ (or, more - // precisely, the degrees of - // freedom on these cells) only - // interact with cells in - // partitions $k-1$, $k$, and - // $k+1$. Within each partition, - // cells are colored in such a way - // that cells with the same color - // do not share degrees of freedom - // and can, therefore, be worked on - // at the same time without - // interference. This determines a - // task dependency graph that is - // scheduled by the Intel Threading - // Building Blocks library. Another - // option would be the strategy @p - // partition_partition, which - // performs better when the grid is - // more unstructured. We could also - // manually set the size of chunks - // that form one task in the - // scheduling process by setting @p - // tasks_block_size, but the - // default strategy to let the - // function decide works well - // already. - // - // To initialize the coefficient, - // we directly give it the - // Coefficient class defined above - // and then select the method - // coefficient_function.value - // with vectorized number (which - // the compiler can deduce from the - // point data type). The use of the - // FEEvaluation class (and its - // template arguments) will be - // explained below. + // @sect4{Initialization} + + // Once we have created the multi-grid + // dof_handler and the constraints, we can + // call the reinit function for each level + // of the multi-grid routine (and the + // active cells). The main purpose of the + // reinit function is to setup the + // MatrixFree instance for the + // problem. Also, the coefficient is + // evaluated. For this, we need to activate + // the update flag in the AdditionalData + // field of MatrixFree that enables the + // storage of quadrature point coordinates + // in real space (by default, it only + // caches data for gradients (inverse + // transposed Jacobians) and JxW + // values). Note that if we call the reinit + // function without specifying the level + // (i.e., giving level = + // numbers::invalid_unsigned_int), + // we have told the class to loop over the + // active cells. + // + // We also set one option regarding + // task parallelism. We choose to + // use the @p partition_color + // strategy, which is based on + // subdivision of cells into + // partitions where cells in + // partition $k$ (or, more + // precisely, the degrees of + // freedom on these cells) only + // interact with cells in + // partitions $k-1$, $k$, and + // $k+1$. Within each partition, + // cells are colored in such a way + // that cells with the same color + // do not share degrees of freedom + // and can, therefore, be worked on + // at the same time without + // interference. This determines a + // task dependency graph that is + // scheduled by the Intel Threading + // Building Blocks library. Another + // option would be the strategy @p + // partition_partition, which + // performs better when the grid is + // more unstructured. We could also + // manually set the size of chunks + // that form one task in the + // scheduling process by setting @p + // tasks_block_size, but the + // default strategy to let the + // function decide works well + // already. + // + // To initialize the coefficient, + // we directly give it the + // Coefficient class defined above + // and then select the method + // coefficient_function.value + // with vectorized number (which + // the compiler can deduce from the + // point data type). The use of the + // FEEvaluation class (and its + // template arguments) will be + // explained below. template void LaplaceOperator::reinit (const MGDoFHandler &dof_handler, - const ConstraintMatrix &constraints, - const unsigned int level) + const ConstraintMatrix &constraints, + const unsigned int level) { typename MatrixFree::AdditionalData additional_data; additional_data.tasks_parallel_scheme = @@ -607,176 +607,176 @@ namespace Step37 - // @sect4{Local evaluation of Laplace operator} - - // Here comes the main function of this - // class, the evaluation of the - // matrix-vector product (or, in general, a - // finite element operator - // evaluation). This is done in a function - // that takes exactly four arguments, the - // MatrixFree object, the destination and - // source vectors, and a range of cells - // that are to be worked on. The method - // cell_loop in the MatrixFree - // class will internally call this function - // with some range of cells that is - // obtained by checking which cells are - // possible to work on simultaneously so - // that write operations do not cause any - // race condition. Note that the total - // range of cells as visible in this class - // is usually not equal to the number of - // (active) cells in the triangulation. In - // fact, "cell" may be the wrong term to - // begin with, since it is rather a - // collection of quadrature points from - // several cells, and the MatrixFree class - // groups the quadrature points of several - // cells into one block to enable a higher - // degree of vectorization. The number of - // such "cells" is stored in MatrixFree and - // can be queried through - // MatrixFree::get_size_info().n_macro_cells. Compared - // to the deal.II cell iterators, in this - // class all cells are laid out in a plain - // array with no direct knowledge of level - // or neighborship relations, which makes - // it possible to index the cells by - // unsigned integers. - // - // The implementation of the Laplace - // operator is quite simple: First, we need - // to create an object FEEvaluation that - // contains the computational kernels and - // has data fields to store temporary - // results (e.g. gradients evaluated on all - // quadrature points on a collection of a - // few cells). Note that temporary results - // do not use a lot of memory, and since we - // specify template arguments with the - // element order, the data is stored on the - // stack (without expensive memory - // allocation). Usually, one only needs to - // set two template arguments, the - // dimension as first argument and the - // degree of the finite element as - // the second argument (this is equal to - // the number of degrees of freedom per - // dimension minus one for FE_Q - // elements). However, here we also want to - // be able to use float numbers for the - // multigrid preconditioner, which is the - // last (fifth) template - // argument. Therefore, we cannot rely on - // the default template arguments and must - // also fill the third and fourth field, - // consequently. The third argument - // specifies the number of quadrature - // points per direction and has a default - // value equal to the degree of the element - // plus one. The fourth argument sets - // the number of components (one can also - // evaluate vector-valued functions in - // systems of PDEs, but the default is a - // scalar element), and finally the last - // argument sets the number type. - // - // Next, we loop over the given cell range and - // then we continue with the actual - // implementation: - //
    - //
  1. Tell the FEEvaluation object the - // (macro) cell we want to work on. - //
  2. Read in the values of the - // source vectors (@p read_dof_values), - // including the resolution of - // constraints. This stores - // $u_\mathrm{cell}$ as described in the - // introduction. - //
  3. Compute the unit-cell gradient - // (the evaluation of finite element - // functions). Since FEEvaluation can - // combine value computations with - // gradient computations, it uses a - // unified interface to all kinds of - // derivatives of order between zero and - // two. We only want gradients, no values - // and no second derivatives, so we set - // the function arguments to true in the - // gradient slot (second slot), and to - // false in the values slot (first slot) - // and Hessian slot (third slot). Note - // that the FEEvaluation class internally - // evaluates shape functions in an - // efficient way where one dimension is - // worked on at a time (using the tensor - // product form of shape functions and - // quadrature points as mentioned in the - // introduction). This gives complexity - // equal to $\mathcal O(d^2 (p+1)^{d+1})$ - // for polynomial degree $p$ in $d$ - // dimensions, compared to the naive - // approach with loops over all local - // degrees of freedom and quadrature - // points that is used in FEValues that - // costs $\mathcal O(d (p+1)^{2d})$. - //
  4. Next comes the application of the - // Jacobian transformation, the - // multiplication by the variable - // coefficient and the quadrature - // weight. FEEvaluation has an access - // function @p get_gradient that applies - // the Jacobian and returns the gradient - // in real space. Then, we just need to - // multiply by the (scalar) coefficient, - // and let the function @p - // submit_gradient apply the second - // Jacobian (for the test function) and - // the quadrature weight and Jacobian - // determinant (JxW). Note that the - // submitted gradient is stored in the - // same data field as where it is read - // from in @p get_gradient. Therefore, - // you need to make sure to not read from - // the same quadrature point again after - // having called @p submit_gradient on - // that particular quadrature point. In - // general, it is a good idea to copy the - // result of @p get_gradient when it is - // used more often than once. - //
  5. Next follows the summation over - // quadrature points for all test - // functions that corresponds to the - // actual integration step. For the - // Laplace operator, we just multiply by - // the gradient, so we call the integrate - // function with the respective argument - // set. If you have an equation where you - // test by both the values of the test - // functions and the gradients, both - // template arguments need to be set to - // true. Calling first the integrate - // function for values and then gradients - // in a separate call leads to wrong - // results, since the second call will - // internally overwrite the results from - // the first call. Note that there is no - // function argument for the second - // derivative for integrate step. - //
  6. Eventually, the local - // contributions in the vector - // $v_\mathrm{cell}$ as mentioned in the - // introduction need to be added into the - // result vector (and constraints are - // applied). This is done with a call to - // @p distribute_local_to_global, the - // same name as the corresponding - // function in the ConstraintMatrix (only - // that we now store the local vector in - // the FEEvaluation object, as are the - // indices between local and global - // degrees of freedom).
+ // @sect4{Local evaluation of Laplace operator} + + // Here comes the main function of this + // class, the evaluation of the + // matrix-vector product (or, in general, a + // finite element operator + // evaluation). This is done in a function + // that takes exactly four arguments, the + // MatrixFree object, the destination and + // source vectors, and a range of cells + // that are to be worked on. The method + // cell_loop in the MatrixFree + // class will internally call this function + // with some range of cells that is + // obtained by checking which cells are + // possible to work on simultaneously so + // that write operations do not cause any + // race condition. Note that the total + // range of cells as visible in this class + // is usually not equal to the number of + // (active) cells in the triangulation. In + // fact, "cell" may be the wrong term to + // begin with, since it is rather a + // collection of quadrature points from + // several cells, and the MatrixFree class + // groups the quadrature points of several + // cells into one block to enable a higher + // degree of vectorization. The number of + // such "cells" is stored in MatrixFree and + // can be queried through + // MatrixFree::get_size_info().n_macro_cells. Compared + // to the deal.II cell iterators, in this + // class all cells are laid out in a plain + // array with no direct knowledge of level + // or neighborship relations, which makes + // it possible to index the cells by + // unsigned integers. + // + // The implementation of the Laplace + // operator is quite simple: First, we need + // to create an object FEEvaluation that + // contains the computational kernels and + // has data fields to store temporary + // results (e.g. gradients evaluated on all + // quadrature points on a collection of a + // few cells). Note that temporary results + // do not use a lot of memory, and since we + // specify template arguments with the + // element order, the data is stored on the + // stack (without expensive memory + // allocation). Usually, one only needs to + // set two template arguments, the + // dimension as first argument and the + // degree of the finite element as + // the second argument (this is equal to + // the number of degrees of freedom per + // dimension minus one for FE_Q + // elements). However, here we also want to + // be able to use float numbers for the + // multigrid preconditioner, which is the + // last (fifth) template + // argument. Therefore, we cannot rely on + // the default template arguments and must + // also fill the third and fourth field, + // consequently. The third argument + // specifies the number of quadrature + // points per direction and has a default + // value equal to the degree of the element + // plus one. The fourth argument sets + // the number of components (one can also + // evaluate vector-valued functions in + // systems of PDEs, but the default is a + // scalar element), and finally the last + // argument sets the number type. + // + // Next, we loop over the given cell range and + // then we continue with the actual + // implementation: + //
    + //
  1. Tell the FEEvaluation object the + // (macro) cell we want to work on. + //
  2. Read in the values of the + // source vectors (@p read_dof_values), + // including the resolution of + // constraints. This stores + // $u_\mathrm{cell}$ as described in the + // introduction. + //
  3. Compute the unit-cell gradient + // (the evaluation of finite element + // functions). Since FEEvaluation can + // combine value computations with + // gradient computations, it uses a + // unified interface to all kinds of + // derivatives of order between zero and + // two. We only want gradients, no values + // and no second derivatives, so we set + // the function arguments to true in the + // gradient slot (second slot), and to + // false in the values slot (first slot) + // and Hessian slot (third slot). Note + // that the FEEvaluation class internally + // evaluates shape functions in an + // efficient way where one dimension is + // worked on at a time (using the tensor + // product form of shape functions and + // quadrature points as mentioned in the + // introduction). This gives complexity + // equal to $\mathcal O(d^2 (p+1)^{d+1})$ + // for polynomial degree $p$ in $d$ + // dimensions, compared to the naive + // approach with loops over all local + // degrees of freedom and quadrature + // points that is used in FEValues that + // costs $\mathcal O(d (p+1)^{2d})$. + //
  4. Next comes the application of the + // Jacobian transformation, the + // multiplication by the variable + // coefficient and the quadrature + // weight. FEEvaluation has an access + // function @p get_gradient that applies + // the Jacobian and returns the gradient + // in real space. Then, we just need to + // multiply by the (scalar) coefficient, + // and let the function @p + // submit_gradient apply the second + // Jacobian (for the test function) and + // the quadrature weight and Jacobian + // determinant (JxW). Note that the + // submitted gradient is stored in the + // same data field as where it is read + // from in @p get_gradient. Therefore, + // you need to make sure to not read from + // the same quadrature point again after + // having called @p submit_gradient on + // that particular quadrature point. In + // general, it is a good idea to copy the + // result of @p get_gradient when it is + // used more often than once. + //
  5. Next follows the summation over + // quadrature points for all test + // functions that corresponds to the + // actual integration step. For the + // Laplace operator, we just multiply by + // the gradient, so we call the integrate + // function with the respective argument + // set. If you have an equation where you + // test by both the values of the test + // functions and the gradients, both + // template arguments need to be set to + // true. Calling first the integrate + // function for values and then gradients + // in a separate call leads to wrong + // results, since the second call will + // internally overwrite the results from + // the first call. Note that there is no + // function argument for the second + // derivative for integrate step. + //
  6. Eventually, the local + // contributions in the vector + // $v_\mathrm{cell}$ as mentioned in the + // introduction need to be added into the + // result vector (and constraints are + // applied). This is done with a call to + // @p distribute_local_to_global, the + // same name as the corresponding + // function in the ConstraintMatrix (only + // that we now store the local vector in + // the FEEvaluation object, as are the + // indices between local and global + // degrees of freedom).
template void LaplaceOperator:: @@ -804,18 +804,18 @@ namespace Step37 - // @sect4{vmult functions} + // @sect4{vmult functions} - // Now to the @p vmult function that is - // called externally: In addition to what - // we do in a @p vmult_add function further - // down, we set the destination to zero - // first. The transposed matrix-vector is - // needed for well-defined multigrid - // preconditioner operations. Since we - // solve a Laplace problem, this is the - // same operation, and we just refer to the - // vmult operation. + // Now to the @p vmult function that is + // called externally: In addition to what + // we do in a @p vmult_add function further + // down, we set the destination to zero + // first. The transposed matrix-vector is + // needed for well-defined multigrid + // preconditioner operations. Since we + // solve a Laplace problem, this is the + // same operation, and we just refer to the + // vmult operation. template void LaplaceOperator::vmult (Vector &dst, @@ -848,63 +848,63 @@ namespace Step37 - // This function implements the loop over all - // cells. This is done with the @p cell_loop - // of the MatrixFree class, which takes - // the operator() of this class with arguments - // MatrixFree, OutVector, InVector, - // cell_range. Note that we could also use a - // simple function as local operation in case - // we had constant coefficients (all we need - // then is the MatrixFree, the vectors and - // the cell range), but since the coefficient - // is stored in a variable of this class, we - // cannot use that variant here. The cell loop - // is automatically performed on several threads - // if multithreading is enabled (this class - // uses a quite elaborate algorithm to work on - // cells that do not share any degrees of - // freedom that could possibly give rise to - // race conditions, using the dynamic task - // scheduler of the Intel Threading Building - // Blocks). - // - // After the cell loop, we need to touch - // the constrained degrees of freedom: - // Since the assembly loop automatically - // resolves constraints (just as the - // ConstraintMatrix::distribute_local_to_global - // call does), it does not compute any - // contribution for constrained degrees of - // freedom. In other words, the entries for - // constrained DoFs remain zero after the - // first part of this function, as if the - // matrix had empty rows and columns for - // constrained degrees of freedom. On the - // other hand, iterative solvers like CG - // only work for non-singular matrices, so - // we have to modify the operation on - // constrained DoFs. The easiest way to do - // that is to pretend that the sub-block of - // the matrix that corresponds to - // constrained DoFs is the identity matrix, - // in which case application of the matrix - // would simply copy the elements of the - // right hand side vector into the left - // hand side. In general, however, one - // needs to make sure that the diagonal - // entries of this sub-block are of the - // same order of magnitude as the diagonal - // elements of the rest of the matrix. - // Here, the domain extent is of unit size, - // so we can simply choose unit size. If we - // had domains that are far away from unit - // size, we would need to choose a number - // that is close to the size of other - // diagonal matrix entries, so that these - // artificial eigenvalues do not change the - // eigenvalue spectrum (and make - // convergence with CG more difficult). + // This function implements the loop over all + // cells. This is done with the @p cell_loop + // of the MatrixFree class, which takes + // the operator() of this class with arguments + // MatrixFree, OutVector, InVector, + // cell_range. Note that we could also use a + // simple function as local operation in case + // we had constant coefficients (all we need + // then is the MatrixFree, the vectors and + // the cell range), but since the coefficient + // is stored in a variable of this class, we + // cannot use that variant here. The cell loop + // is automatically performed on several threads + // if multithreading is enabled (this class + // uses a quite elaborate algorithm to work on + // cells that do not share any degrees of + // freedom that could possibly give rise to + // race conditions, using the dynamic task + // scheduler of the Intel Threading Building + // Blocks). + // + // After the cell loop, we need to touch + // the constrained degrees of freedom: + // Since the assembly loop automatically + // resolves constraints (just as the + // ConstraintMatrix::distribute_local_to_global + // call does), it does not compute any + // contribution for constrained degrees of + // freedom. In other words, the entries for + // constrained DoFs remain zero after the + // first part of this function, as if the + // matrix had empty rows and columns for + // constrained degrees of freedom. On the + // other hand, iterative solvers like CG + // only work for non-singular matrices, so + // we have to modify the operation on + // constrained DoFs. The easiest way to do + // that is to pretend that the sub-block of + // the matrix that corresponds to + // constrained DoFs is the identity matrix, + // in which case application of the matrix + // would simply copy the elements of the + // right hand side vector into the left + // hand side. In general, however, one + // needs to make sure that the diagonal + // entries of this sub-block are of the + // same order of magnitude as the diagonal + // elements of the rest of the matrix. + // Here, the domain extent is of unit size, + // so we can simply choose unit size. If we + // had domains that are far away from unit + // size, we would need to choose a number + // that is close to the size of other + // diagonal matrix entries, so that these + // artificial eigenvalues do not change the + // eigenvalue spectrum (and make + // convergence with CG more difficult). template void LaplaceOperator::vmult_add (Vector &dst, @@ -913,27 +913,27 @@ namespace Step37 data.cell_loop (&LaplaceOperator::local_apply, this, dst, src); const std::vector & - constrained_dofs = data.get_constrained_dofs(); + constrained_dofs = data.get_constrained_dofs(); for (unsigned int i=0; i number LaplaceOperator::el (const unsigned int row, - const unsigned int col) const + const unsigned int col) const { Assert (row == col, ExcNotImplemented()); Assert (diagonal_is_available == true, ExcNotInitialized()); @@ -942,31 +942,31 @@ namespace Step37 - // Regarding the calculation of the - // diagonal, we expect the user to - // provide a vector with the - // diagonal entries (and we will - // compute them in the code - // below). We only need it for the - // level matrices of multigrid, not - // the system matrix (since we only - // need these diagonals for the - // multigrid smoother). Since we - // fill only elements into - // unconstrained entries, we have - // to set constrained entries to - // one in order to avoid the same - // problems as discussed above. + // Regarding the calculation of the + // diagonal, we expect the user to + // provide a vector with the + // diagonal entries (and we will + // compute them in the code + // below). We only need it for the + // level matrices of multigrid, not + // the system matrix (since we only + // need these diagonals for the + // multigrid smoother). Since we + // fill only elements into + // unconstrained entries, we have + // to set constrained entries to + // one in order to avoid the same + // problems as discussed above. template void - LaplaceOperator::set_diagonal(const Vector&diagonal) + LaplaceOperator::set_diagonal(const Vector &diagonal) { AssertDimension (m(), diagonal.size()); diagonal_values = diagonal; const std::vector & - constrained_dofs = data.get_constrained_dofs(); + constrained_dofs = data.get_constrained_dofs(); for (unsigned int i=0; i std::size_t LaplaceOperator::memory_consumption () const @@ -1000,102 +1000,102 @@ namespace Step37 - // @sect3{LaplaceProblem class} - - // This class is based on the one in - // step-16. However, we replaced the - // SparseMatrix class by our - // matrix-free implementation, which means - // that we can also skip the sparsity - // patterns. Notice that we define the - // LaplaceOperator class with the degree of finite - // element as template argument (the value is - // defined at the top of the file), and that - // we use float numbers for the multigrid - // level matrices. - // - // The class also has a member variable to - // keep track of all the time we spend on - // setting up the entire chain of data - // before we actually go about solving the - // problem. In addition, there is an output - // stream (that is disabled by default) - // that can be used to output details for - // the individual setup operations instead - // of the summary only that is printed out - // by default. + // @sect3{LaplaceProblem class} + + // This class is based on the one in + // step-16. However, we replaced the + // SparseMatrix class by our + // matrix-free implementation, which means + // that we can also skip the sparsity + // patterns. Notice that we define the + // LaplaceOperator class with the degree of finite + // element as template argument (the value is + // defined at the top of the file), and that + // we use float numbers for the multigrid + // level matrices. + // + // The class also has a member variable to + // keep track of all the time we spend on + // setting up the entire chain of data + // before we actually go about solving the + // problem. In addition, there is an output + // stream (that is disabled by default) + // that can be used to output details for + // the individual setup operations instead + // of the summary only that is printed out + // by default. template class LaplaceProblem { - public: - LaplaceProblem (); - void run (); - - private: - void setup_system (); - void assemble_system (); - void assemble_multigrid (); - void solve (); - void output_results (const unsigned int cycle) const; - - typedef LaplaceOperator SystemMatrixType; - typedef LaplaceOperator LevelMatrixType; - - Triangulation triangulation; - FE_Q fe; - MGDoFHandler mg_dof_handler; - ConstraintMatrix constraints; - - SystemMatrixType system_matrix; - MGLevelObject mg_matrices; - FullMatrix coarse_matrix; - MGLevelObject mg_constraints; - - Vector solution; - Vector system_rhs; - - double setup_time; - ConditionalOStream time_details; + public: + LaplaceProblem (); + void run (); + + private: + void setup_system (); + void assemble_system (); + void assemble_multigrid (); + void solve (); + void output_results (const unsigned int cycle) const; + + typedef LaplaceOperator SystemMatrixType; + typedef LaplaceOperator LevelMatrixType; + + Triangulation triangulation; + FE_Q fe; + MGDoFHandler mg_dof_handler; + ConstraintMatrix constraints; + + SystemMatrixType system_matrix; + MGLevelObject mg_matrices; + FullMatrix coarse_matrix; + MGLevelObject mg_constraints; + + Vector solution; + Vector system_rhs; + + double setup_time; + ConditionalOStream time_details; }; - // When we initialize the finite element, we - // of course have to use the degree specified - // at the top of the file as well (otherwise, - // an exception will be thrown at some point, - // since the computational kernel defined in - // the templated LaplaceOperator class and the - // information from the finite element read - // out by MatrixFree will not match). + // When we initialize the finite element, we + // of course have to use the degree specified + // at the top of the file as well (otherwise, + // an exception will be thrown at some point, + // since the computational kernel defined in + // the templated LaplaceOperator class and the + // information from the finite element read + // out by MatrixFree will not match). template LaplaceProblem::LaplaceProblem () - : - fe (degree_finite_element), - mg_dof_handler (triangulation), - time_details (std::cout, false) + : + fe (degree_finite_element), + mg_dof_handler (triangulation), + time_details (std::cout, false) {} - // @sect4{LaplaceProblem::setup_system} - - // This is the function of step-16 with - // relevant changes due to the LaplaceOperator - // class. We do not use adaptive grids, so we - // do not have to compute edge matrices. Thus, - // all we do is to implement Dirichlet - // boundary conditions through the - // ConstraintMatrix, set up the - // (one-dimensional) quadrature that should - // be used by the matrix-free class, and call - // the initialization functions. - // - // In the process, we output data on both - // the run time of the program as well as - // on memory consumption, where we output - // memory data in megabytes (1 million - // bytes). + // @sect4{LaplaceProblem::setup_system} + + // This is the function of step-16 with + // relevant changes due to the LaplaceOperator + // class. We do not use adaptive grids, so we + // do not have to compute edge matrices. Thus, + // all we do is to implement Dirichlet + // boundary conditions through the + // ConstraintMatrix, set up the + // (one-dimensional) quadrature that should + // be used by the matrix-free class, and call + // the initialization functions. + // + // In the process, we output data on both + // the run time of the program as well as + // on memory consumption, where we output + // memory data in megabytes (1 million + // bytes). template void LaplaceProblem::setup_system () { @@ -1139,24 +1139,24 @@ namespace Step37 << time() << "s/" << time.wall_time() << "s" << std::endl; time.restart(); - // Next, initialize the matrices - // for the multigrid method on - // all the levels. The function - // MGTools::make_boundary_list - // returns for each multigrid - // level which degrees of freedom - // are located on a Dirichlet - // boundary; we force these DoFs - // to have value zero by adding - // to the ConstraintMatrix object - // a zero condition by using the - // command - // ConstraintMatrix::add_line. Once - // this is done, we close the - // ConstraintMatrix on each level - // so it can be used to read out - // indices internally in the - // MatrixFree. + // Next, initialize the matrices + // for the multigrid method on + // all the levels. The function + // MGTools::make_boundary_list + // returns for each multigrid + // level which degrees of freedom + // are located on a Dirichlet + // boundary; we force these DoFs + // to have value zero by adding + // to the ConstraintMatrix object + // a zero condition by using the + // command + // ConstraintMatrix::add_line. Once + // this is done, we close the + // ConstraintMatrix on each level + // so it can be used to read out + // indices internally in the + // MatrixFree. const unsigned int nlevels = triangulation.n_levels(); mg_matrices.resize(0, nlevels-1); mg_constraints.resize (0, nlevels-1); @@ -1168,7 +1168,7 @@ namespace Step37 MGTools::make_boundary_list (mg_dof_handler, dirichlet_boundary, boundary_indices); - for (unsigned int level=0;level::iterator bc_it = boundary_indices[level].begin(); for ( ; bc_it != boundary_indices[level].end(); ++bc_it) @@ -1188,15 +1188,15 @@ namespace Step37 - // @sect4{LaplaceProblem::assemble_system} + // @sect4{LaplaceProblem::assemble_system} - // The assemble function is significantly - // reduced compared to step-16. All we need - // to do is to assemble the right hand - // side. That is the same as in many other - // tutorial programs. In the end, we condense - // the constraints from Dirichlet boundary - // conditions away from the right hand side. + // The assemble function is significantly + // reduced compared to step-16. All we need + // to do is to assemble the right hand + // side. That is the same as in many other + // tutorial programs. In the end, we condense + // the constraints from Dirichlet boundary + // conditions away from the right hand side. template void LaplaceProblem::assemble_system () { @@ -1234,18 +1234,18 @@ namespace Step37 } - // @sect4{LaplaceProblem::assemble_multigrid} + // @sect4{LaplaceProblem::assemble_multigrid} - // Here is another assemble - // function. Again, it is simpler than - // assembling matrices. We need to compute - // the diagonal of the Laplace matrices on - // the individual levels, send the final - // matrices to the LaplaceOperator class, - // and we need to compute the full matrix - // on the coarsest level (since that is - // inverted exactly in the deal.II - // multigrid implementation). + // Here is another assemble + // function. Again, it is simpler than + // assembling matrices. We need to compute + // the diagonal of the Laplace matrices on + // the individual levels, send the final + // matrices to the LaplaceOperator class, + // and we need to compute the full matrix + // on the coarsest level (since that is + // inverted exactly in the deal.II + // multigrid implementation). template void LaplaceProblem::assemble_multigrid () { @@ -1325,19 +1325,19 @@ namespace Step37 - // @sect4{LaplaceProblem::solve} + // @sect4{LaplaceProblem::solve} - // The solution process again looks like - // step-16. We now use a Chebyshev smoother - // instead of SOR (SOR would be very - // difficult to implement because we do not - // have the matrix elements available - // explicitly, and it is difficult to make it - // work efficiently in %parallel). The - // multigrid classes provide a simple - // interface for using the Chebyshev smoother - // which is defined in a preconditioner - // class: MGSmootherPrecondition. + // The solution process again looks like + // step-16. We now use a Chebyshev smoother + // instead of SOR (SOR would be very + // difficult to implement because we do not + // have the matrix elements available + // explicitly, and it is difficult to make it + // work efficiently in %parallel). The + // multigrid classes provide a simple + // interface for using the Chebyshev smoother + // which is defined in a preconditioner + // class: MGSmootherPrecondition. template void LaplaceProblem::solve () { @@ -1360,27 +1360,27 @@ namespace Step37 typedef PreconditionChebyshev > SMOOTHER; MGSmootherPrecondition > - mg_smoother(vector_memory); - - // Then, we initialize the smoother with - // our level matrices and the mandatory - // additional data for the Chebyshev - // smoother. We use quite a high degree - // here (6), since matrix-vector products - // are comparably cheap and more parallel - // than the level-transfer operations. We - // choose to smooth out a range of $[1.2 - // \hat{\lambda}_{\max}/10,1.2 - // \hat{\lambda}_{\max}]$ in the smoother - // where $\hat{\lambda}_{\max}$ is an - // estimate of the largest eigenvalue. In - // order to compute that eigenvalue, the - // Chebyshev initializations performs a - // few steps of a CG algorithm without - // preconditioner. Since the highest - // eigenvalue is usually the easiest one - // to find and a rough estimate is enough, - // we choose 10 iterations. + mg_smoother(vector_memory); + + // Then, we initialize the smoother with + // our level matrices and the mandatory + // additional data for the Chebyshev + // smoother. We use quite a high degree + // here (6), since matrix-vector products + // are comparably cheap and more parallel + // than the level-transfer operations. We + // choose to smooth out a range of $[1.2 + // \hat{\lambda}_{\max}/10,1.2 + // \hat{\lambda}_{\max}]$ in the smoother + // where $\hat{\lambda}_{\max}$ is an + // estimate of the largest eigenvalue. In + // order to compute that eigenvalue, the + // Chebyshev initializations performs a + // few steps of a CG algorithm without + // preconditioner. Since the highest + // eigenvalue is usually the easiest one + // to find and a rough estimate is enough, + // we choose 10 iterations. typename SMOOTHER::AdditionalData smoother_data; smoother_data.smoothing_range = 10.; smoother_data.degree = 6; @@ -1388,7 +1388,7 @@ namespace Step37 mg_smoother.initialize(mg_matrices, smoother_data); MGMatrix > - mg_matrix(&mg_matrices); + mg_matrix(&mg_matrices); Multigrid > mg(mg_dof_handler, mg_matrix, @@ -1397,28 +1397,28 @@ namespace Step37 mg_smoother, mg_smoother); PreconditionMG, - MGTransferPrebuilt > > - preconditioner(mg_dof_handler, mg, mg_transfer); - - // Finally, write out the memory - // consumption of the Multigrid object - // (or rather, of its most significant - // components, since there is no built-in - // function for the total multigrid - // object), then create the solver object - // and solve the system. This is very - // easy, and we didn't even see any - // difference in the solve process - // compared to step-16. The magic is all - // hidden behind the implementation of - // the LaplaceOperator::vmult - // operation. Note that we print out the - // solve time and the accumulated setup - // time through standard out, i.e., in - // any case, whereas detailed times for - // the setup operations are only printed - // in case the flag for detail_times in - // the constructor is changed. + MGTransferPrebuilt > > + preconditioner(mg_dof_handler, mg, mg_transfer); + + // Finally, write out the memory + // consumption of the Multigrid object + // (or rather, of its most significant + // components, since there is no built-in + // function for the total multigrid + // object), then create the solver object + // and solve the system. This is very + // easy, and we didn't even see any + // difference in the solve process + // compared to step-16. The magic is all + // hidden behind the implementation of + // the LaplaceOperator::vmult + // operation. Note that we print out the + // solve time and the accumulated setup + // time through standard out, i.e., in + // any case, whereas detailed times for + // the setup operations are only printed + // in case the flag for detail_times in + // the constructor is changed. const std::size_t multigrid_memory = (mg_matrices.memory_consumption() + mg_transfer.memory_consumption() + @@ -1450,13 +1450,13 @@ namespace Step37 - // @sect4{LaplaceProblem::output_results} + // @sect4{LaplaceProblem::output_results} - // Here is the data output, which is a - // simplified version of step-5. We use the - // standard VTU (= compressed VTK) output for - // each grid produced in the refinement - // process. + // Here is the data output, which is a + // simplified version of step-5. We use the + // standard VTU (= compressed VTK) output for + // each grid produced in the refinement + // process. template void LaplaceProblem::output_results (const unsigned int cycle) const { @@ -1477,12 +1477,12 @@ namespace Step37 - // @sect4{LaplaceProblem::run} + // @sect4{LaplaceProblem::run} - // The function that runs the program is - // very similar to the one in step-16. We - // make less refinement steps in 3D - // compared to 2D, but that's it. + // The function that runs the program is + // very similar to the one in step-16. We + // make less refinement steps in 3D + // compared to 2D, but that's it. template void LaplaceProblem::run () { @@ -1508,9 +1508,9 @@ namespace Step37 - // @sect3{The main function} +// @sect3{The main function} - // This is as in most other programs. +// This is as in most other programs. int main () { try diff --git a/deal.II/examples/step-38/step-38.cc b/deal.II/examples/step-38/step-38.cc index b26b6b8db1..e473a81b27 100644 --- a/deal.II/examples/step-38/step-38.cc +++ b/deal.II/examples/step-38/step-38.cc @@ -9,13 +9,13 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // If you've read through step-4 and step-7, - // you will recognize that we have used all - // of the following include files there - // already. Consequently, we will not explain - // their meaning here again. +// If you've read through step-4 and step-7, +// you will recognize that we have used all +// of the following include files there +// already. Consequently, we will not explain +// their meaning here again. #include #include #include @@ -47,131 +47,131 @@ namespace Step38 { using namespace dealii; - // @sect3{The LaplaceBeltramiProblem class template} - - // This class is almost exactly similar to - // the LaplaceProblem class in - // step-4. - - // The essential differences are these: - // - // - The template parameter now denotes the - // dimensionality of the embedding space, - // which is no longer the same as the - // dimensionality of the domain and the - // triangulation on which we compute. We - // indicate this by calling the parameter - // @p spacedim , and introducing a constant - // @p dim equal to the dimensionality of - // the domain -- here equal to - // spacedim-1. - // - All member variables that have geometric - // aspects now need to know about both - // their own dimensionality as well as that - // of the embedding space. Consequently, we - // need to specify both of their template - // parameters one for the dimension of the - // mesh @p dim, and the other for the - // dimension of the embedding space, - // @p spacedim. This is exactly what we - // did in step-34, take a look there for - // a deeper explanation. - - // - We need an object that describes which - // kind of mapping to use from the - // reference cell to the cells that the - // triangulation is composed of. The - // classes derived from the Mapping base - // class do exactly this. Throughout most - // of deal.II, if you don't do anything at - // all, the library assumes that you want - // an object of kind MappingQ1 that uses a - // (bi-, tri-)linear mapping. In many - // cases, this is quite sufficient, which - // is why the use of these objects is - // mostly optional: for example, if you - // have a polygonal two-dimensional domain - // in two-dimensional space, a bilinear - // mapping of the reference cell to the - // cells of the triangulation yields an - // exact representation of the domain. If - // you have a curved domain, one may want - // to use a higher order mapping for those - // cells that lie at the boundary of the - // domain -- this is what we did in - // step-11, for example. However, here we - // have a curved domain, not just a curved - // boundary, and while we can approximate - // it with bilinearly mapped cells, it is - // really only prodent to use a higher - // order mapping for all - // cells. Consequently, this class has a - // member variable of type MappingQ; we - // will choose the polynomial degree of the - // mapping equal to the polynomial degree - // of the finite element used in the - // computations to ensure optimal approximation, though this - // iso-parametricity is not required. + // @sect3{The LaplaceBeltramiProblem class template} + + // This class is almost exactly similar to + // the LaplaceProblem class in + // step-4. + + // The essential differences are these: + // + // - The template parameter now denotes the + // dimensionality of the embedding space, + // which is no longer the same as the + // dimensionality of the domain and the + // triangulation on which we compute. We + // indicate this by calling the parameter + // @p spacedim , and introducing a constant + // @p dim equal to the dimensionality of + // the domain -- here equal to + // spacedim-1. + // - All member variables that have geometric + // aspects now need to know about both + // their own dimensionality as well as that + // of the embedding space. Consequently, we + // need to specify both of their template + // parameters one for the dimension of the + // mesh @p dim, and the other for the + // dimension of the embedding space, + // @p spacedim. This is exactly what we + // did in step-34, take a look there for + // a deeper explanation. + + // - We need an object that describes which + // kind of mapping to use from the + // reference cell to the cells that the + // triangulation is composed of. The + // classes derived from the Mapping base + // class do exactly this. Throughout most + // of deal.II, if you don't do anything at + // all, the library assumes that you want + // an object of kind MappingQ1 that uses a + // (bi-, tri-)linear mapping. In many + // cases, this is quite sufficient, which + // is why the use of these objects is + // mostly optional: for example, if you + // have a polygonal two-dimensional domain + // in two-dimensional space, a bilinear + // mapping of the reference cell to the + // cells of the triangulation yields an + // exact representation of the domain. If + // you have a curved domain, one may want + // to use a higher order mapping for those + // cells that lie at the boundary of the + // domain -- this is what we did in + // step-11, for example. However, here we + // have a curved domain, not just a curved + // boundary, and while we can approximate + // it with bilinearly mapped cells, it is + // really only prodent to use a higher + // order mapping for all + // cells. Consequently, this class has a + // member variable of type MappingQ; we + // will choose the polynomial degree of the + // mapping equal to the polynomial degree + // of the finite element used in the + // computations to ensure optimal approximation, though this + // iso-parametricity is not required. template class LaplaceBeltramiProblem { - public: - LaplaceBeltramiProblem (const unsigned degree = 2); - void run (); + public: + LaplaceBeltramiProblem (const unsigned degree = 2); + void run (); - private: - static const unsigned int dim = spacedim-1; + private: + static const unsigned int dim = spacedim-1; - void make_grid_and_dofs (); - void assemble_system (); - void solve (); - void output_results () const; - void compute_error () const; + void make_grid_and_dofs (); + void assemble_system (); + void solve (); + void output_results () const; + void compute_error () const; - Triangulation triangulation; - FE_Q fe; - DoFHandler dof_handler; - MappingQ mapping; + Triangulation triangulation; + FE_Q fe; + DoFHandler dof_handler; + MappingQ mapping; - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; - Vector solution; - Vector system_rhs; + Vector solution; + Vector system_rhs; }; - // @sect3{Equation data} - - // Next, let us define the classes that - // describe the exact solution and the right - // hand sides of the problem. This is in - // analogy to step-4 and step-7 where we also - // defined such objects. Given the discussion - // in the introduction, the actual formulas - // should be self-explanatory. A point of - // interest may be how we define the value - // and gradient functions for the 2d and 3d - // cases separately, using explicit - // specializations of the general - // template. An alternative to doing it this - // way might have been to define the general - // template and have a switch - // statement (or a sequence of - // ifs) for each possible value - // of the spatial dimension. + // @sect3{Equation data} + + // Next, let us define the classes that + // describe the exact solution and the right + // hand sides of the problem. This is in + // analogy to step-4 and step-7 where we also + // defined such objects. Given the discussion + // in the introduction, the actual formulas + // should be self-explanatory. A point of + // interest may be how we define the value + // and gradient functions for the 2d and 3d + // cases separately, using explicit + // specializations of the general + // template. An alternative to doing it this + // way might have been to define the general + // template and have a switch + // statement (or a sequence of + // ifs) for each possible value + // of the spatial dimension. template class Solution : public Function { - public: - Solution () : Function() {} + public: + Solution () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; }; @@ -179,7 +179,7 @@ namespace Step38 template <> double Solution<2>::value (const Point<2> &p, - const unsigned int) const + const unsigned int) const { return ( -2. * p(0) * p(1) ); } @@ -188,7 +188,7 @@ namespace Step38 template <> Tensor<1,2> Solution<2>::gradient (const Point<2> &p, - const unsigned int) const + const unsigned int) const { Tensor<1,2> return_value; return_value[0] = -2. * p(1) * (1 - 2. * p(0) * p(0)); @@ -201,17 +201,17 @@ namespace Step38 template <> double Solution<3>::value (const Point<3> &p, - const unsigned int) const + const unsigned int) const { return (std::sin(numbers::PI * p(0)) * - std::cos(numbers::PI * p(1))*exp(p(2))); + std::cos(numbers::PI * p(1))*exp(p(2))); } template <> Tensor<1,3> Solution<3>::gradient (const Point<3> &p, - const unsigned int) const + const unsigned int) const { using numbers::PI; @@ -229,17 +229,17 @@ namespace Step38 template class RightHandSide : public Function { - public: - RightHandSide () : Function() {} + public: + RightHandSide () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; template <> double RightHandSide<2>::value (const Point<2> &p, - const unsigned int /*component*/) const + const unsigned int /*component*/) const { return ( -8. * p(0) * p(1) ); } @@ -248,7 +248,7 @@ namespace Step38 template <> double RightHandSide<3>::value (const Point<3> &p, - const unsigned int /*component*/) const + const unsigned int /*component*/) const { using numbers::PI; @@ -276,101 +276,101 @@ namespace Step38 normal /= p.norm(); return (- trace(hessian) - + 2 * (gradient * normal) - + (hessian * normal) * normal); + + 2 * (gradient * normal) + + (hessian * normal) * normal); } - // @sect3{Implementation of the LaplaceBeltramiProblem class} + // @sect3{Implementation of the LaplaceBeltramiProblem class} - // The rest of the program is actually quite - // unspectacular if you know step-4. Our - // first step is to define the constructor, - // setting the polynomial degree of the - // finite element and mapping, and - // associating the DoF handler to the - // triangulation: + // The rest of the program is actually quite + // unspectacular if you know step-4. Our + // first step is to define the constructor, + // setting the polynomial degree of the + // finite element and mapping, and + // associating the DoF handler to the + // triangulation: template LaplaceBeltramiProblem:: LaplaceBeltramiProblem (const unsigned degree) - : - fe (degree), - dof_handler(triangulation), - mapping (degree) + : + fe (degree), + dof_handler(triangulation), + mapping (degree) {} - // @sect4{LaplaceBeltramiProblem::make_grid_and_dofs} - - // The next step is to create the mesh, - // distribute degrees of freedom, and set up - // the various variables that describe the - // linear system. All of these steps are - // standard with the exception of how to - // create a mesh that describes a surface. We - // could generate a mesh for the domain we - // are interested in, generate a - // triangulation using a mesh generator, and - // read it in using the GridIn class. Or, as - // we do here, we generate the mesh using the - // facilities in the GridGenerator namespace. - // - // In particular, what we're going to do is - // this (enclosed between the set of braces - // below): we generate a - // spacedim dimensional mesh for - // the half disk (in 2d) or half ball (in - // 3d), using the - // GridGenerator::half_hyper_ball - // function. This function sets the boundary - // indicators of all faces on the outside of - // the boundary to zero for the ones located - // on the perimeter of the disk/ball, and one - // on the straight part that splits the full - // disk/ball into two halves. The next step - // is the main point: The - // GridTools::extract_boundary_mesh function - // creates a mesh that consists of those - // cells that are the faces of the previous - // mesh, i.e. it describes the surface - // cells of the original (volume) - // mesh. However, we do not want all faces: - // only those on the perimeter of the disk or - // ball which carry boundary indicator zero; - // we can select these cells using a set of - // boundary indicators that we pass to - // GridTools::extract_boundary_mesh. - // - // There is one point that needs to be - // mentioned. In order to refine a surface - // mesh appropriately if the manifold is - // curved (similarly to refining the faces - // of cells that are adjacent to a curved - // boundary), the triangulation has to have - // an object attached to it that describes - // where new vertices should be located. If - // you don't attach such a boundary object, - // they will be located halfway between - // existing vertices; this is appropriate - // if you have a domain with straight - // boundaries (e.g. a polygon) but not - // when, as here, the manifold has - // curvature. So for things to work - // properly, we need to attach a manifold - // object to our (surface) triangulation, - // in much the same way as we've already - // done in 1d for the boundary. We create - // such an object (with indefinite, - // static, lifetime) at the - // top of the function and attach it to the - // triangulation for all cells with - // boundary indicator zero that will be - // created henceforth. - // - // The final step in creating the mesh is to - // refine it a number of times. The rest of - // the function is the same as in previous - // tutorial programs. + // @sect4{LaplaceBeltramiProblem::make_grid_and_dofs} + + // The next step is to create the mesh, + // distribute degrees of freedom, and set up + // the various variables that describe the + // linear system. All of these steps are + // standard with the exception of how to + // create a mesh that describes a surface. We + // could generate a mesh for the domain we + // are interested in, generate a + // triangulation using a mesh generator, and + // read it in using the GridIn class. Or, as + // we do here, we generate the mesh using the + // facilities in the GridGenerator namespace. + // + // In particular, what we're going to do is + // this (enclosed between the set of braces + // below): we generate a + // spacedim dimensional mesh for + // the half disk (in 2d) or half ball (in + // 3d), using the + // GridGenerator::half_hyper_ball + // function. This function sets the boundary + // indicators of all faces on the outside of + // the boundary to zero for the ones located + // on the perimeter of the disk/ball, and one + // on the straight part that splits the full + // disk/ball into two halves. The next step + // is the main point: The + // GridTools::extract_boundary_mesh function + // creates a mesh that consists of those + // cells that are the faces of the previous + // mesh, i.e. it describes the surface + // cells of the original (volume) + // mesh. However, we do not want all faces: + // only those on the perimeter of the disk or + // ball which carry boundary indicator zero; + // we can select these cells using a set of + // boundary indicators that we pass to + // GridTools::extract_boundary_mesh. + // + // There is one point that needs to be + // mentioned. In order to refine a surface + // mesh appropriately if the manifold is + // curved (similarly to refining the faces + // of cells that are adjacent to a curved + // boundary), the triangulation has to have + // an object attached to it that describes + // where new vertices should be located. If + // you don't attach such a boundary object, + // they will be located halfway between + // existing vertices; this is appropriate + // if you have a domain with straight + // boundaries (e.g. a polygon) but not + // when, as here, the manifold has + // curvature. So for things to work + // properly, we need to attach a manifold + // object to our (surface) triangulation, + // in much the same way as we've already + // done in 1d for the boundary. We create + // such an object (with indefinite, + // static, lifetime) at the + // top of the function and attach it to the + // triangulation for all cells with + // boundary indicator zero that will be + // created henceforth. + // + // The final step in creating the mesh is to + // refine it a number of times. The rest of + // the function is the same as in previous + // tutorial programs. template void LaplaceBeltramiProblem::make_grid_and_dofs () { @@ -385,13 +385,13 @@ namespace Step38 boundary_ids.insert (0); GridTools::extract_boundary_mesh (volume_mesh, triangulation, - boundary_ids); + boundary_ids); } triangulation.refine_global(4); std::cout << "Surface mesh has " << triangulation.n_active_cells() - << " cells." - << std::endl; + << " cells." + << std::endl; dof_handler.distribute_dofs (fe); @@ -410,22 +410,22 @@ namespace Step38 } - // @sect4{LaplaceBeltramiProblem::assemble_system} - - // The following is the central function of - // this program, assembling the matrix that - // corresponds to the surface Laplacian - // (Laplace-Beltrami operator). Maybe - // surprisingly, it actually looks exactly - // the same as for the regular Laplace - // operator discussed in, for example, - // step-4. The key is that the - // FEValues::shape_gradient function does the - // magic: It returns the surface gradient - // $\nabla_K \phi_i(x_q)$ of the $i$th shape - // function at the $q$th quadrature - // point. The rest then does not need any - // changes either: + // @sect4{LaplaceBeltramiProblem::assemble_system} + + // The following is the central function of + // this program, assembling the matrix that + // corresponds to the surface Laplacian + // (Laplace-Beltrami operator). Maybe + // surprisingly, it actually looks exactly + // the same as for the regular Laplace + // operator discussed in, for example, + // step-4. The key is that the + // FEValues::shape_gradient function does the + // magic: It returns the surface gradient + // $\nabla_K \phi_i(x_q)$ of the $i$th shape + // function at the $q$th quadrature + // point. The rest then does not need any + // changes either: template void LaplaceBeltramiProblem::assemble_system () { @@ -451,8 +451,8 @@ namespace Step38 const RightHandSide rhs; for (typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); cell!=endc; ++cell) { cell_matrix = 0; @@ -502,11 +502,11 @@ namespace Step38 - // @sect4{LaplaceBeltramiProblem::solve} + // @sect4{LaplaceBeltramiProblem::solve} - // The next function is the one that solves - // the linear system. Here, too, no changes - // are necessary: + // The next function is the one that solves + // the linear system. Here, too, no changes + // are necessary: template void LaplaceBeltramiProblem::solve () { @@ -523,50 +523,50 @@ namespace Step38 - // @sect4{LaplaceBeltramiProblem::output_result} - - // This is the function that generates - // graphical output from the solution. Most - // of it is boilerplate code, but there are - // two points worth pointing out: - // - // - The DataOut::add_data_vector function - // can take two kinds of vectors: Either - // vectors that have one value per degree - // of freedom defined by the DoFHandler - // object previously attached via - // DataOut::attach_dof_handler; and vectors - // that have one value for each cell of the - // triangulation, for example to output - // estimated errors for each - // cell. Typically, the DataOut class knows - // to tell these two kinds of vectors - // apart: there are almost always more - // degrees of freedom than cells, so we can - // differentiate by the two kinds looking - // at the length of a vector. We could do - // the same here, but only because we got - // lucky: we use a half sphere. If we had - // used the whole sphere as domain and - // $Q_1$ elements, we would have the same - // number of cells as vertices and - // consequently the two kinds of vectors - // would have the same number of - // elements. To avoid the resulting - // confusion, we have to tell the - // DataOut::add_data_vector function which - // kind of vector we have: DoF data. This - // is what the third argument to the - // function does. - // - The DataOut::build_patches function can - // generate output that subdivides each - // cell so that visualization programs can - // resolve curved manifolds or higher - // polynomial degree shape functions - // better. We here subdivide each element - // in each coordinate direction as many - // times as the polynomial degree of the - // finite element in use. + // @sect4{LaplaceBeltramiProblem::output_result} + + // This is the function that generates + // graphical output from the solution. Most + // of it is boilerplate code, but there are + // two points worth pointing out: + // + // - The DataOut::add_data_vector function + // can take two kinds of vectors: Either + // vectors that have one value per degree + // of freedom defined by the DoFHandler + // object previously attached via + // DataOut::attach_dof_handler; and vectors + // that have one value for each cell of the + // triangulation, for example to output + // estimated errors for each + // cell. Typically, the DataOut class knows + // to tell these two kinds of vectors + // apart: there are almost always more + // degrees of freedom than cells, so we can + // differentiate by the two kinds looking + // at the length of a vector. We could do + // the same here, but only because we got + // lucky: we use a half sphere. If we had + // used the whole sphere as domain and + // $Q_1$ elements, we would have the same + // number of cells as vertices and + // consequently the two kinds of vectors + // would have the same number of + // elements. To avoid the resulting + // confusion, we have to tell the + // DataOut::add_data_vector function which + // kind of vector we have: DoF data. This + // is what the third argument to the + // function does. + // - The DataOut::build_patches function can + // generate output that subdivides each + // cell so that visualization programs can + // resolve curved manifolds or higher + // polynomial degree shape functions + // better. We here subdivide each element + // in each coordinate direction as many + // times as the polynomial degree of the + // finite element in use. template void LaplaceBeltramiProblem::output_results () const { @@ -587,19 +587,19 @@ namespace Step38 - // @sect4{LaplaceBeltramiProblem::compute_error} + // @sect4{LaplaceBeltramiProblem::compute_error} - // This is the last piece of functionality: - // we want to compute the error in the - // numerical solution. It is a verbatim copy - // of the code previously shown and discussed - // in step-7. As mentioned in the - // introduction, the Solution - // class provides the (tangential) gradient - // of the solution. To avoid evaluating the - // error only a superconvergence points, we - // choose a quadrature rule of sufficiently - // high order. + // This is the last piece of functionality: + // we want to compute the error in the + // numerical solution. It is a verbatim copy + // of the code previously shown and discussed + // in step-7. As mentioned in the + // introduction, the Solution + // class provides the (tangential) gradient + // of the solution. To avoid evaluating the + // error only a superconvergence points, we + // choose a quadrature rule of sufficiently + // high order. template void LaplaceBeltramiProblem::compute_error () const { @@ -617,10 +617,10 @@ namespace Step38 - // @sect4{LaplaceBeltramiProblem::run} + // @sect4{LaplaceBeltramiProblem::run} - // The last function provides the top-level - // logic. Its contents are self-explanatory: + // The last function provides the top-level + // logic. Its contents are self-explanatory: template void LaplaceBeltramiProblem::run () { @@ -633,13 +633,13 @@ namespace Step38 } - // @sect3{The main() function} +// @sect3{The main() function} - // The remainder of the program is taken up - // by the main() function. It - // follows exactly the general layout first - // introduced in step-6 and used in all - // following tutorial programs: +// The remainder of the program is taken up +// by the main() function. It +// follows exactly the general layout first +// introduced in step-6 and used in all +// following tutorial programs: int main () { try diff --git a/deal.II/examples/step-39/step-39.cc b/deal.II/examples/step-39/step-39.cc index 5672cb156f..d1a43acaf1 100644 --- a/deal.II/examples/step-39/step-39.cc +++ b/deal.II/examples/step-39/step-39.cc @@ -9,11 +9,11 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // The include files for the linear - // algebra: A regular SparseMatrix, - // which in turn will include the - // necessary files for - // SparsityPattern and Vector classes. +// The include files for the linear +// algebra: A regular SparseMatrix, +// which in turn will include the +// necessary files for +// SparsityPattern and Vector classes. #include #include #include @@ -21,32 +21,32 @@ #include #include - // Include files for setting up the - // mesh +// Include files for setting up the +// mesh #include #include - // Include files for FiniteElement - // classes and DoFHandler. +// Include files for FiniteElement +// classes and DoFHandler. #include #include #include #include #include - // The include files for using the - // MeshWorker framework +// The include files for using the +// MeshWorker framework #include #include #include #include - // The include file for local - // integrators associated with the - // Laplacian +// The include file for local +// integrators associated with the +// Laplacian #include - // Support for multigrid methods +// Support for multigrid methods #include #include #include @@ -54,9 +54,9 @@ #include #include - // Finally, we take our exact - // solution from the library as well - // as quadrature and additional tools. +// Finally, we take our exact +// solution from the library as well +// as quadrature and additional tools. #include #include #include @@ -65,83 +65,83 @@ #include #include - // All classes of the deal.II library - // are in the namespace dealii. In - // order to save typing, we tell the - // compiler to search names in there - // as well. +// All classes of the deal.II library +// are in the namespace dealii. In +// order to save typing, we tell the +// compiler to search names in there +// as well. namespace Step39 { using namespace dealii; - // This is the function we use to set - // the boundary values and also the - // exact solution we compare to. + // This is the function we use to set + // the boundary values and also the + // exact solution we compare to. Functions::SlitSingularityFunction<2> exact_solution; - // @sect3{The local integrators} - - // MeshWorker separates local - // integration from the loops over - // cells and faces. Thus, we have to - // write local integration classes - // for generating matrices, the right - // hand side and the error - // estimator. - - // All these classes have the same - // three functions for integrating - // over cells, boundary faces and - // interior faces, respectively. All - // the information needed for the - // local integration is provided by - // MeshWorker::IntegrationInfo. Note - // that the signature of the functions cannot - // be changed, because it is expected - // by MeshWorker::integration_loop(). - - // The first class defining local - // integrators is responsible for - // computing cell and face - // matrices. It is used to assemble - // the global matrix as well as the - // level matrices. + // @sect3{The local integrators} + + // MeshWorker separates local + // integration from the loops over + // cells and faces. Thus, we have to + // write local integration classes + // for generating matrices, the right + // hand side and the error + // estimator. + + // All these classes have the same + // three functions for integrating + // over cells, boundary faces and + // interior faces, respectively. All + // the information needed for the + // local integration is provided by + // MeshWorker::IntegrationInfo. Note + // that the signature of the functions cannot + // be changed, because it is expected + // by MeshWorker::integration_loop(). + + // The first class defining local + // integrators is responsible for + // computing cell and face + // matrices. It is used to assemble + // the global matrix as well as the + // level matrices. template class MatrixIntegrator : public Subscriptor { - public: - static void cell(MeshWorker::DoFInfo& dinfo, - typename MeshWorker::IntegrationInfo& info); - static void boundary(MeshWorker::DoFInfo& dinfo, - typename MeshWorker::IntegrationInfo& info); - static void face(MeshWorker::DoFInfo& dinfo1, - MeshWorker::DoFInfo& dinfo2, - typename MeshWorker::IntegrationInfo& info1, - typename MeshWorker::IntegrationInfo& info2); + public: + static void cell(MeshWorker::DoFInfo &dinfo, + typename MeshWorker::IntegrationInfo &info); + static void boundary(MeshWorker::DoFInfo &dinfo, + typename MeshWorker::IntegrationInfo &info); + static void face(MeshWorker::DoFInfo &dinfo1, + MeshWorker::DoFInfo &dinfo2, + typename MeshWorker::IntegrationInfo &info1, + typename MeshWorker::IntegrationInfo &info2); }; - // On each cell, we integrate the - // Dirichlet form. We use the library - // of ready made integrals in - // LocalIntegrators to avoid writing - // these loops ourselves. Similarly, - // we implement Nitsche boundary - // conditions and the interior - // penalty fluxes between cells. - // - // The boundary und flux terms need a - // penalty parameter, which should be - // adjusted to the cell size and the - // polynomial degree. A safe choice - // of this parameter for constant - // coefficients can be found in - // LocalIntegrators::Laplace::compute_penalty() - // and we use this below. + // On each cell, we integrate the + // Dirichlet form. We use the library + // of ready made integrals in + // LocalIntegrators to avoid writing + // these loops ourselves. Similarly, + // we implement Nitsche boundary + // conditions and the interior + // penalty fluxes between cells. + // + // The boundary und flux terms need a + // penalty parameter, which should be + // adjusted to the cell size and the + // polynomial degree. A safe choice + // of this parameter for constant + // coefficients can be found in + // LocalIntegrators::Laplace::compute_penalty() + // and we use this below. template void MatrixIntegrator::cell( - MeshWorker::DoFInfo& dinfo, - typename MeshWorker::IntegrationInfo& info) + MeshWorker::DoFInfo &dinfo, + typename MeshWorker::IntegrationInfo &info) { LocalIntegrators::Laplace::cell_matrix(dinfo.matrix(0,false).matrix, info.fe_values()); } @@ -149,8 +149,8 @@ namespace Step39 template void MatrixIntegrator::boundary( - MeshWorker::DoFInfo& dinfo, - typename MeshWorker::IntegrationInfo& info) + MeshWorker::DoFInfo &dinfo, + typename MeshWorker::IntegrationInfo &info) { const unsigned int deg = info.fe_values(0).get_fe().tensor_degree(); LocalIntegrators::Laplace::nitsche_matrix( @@ -158,14 +158,14 @@ namespace Step39 LocalIntegrators::Laplace::compute_penalty(dinfo, dinfo, deg, deg)); } - // Interior faces use the interior - // penalty method + // Interior faces use the interior + // penalty method template void MatrixIntegrator::face( - MeshWorker::DoFInfo& dinfo1, - MeshWorker::DoFInfo& dinfo2, - typename MeshWorker::IntegrationInfo& info1, - typename MeshWorker::IntegrationInfo& info2) + MeshWorker::DoFInfo &dinfo1, + MeshWorker::DoFInfo &dinfo2, + typename MeshWorker::IntegrationInfo &info1, + typename MeshWorker::IntegrationInfo &info2) { const unsigned int deg = info1.fe_values(0).get_fe().tensor_degree(); LocalIntegrators::Laplace::ip_matrix( @@ -175,35 +175,35 @@ namespace Step39 LocalIntegrators::Laplace::compute_penalty(dinfo1, dinfo2, deg, deg)); } - // The second local integrator builds - // the right hand side. In our - // example, the right hand side - // function is zero, such that only - // the boundary condition is set here - // in weak form. + // The second local integrator builds + // the right hand side. In our + // example, the right hand side + // function is zero, such that only + // the boundary condition is set here + // in weak form. template class RHSIntegrator : public Subscriptor { - public: - static void cell(MeshWorker::DoFInfo& dinfo, typename MeshWorker::IntegrationInfo& info); - static void boundary(MeshWorker::DoFInfo& dinfo, typename MeshWorker::IntegrationInfo& info); - static void face(MeshWorker::DoFInfo& dinfo1, - MeshWorker::DoFInfo& dinfo2, - typename MeshWorker::IntegrationInfo& info1, - typename MeshWorker::IntegrationInfo& info2); + public: + static void cell(MeshWorker::DoFInfo &dinfo, typename MeshWorker::IntegrationInfo &info); + static void boundary(MeshWorker::DoFInfo &dinfo, typename MeshWorker::IntegrationInfo &info); + static void face(MeshWorker::DoFInfo &dinfo1, + MeshWorker::DoFInfo &dinfo2, + typename MeshWorker::IntegrationInfo &info1, + typename MeshWorker::IntegrationInfo &info2); }; template - void RHSIntegrator::cell(MeshWorker::DoFInfo&, typename MeshWorker::IntegrationInfo&) + void RHSIntegrator::cell(MeshWorker::DoFInfo &, typename MeshWorker::IntegrationInfo &) {} template - void RHSIntegrator::boundary(MeshWorker::DoFInfo& dinfo, typename MeshWorker::IntegrationInfo& info) + void RHSIntegrator::boundary(MeshWorker::DoFInfo &dinfo, typename MeshWorker::IntegrationInfo &info) { - const FEValuesBase& fe = info.fe_values(); - Vector& local_vector = dinfo.vector(0).block(0); + const FEValuesBase &fe = info.fe_values(); + Vector &local_vector = dinfo.vector(0).block(0); std::vector boundary_values(fe.n_quadrature_points); exact_solution.value_list(fe.get_quadrature_points(), boundary_values); @@ -211,7 +211,7 @@ namespace Step39 const unsigned int deg = fe.get_fe().tensor_degree(); const double penalty = 2. * deg * (deg+1) * dinfo.face->measure() / dinfo.cell->measure(); - for (unsigned k=0;k - void RHSIntegrator::face(MeshWorker::DoFInfo&, - MeshWorker::DoFInfo&, - typename MeshWorker::IntegrationInfo&, - typename MeshWorker::IntegrationInfo&) + void RHSIntegrator::face(MeshWorker::DoFInfo &, + MeshWorker::DoFInfo &, + typename MeshWorker::IntegrationInfo &, + typename MeshWorker::IntegrationInfo &) {} - // The third local integrator is - // responsible for the contributions - // to the error estimate. This is the - // standard energy estimator due to - // Karakashian and Pascal (2003). + // The third local integrator is + // responsible for the contributions + // to the error estimate. This is the + // standard energy estimator due to + // Karakashian and Pascal (2003). template class Estimator : public Subscriptor { - public: - static void cell(MeshWorker::DoFInfo& dinfo, typename MeshWorker::IntegrationInfo& info); - static void boundary(MeshWorker::DoFInfo& dinfo, typename MeshWorker::IntegrationInfo& info); - static void face(MeshWorker::DoFInfo& dinfo1, - MeshWorker::DoFInfo& dinfo2, - typename MeshWorker::IntegrationInfo& info1, - typename MeshWorker::IntegrationInfo& info2); + public: + static void cell(MeshWorker::DoFInfo &dinfo, typename MeshWorker::IntegrationInfo &info); + static void boundary(MeshWorker::DoFInfo &dinfo, typename MeshWorker::IntegrationInfo &info); + static void face(MeshWorker::DoFInfo &dinfo1, + MeshWorker::DoFInfo &dinfo2, + typename MeshWorker::IntegrationInfo &info1, + typename MeshWorker::IntegrationInfo &info2); }; - // The cell contribution is the - // Laplacian of the discrete - // solution, since the right hand - // side is zero. + // The cell contribution is the + // Laplacian of the discrete + // solution, since the right hand + // side is zero. template - void Estimator::cell(MeshWorker::DoFInfo& dinfo, typename MeshWorker::IntegrationInfo& info) + void Estimator::cell(MeshWorker::DoFInfo &dinfo, typename MeshWorker::IntegrationInfo &info) { - const FEValuesBase& fe = info.fe_values(); + const FEValuesBase &fe = info.fe_values(); - const std::vector >& DDuh = info.hessians[0][0]; - for (unsigned k=0;k > &DDuh = info.hessians[0][0]; + for (unsigned k=0; kdiameter() * trace(DDuh[k]); dinfo.value(0) += t*t * fe.JxW(k); @@ -263,47 +263,47 @@ namespace Step39 dinfo.value(0) = std::sqrt(dinfo.value(0)); } - // At the boundary, we use simply a - // weighted form of the boundary - // residual, namely the norm of the - // difference between the finite - // element solution and the correct - // boundary condition. + // At the boundary, we use simply a + // weighted form of the boundary + // residual, namely the norm of the + // difference between the finite + // element solution and the correct + // boundary condition. template - void Estimator::boundary(MeshWorker::DoFInfo& dinfo, typename MeshWorker::IntegrationInfo& info) + void Estimator::boundary(MeshWorker::DoFInfo &dinfo, typename MeshWorker::IntegrationInfo &info) { - const FEValuesBase& fe = info.fe_values(); + const FEValuesBase &fe = info.fe_values(); std::vector boundary_values(fe.n_quadrature_points); exact_solution.value_list(fe.get_quadrature_points(), boundary_values); - const std::vector& uh = info.values[0][0]; + const std::vector &uh = info.values[0][0]; const unsigned int deg = fe.get_fe().tensor_degree(); const double penalty = 2. * deg * (deg+1) * dinfo.face->measure() / dinfo.cell->measure(); - for (unsigned k=0;k - void Estimator::face(MeshWorker::DoFInfo& dinfo1, - MeshWorker::DoFInfo& dinfo2, - typename MeshWorker::IntegrationInfo& info1, - typename MeshWorker::IntegrationInfo& info2) + void Estimator::face(MeshWorker::DoFInfo &dinfo1, + MeshWorker::DoFInfo &dinfo2, + typename MeshWorker::IntegrationInfo &info1, + typename MeshWorker::IntegrationInfo &info2) { - const FEValuesBase& fe = info1.fe_values(); - const std::vector& uh1 = info1.values[0][0]; - const std::vector& uh2 = info2.values[0][0]; - const std::vector >& Duh1 = info1.gradients[0][0]; - const std::vector >& Duh2 = info2.gradients[0][0]; + const FEValuesBase &fe = info1.fe_values(); + const std::vector &uh1 = info1.values[0][0]; + const std::vector &uh2 = info2.values[0][0]; + const std::vector > &Duh1 = info1.gradients[0][0]; + const std::vector > &Duh2 = info2.gradients[0][0]; const unsigned int deg = fe.get_fe().tensor_degree(); const double penalty1 = deg * (deg+1) * dinfo1.face->measure() / dinfo1.cell->measure(); @@ -311,7 +311,7 @@ namespace Step39 const double penalty = penalty1 + penalty2; const double h = dinfo1.face->measure(); - for (unsigned k=0;k class ErrorIntegrator : public Subscriptor { - public: - static void cell(MeshWorker::DoFInfo& dinfo, typename MeshWorker::IntegrationInfo& info); - static void boundary(MeshWorker::DoFInfo& dinfo, typename MeshWorker::IntegrationInfo& info); - static void face(MeshWorker::DoFInfo& dinfo1, - MeshWorker::DoFInfo& dinfo2, - typename MeshWorker::IntegrationInfo& info1, - typename MeshWorker::IntegrationInfo& info2); + public: + static void cell(MeshWorker::DoFInfo &dinfo, typename MeshWorker::IntegrationInfo &info); + static void boundary(MeshWorker::DoFInfo &dinfo, typename MeshWorker::IntegrationInfo &info); + static void face(MeshWorker::DoFInfo &dinfo1, + MeshWorker::DoFInfo &dinfo2, + typename MeshWorker::IntegrationInfo &info1, + typename MeshWorker::IntegrationInfo &info2); }; - // Here we have the integration on - // cells. There is currently no good - // interfce in MeshWorker that would - // allow us to access values of - // regular functions in the - // quadrature points. Thus, we have - // to create the vectors for the - // exact function's values and - // gradients inside the cell - // integrator. After that, everything - // is as before and we just add up - // the squares of the differences. - - // Additionally to computing the error - // in the energy norm, we use the - // capability of the mesh worker to - // compute two functionals at the - // same time and compute the - // L2-error in the - // same loop. Obviously, this one - // does not have any jump terms and - // only appears in the integration on - // cells. + // Here we have the integration on + // cells. There is currently no good + // interfce in MeshWorker that would + // allow us to access values of + // regular functions in the + // quadrature points. Thus, we have + // to create the vectors for the + // exact function's values and + // gradients inside the cell + // integrator. After that, everything + // is as before and we just add up + // the squares of the differences. + + // Additionally to computing the error + // in the energy norm, we use the + // capability of the mesh worker to + // compute two functionals at the + // same time and compute the + // L2-error in the + // same loop. Obviously, this one + // does not have any jump terms and + // only appears in the integration on + // cells. template void ErrorIntegrator::cell( - MeshWorker::DoFInfo& dinfo, - typename MeshWorker::IntegrationInfo& info) + MeshWorker::DoFInfo &dinfo, + typename MeshWorker::IntegrationInfo &info) { - const FEValuesBase& fe = info.fe_values(); + const FEValuesBase &fe = info.fe_values(); std::vector > exact_gradients(fe.n_quadrature_points); std::vector exact_values(fe.n_quadrature_points); exact_solution.gradient_list(fe.get_quadrature_points(), exact_gradients); exact_solution.value_list(fe.get_quadrature_points(), exact_values); - const std::vector >& Duh = info.gradients[0][0]; - const std::vector& uh = info.values[0][0]; + const std::vector > &Duh = info.gradients[0][0]; + const std::vector &uh = info.values[0][0]; - for (unsigned k=0;k void ErrorIntegrator::boundary( - MeshWorker::DoFInfo& dinfo, - typename MeshWorker::IntegrationInfo& info) + MeshWorker::DoFInfo &dinfo, + typename MeshWorker::IntegrationInfo &info) { - const FEValuesBase& fe = info.fe_values(); + const FEValuesBase &fe = info.fe_values(); std::vector exact_values(fe.n_quadrature_points); exact_solution.value_list(fe.get_quadrature_points(), exact_values); - const std::vector& uh = info.values[0][0]; + const std::vector &uh = info.values[0][0]; const unsigned int deg = fe.get_fe().tensor_degree(); const double penalty = 2. * deg * (deg+1) * dinfo.face->measure() / dinfo.cell->measure(); - for (unsigned k=0;k void ErrorIntegrator::face( - MeshWorker::DoFInfo& dinfo1, - MeshWorker::DoFInfo& dinfo2, - typename MeshWorker::IntegrationInfo& info1, - typename MeshWorker::IntegrationInfo& info2) + MeshWorker::DoFInfo &dinfo1, + MeshWorker::DoFInfo &dinfo2, + typename MeshWorker::IntegrationInfo &info1, + typename MeshWorker::IntegrationInfo &info2) { - const FEValuesBase& fe = info1.fe_values(); - const std::vector& uh1 = info1.values[0][0]; - const std::vector& uh2 = info2.values[0][0]; + const FEValuesBase &fe = info1.fe_values(); + const std::vector &uh1 = info1.values[0][0]; + const std::vector &uh2 = info2.values[0][0]; const unsigned int deg = fe.get_fe().tensor_degree(); const double penalty1 = deg * (deg+1) * dinfo1.face->measure() / dinfo1.cell->measure(); const double penalty2 = deg * (deg+1) * dinfo2.face->measure() / dinfo2.cell->measure(); const double penalty = penalty1 + penalty2; - for (unsigned k=0;k class InteriorPenaltyProblem { - public: - typedef MeshWorker::IntegrationInfo CellInfo; - - InteriorPenaltyProblem(const FiniteElement& fe); - - void run(unsigned int n_steps); - - private: - void setup_system (); - void assemble_matrix (); - void assemble_mg_matrix (); - void assemble_right_hand_side (); - void error (); - double estimate (); - void solve (); - void output_results (const unsigned int cycle) const; - - // The member objects related to - // the discretization are here. - Triangulation triangulation; - const MappingQ1 mapping; - const FiniteElement& fe; - MGDoFHandler mg_dof_handler; - DoFHandler& dof_handler; - - // Then, we have the matrices and - // vectors related to the global - // discrete system. - SparsityPattern sparsity; - SparseMatrix matrix; - Vector solution; - Vector right_hand_side; - BlockVector estimates; - - // Finally, we have a group of - // sparsity patterns and sparse - // matrices related to the - // multilevel preconditioner. - // First, we have a level matrix - // and its sparsity pattern. - MGLevelObject mg_sparsity; - MGLevelObject > mg_matrix; - - // When we perform multigrid with - // local smoothing on locally - // refined meshes, additional - // matrices are required; see - // Kanschat (2004). Here is the - // sparsity pattern for these - // edge matrices. We only need - // one, because the pattern of - // the up matrix is the - // transpose of that of the down - // matrix. Actually, we do not - // care too much about these - // details, since the MeshWorker - // is filling these matrices. - MGLevelObject mg_sparsity_dg_interface; - // The flux matrix at the - // refinement edge, coupling fine - // level degrees of freedom to - // coarse level. - MGLevelObject > mg_matrix_dg_down; - // The transpose of the flux - // matrix at the refinement edge, - // coupling coarse level degrees - // of freedom to fine level. - MGLevelObject > mg_matrix_dg_up; + public: + typedef MeshWorker::IntegrationInfo CellInfo; + + InteriorPenaltyProblem(const FiniteElement &fe); + + void run(unsigned int n_steps); + + private: + void setup_system (); + void assemble_matrix (); + void assemble_mg_matrix (); + void assemble_right_hand_side (); + void error (); + double estimate (); + void solve (); + void output_results (const unsigned int cycle) const; + + // The member objects related to + // the discretization are here. + Triangulation triangulation; + const MappingQ1 mapping; + const FiniteElement &fe; + MGDoFHandler mg_dof_handler; + DoFHandler &dof_handler; + + // Then, we have the matrices and + // vectors related to the global + // discrete system. + SparsityPattern sparsity; + SparseMatrix matrix; + Vector solution; + Vector right_hand_side; + BlockVector estimates; + + // Finally, we have a group of + // sparsity patterns and sparse + // matrices related to the + // multilevel preconditioner. + // First, we have a level matrix + // and its sparsity pattern. + MGLevelObject mg_sparsity; + MGLevelObject > mg_matrix; + + // When we perform multigrid with + // local smoothing on locally + // refined meshes, additional + // matrices are required; see + // Kanschat (2004). Here is the + // sparsity pattern for these + // edge matrices. We only need + // one, because the pattern of + // the up matrix is the + // transpose of that of the down + // matrix. Actually, we do not + // care too much about these + // details, since the MeshWorker + // is filling these matrices. + MGLevelObject mg_sparsity_dg_interface; + // The flux matrix at the + // refinement edge, coupling fine + // level degrees of freedom to + // coarse level. + MGLevelObject > mg_matrix_dg_down; + // The transpose of the flux + // matrix at the refinement edge, + // coupling coarse level degrees + // of freedom to fine level. + MGLevelObject > mg_matrix_dg_up; }; - // The constructor simply sets up the - // coarse grid and the - // DoFHandler. The FiniteElement is - // provided as a parameter to allow - // flexibility. + // The constructor simply sets up the + // coarse grid and the + // DoFHandler. The FiniteElement is + // provided as a parameter to allow + // flexibility. template - InteriorPenaltyProblem::InteriorPenaltyProblem(const FiniteElement& fe) - : - mapping(), - fe(fe), - mg_dof_handler(triangulation), - dof_handler(mg_dof_handler), - estimates(1) + InteriorPenaltyProblem::InteriorPenaltyProblem(const FiniteElement &fe) + : + mapping(), + fe(fe), + mg_dof_handler(triangulation), + dof_handler(mg_dof_handler), + estimates(1) { GridGenerator::hyper_cube_slit(triangulation, -1, 1); } - // In this function, we set up the - // dimension of the linear system and - // the sparsity patterns for the - // global matrix as well as the level - // matrices. + // In this function, we set up the + // dimension of the linear system and + // the sparsity patterns for the + // global matrix as well as the level + // matrices. template void InteriorPenaltyProblem::setup_system() { - // First, we use the finite element - // to distribute degrees of - // freedom over the mesh and number - // them. + // First, we use the finite element + // to distribute degrees of + // freedom over the mesh and number + // them. dof_handler.distribute_dofs(fe); unsigned int n_dofs = dof_handler.n_dofs(); - // Then, we already know the size - // of the vectors representing - // finite element functions. + // Then, we already know the size + // of the vectors representing + // finite element functions. solution.reinit(n_dofs); right_hand_side.reinit(n_dofs); - // Next, we set up the sparsity - // pattern for the global - // matrix. Since we do not know the - // row sizes in advance, we first - // fill a temporary - // CompressedSparsityPattern object - // and copy it to the regular - // SparsityPattern once it is - // complete. + // Next, we set up the sparsity + // pattern for the global + // matrix. Since we do not know the + // row sizes in advance, we first + // fill a temporary + // CompressedSparsityPattern object + // and copy it to the regular + // SparsityPattern once it is + // complete. CompressedSparsityPattern c_sparsity(n_dofs); DoFTools::make_flux_sparsity_pattern(dof_handler, c_sparsity); sparsity.copy_from(c_sparsity); matrix.reinit(sparsity); const unsigned int n_levels = triangulation.n_levels(); - // The global system is set up, now - // we attend to the level - // matrices. We resize all matrix - // objects to hold one matrix per level. + // The global system is set up, now + // we attend to the level + // matrices. We resize all matrix + // objects to hold one matrix per level. mg_matrix.resize(0, n_levels-1); mg_matrix.clear(); mg_matrix_dg_up.resize(0, n_levels-1); mg_matrix_dg_up.clear(); mg_matrix_dg_down.resize(0, n_levels-1); mg_matrix_dg_down.clear(); - // It is important to update the - // sparsity patterns after - // clear() was called for - // the level matrices, since the - // matrices lock the sparsity - // pattern through the Smartpointer - // ans Subscriptor mechanism. + // It is important to update the + // sparsity patterns after + // clear() was called for + // the level matrices, since the + // matrices lock the sparsity + // pattern through the Smartpointer + // ans Subscriptor mechanism. mg_sparsity.resize(0, n_levels-1); mg_sparsity_dg_interface.resize(0, n_levels-1); - // Now all objects are prepared to - // hold one sparsity pattern or - // matrix per level. What's left is - // setting up the sparsity patterns - // on each level. + // Now all objects are prepared to + // hold one sparsity pattern or + // matrix per level. What's left is + // setting up the sparsity patterns + // on each level. for (unsigned int level=mg_sparsity.get_minlevel(); - level<=mg_sparsity.get_maxlevel();++level) + level<=mg_sparsity.get_maxlevel(); ++level) { - // These are roughly the same - // lines as above for the - // global matrix, now for each - // level. + // These are roughly the same + // lines as above for the + // global matrix, now for each + // level. CompressedSparsityPattern c_sparsity(mg_dof_handler.n_dofs(level)); MGTools::make_flux_sparsity_pattern(mg_dof_handler, c_sparsity, level); mg_sparsity[level].copy_from(c_sparsity); mg_matrix[level].reinit(mg_sparsity[level]); - // Additionally, we need to - // initialize the transfer - // matrices at the refinement - // edge between levels. They - // are stored at the index - // referring to the finer of - // the two indices, thus there - // is no such object on level - // 0. + // Additionally, we need to + // initialize the transfer + // matrices at the refinement + // edge between levels. They + // are stored at the index + // referring to the finer of + // the two indices, thus there + // is no such object on level + // 0. if (level>0) { CompressedSparsityPattern ci_sparsity; @@ -664,69 +664,69 @@ namespace Step39 } - // In this function, we assemble the - // global system matrix, where by - // global we indicate that this is - // the matrix of the discrete system - // we solve and it is covering the - // whole mesh. + // In this function, we assemble the + // global system matrix, where by + // global we indicate that this is + // the matrix of the discrete system + // we solve and it is covering the + // whole mesh. template void InteriorPenaltyProblem::assemble_matrix() { - // First, we need t set up the - // object providing the values we - // integrate. This object contains - // all FEValues and FEFaceValues - // objects needed and also - // maintains them automatically - // such that they always point to - // the current cell. To this end, - // we need to tell it first, where - // and what to compute. Since we - // are not doing anything fancy, we - // can rely on their standard - // choice for quadrature rules. - // - // Since their default update flags - // are minimal, we add what we need - // additionally, namely the values - // and gradients of shape functions - // on all objects (cells, boundary - // and interior faces). Afterwards, - // we are ready to initialize the - // container, which will create all - // necessary FEValuesBase objects - // for integration. + // First, we need t set up the + // object providing the values we + // integrate. This object contains + // all FEValues and FEFaceValues + // objects needed and also + // maintains them automatically + // such that they always point to + // the current cell. To this end, + // we need to tell it first, where + // and what to compute. Since we + // are not doing anything fancy, we + // can rely on their standard + // choice for quadrature rules. + // + // Since their default update flags + // are minimal, we add what we need + // additionally, namely the values + // and gradients of shape functions + // on all objects (cells, boundary + // and interior faces). Afterwards, + // we are ready to initialize the + // container, which will create all + // necessary FEValuesBase objects + // for integration. MeshWorker::IntegrationInfoBox info_box; UpdateFlags update_flags = update_values | update_gradients; info_box.add_update_flags_all(update_flags); info_box.initialize(fe, mapping); - // This is the object into which we - // integrate local data. It is - // filled by the local integration - // routines in MatrixIntegrator and - // then used by the assembler to - // distribute the information into - // the global matrix. + // This is the object into which we + // integrate local data. It is + // filled by the local integration + // routines in MatrixIntegrator and + // then used by the assembler to + // distribute the information into + // the global matrix. MeshWorker::DoFInfo dof_info(dof_handler); - // Finally, we need an object that - // assembles the local matrix into - // the global matrix. + // Finally, we need an object that + // assembles the local matrix into + // the global matrix. MeshWorker::Assembler::MatrixSimple > assembler; assembler.initialize(matrix); - // Now, we throw everything into a - // MeshWorker::loop(), which here - // traverses all active cells of - // the mesh, computes cell and face - // matrices and assembles them into - // the global matrix. We use the - // variable dof_handler - // here in order to use the global - // numbering of degrees of freedom. + // Now, we throw everything into a + // MeshWorker::loop(), which here + // traverses all active cells of + // the mesh, computes cell and face + // matrices and assembles them into + // the global matrix. We use the + // variable dof_handler + // here in order to use the global + // numbering of degrees of freedom. MeshWorker::integration_loop( dof_handler.begin_active(), dof_handler.end(), dof_info, info_box, @@ -737,11 +737,11 @@ namespace Step39 } - // Now, we do the same for the level - // matrices. Not too surprisingly, - // this function looks like a twin of - // the previous one. Indeed, there - // are only two minor differences. + // Now, we do the same for the level + // matrices. Not too surprisingly, + // this function looks like a twin of + // the previous one. Indeed, there + // are only two minor differences. template void InteriorPenaltyProblem::assemble_mg_matrix() @@ -753,23 +753,23 @@ namespace Step39 MeshWorker::DoFInfo dof_info(mg_dof_handler); - // Obviously, the assembler needs - // to be replaced by one filling - // level matrices. Note that it - // automatically fills the edge - // matrices as well. + // Obviously, the assembler needs + // to be replaced by one filling + // level matrices. Note that it + // automatically fills the edge + // matrices as well. MeshWorker::Assembler::MGMatrixSimple > assembler; assembler.initialize(mg_matrix); assembler.initialize_fluxes(mg_matrix_dg_up, mg_matrix_dg_down); - // Here is the other difference to - // the previous function: we run - // over all cells, not only the - // active ones. And we use - // mg_dof_handler, since - // we need the degrees of freedom - // on each level, not the global - // numbering. + // Here is the other difference to + // the previous function: we run + // over all cells, not only the + // active ones. And we use + // mg_dof_handler, since + // we need the degrees of freedom + // on each level, not the global + // numbering. MeshWorker::integration_loop ( mg_dof_handler.begin(), mg_dof_handler.end(), dof_info, info_box, @@ -780,11 +780,11 @@ namespace Step39 } - // Here we have another clone of the - // assemble function. The difference - // to assembling the system matrix - // consists in that we assemble a - // vector here. + // Here we have another clone of the + // assemble function. The difference + // to assembling the system matrix + // consists in that we assemble a + // vector here. template void InteriorPenaltyProblem::assemble_right_hand_side() @@ -796,19 +796,19 @@ namespace Step39 MeshWorker::DoFInfo dof_info(dof_handler); - // Since this assembler alows us to - // fill several vectors, the - // interface is a little more - // complicated as above. The - // pointers to the vectors have to - // be stored in a NamedData - // object. While this seems to - // cause two extra lines of code - // here, it actually comes handy in - // more complex applications. + // Since this assembler alows us to + // fill several vectors, the + // interface is a little more + // complicated as above. The + // pointers to the vectors have to + // be stored in a NamedData + // object. While this seems to + // cause two extra lines of code + // here, it actually comes handy in + // more complex applications. MeshWorker::Assembler::ResidualSimple > assembler; NamedData* > data; - Vector* rhs = &right_hand_side; + Vector *rhs = &right_hand_side; data.add(rhs, "RHS"); assembler.initialize(data); @@ -824,324 +824,324 @@ namespace Step39 } - // Now that we have coded all - // functions building the discrete - // linear system, it is about time - // that we actually solve it. + // Now that we have coded all + // functions building the discrete + // linear system, it is about time + // that we actually solve it. template void InteriorPenaltyProblem::solve() { - // The solver of choice is - // conjugate gradient. + // The solver of choice is + // conjugate gradient. SolverControl control(1000, 1.e-12); SolverCG > solver(control); - // Now we are setting up the - // components of the multilevel - // preconditioner. First, we need - // transfer between grid - // levels. The object we are using - // here generates sparse matrices - // for these transfers. + // Now we are setting up the + // components of the multilevel + // preconditioner. First, we need + // transfer between grid + // levels. The object we are using + // here generates sparse matrices + // for these transfers. MGTransferPrebuilt > mg_transfer; mg_transfer.build_matrices(mg_dof_handler); - // Then, we need an exact solver - // for the matrix on the coarsest - // level. + // Then, we need an exact solver + // for the matrix on the coarsest + // level. FullMatrix coarse_matrix; coarse_matrix.copy_from (mg_matrix[0]); MGCoarseGridHouseholder > mg_coarse; mg_coarse.initialize(coarse_matrix); - // While transfer and coarse grid - // solver are pretty much generic, - // more flexibility is offered for - // the smoother. First, we choose - // Gauss-Seidel as our smoothing - // method. + // While transfer and coarse grid + // solver are pretty much generic, + // more flexibility is offered for + // the smoother. First, we choose + // Gauss-Seidel as our smoothing + // method. GrowingVectorMemory > mem; typedef PreconditionSOR > RELAXATION; MGSmootherRelaxation, RELAXATION, Vector > - mg_smoother(mem); + mg_smoother(mem); RELAXATION::AdditionalData smoother_data(1.); mg_smoother.initialize(mg_matrix, smoother_data); - // Do two smoothing steps on each - // level. + // Do two smoothing steps on each + // level. mg_smoother.set_steps(2); - // Since the SOR method is not - // symmetric, but we use conjugate - // gradient iteration below, here - // is a trick to make the - // multilevel preconditioner a - // symmetric operator even for - // nonsymmetric smoothers. + // Since the SOR method is not + // symmetric, but we use conjugate + // gradient iteration below, here + // is a trick to make the + // multilevel preconditioner a + // symmetric operator even for + // nonsymmetric smoothers. mg_smoother.set_symmetric(true); - // The smoother class optionally - // implements the variable V-cycle, - // which we do not want here. + // The smoother class optionally + // implements the variable V-cycle, + // which we do not want here. mg_smoother.set_variable(false); - // Finally, we must wrap our - // matrices in an object having the - // required multiplication - // functions. + // Finally, we must wrap our + // matrices in an object having the + // required multiplication + // functions. MGMatrix, Vector > mgmatrix(&mg_matrix); MGMatrix, Vector > mgdown(&mg_matrix_dg_down); MGMatrix, Vector > mgup(&mg_matrix_dg_up); - // Now, we are ready to set up the - // V-cycle operator and the - // multilevel preconditioner. + // Now, we are ready to set up the + // V-cycle operator and the + // multilevel preconditioner. Multigrid > mg(mg_dof_handler, mgmatrix, mg_coarse, mg_transfer, mg_smoother, mg_smoother); - // Let us not forget the edge - // matrices needed because of the - // adaptive refinement. + // Let us not forget the edge + // matrices needed because of the + // adaptive refinement. mg.set_edge_flux_matrices(mgdown, mgup); - // After all preparations, wrap the - // Multigrid object into another - // object, which can be used as a - // regular preconditioner, + // After all preparations, wrap the + // Multigrid object into another + // object, which can be used as a + // regular preconditioner, PreconditionMG, MGTransferPrebuilt > > - preconditioner(mg_dof_handler, mg, mg_transfer); - // and use it to solve the system. - solver.solve(matrix, solution, right_hand_side, preconditioner); -} + preconditioner(mg_dof_handler, mg, mg_transfer); + // and use it to solve the system. + solver.solve(matrix, solution, right_hand_side, preconditioner); + } - // Another clone of the assemble - // function. The big difference to - // the previous ones is here that we - // also have an input vector. -template -double -InteriorPenaltyProblem::estimate() -{ - // The results of the estimator are - // stored in a vector with one - // entry per cell. Since cells in - // deal.II are not numbered, we - // have to create our own numbering - // in order to use this vector. - // - // On the other hand, somebody - // might have used the user indices - // already. So, let's be good - // citizens and save them before - // tampering with them. - std::vector old_user_indices; - triangulation.save_user_indices(old_user_indices); - - estimates.block(0).reinit(triangulation.n_active_cells()); - unsigned int i=0; - for (typename Triangulation::active_cell_iterator cell = triangulation.begin_active(); - cell != triangulation.end();++cell,++i) - cell->set_user_index(i); - - // This starts like before, - MeshWorker::IntegrationInfoBox info_box; - const unsigned int n_gauss_points = dof_handler.get_fe().tensor_degree()+1; - info_box.initialize_gauss_quadrature(n_gauss_points, n_gauss_points+1, n_gauss_points); - - // but now we need to notify the - // info box of the finite element - // functio we want to evaluate in - // the quadrature points. First, we - // create a NamedData object with - // this vector, which is the - // solution we just computed. - NamedData* > solution_data; - solution_data.add(&solution, "solution"); - - // Then, we tell the Meshworker::VectorSelector - // for cells, that we need the - // second derivatives of this - // solution (to compute the - // Laplacian). Therefore, the - // boolean arguments selecting - // function values and first - // derivatives a false, only the - // last one selecting second - // derivatives is true. - info_box.cell_selector.add("solution", false, false, true); - // On interior and boundary faces, - // we need the function values and - // the first derivatives, but not - // second derivatives. - info_box.boundary_selector.add("solution", true, true, false); - info_box.face_selector.add("solution", true, true, false); - - // And we continue as before, with - // the exception that the default - // update flags are already - // adjusted to the values and - // derivatives we requested above. - info_box.add_update_flags_boundary(update_quadrature_points); - info_box.initialize(fe, mapping, solution_data); - - MeshWorker::DoFInfo dof_info(dof_handler); - - // The assembler stores one number - // per cell, but else this is the - // same as in the computation of - // the right hand side. - MeshWorker::Assembler::CellsAndFaces assembler; - NamedData* > out_data; - BlockVector* est = &estimates; - out_data.add(est, "cells"); - assembler.initialize(out_data, false); - - MeshWorker::integration_loop ( - dof_handler.begin_active(), dof_handler.end(), - dof_info, info_box, - &Estimator::cell, - &Estimator::boundary, - &Estimator::face, - assembler); - - // Right before we return the - // result of the error estimate, we - // restore the old user indices. - triangulation.load_user_indices(old_user_indices); - return estimates.block(0).l2_norm(); -} + // Another clone of the assemble + // function. The big difference to + // the previous ones is here that we + // also have an input vector. + template + double + InteriorPenaltyProblem::estimate() + { + // The results of the estimator are + // stored in a vector with one + // entry per cell. Since cells in + // deal.II are not numbered, we + // have to create our own numbering + // in order to use this vector. + // + // On the other hand, somebody + // might have used the user indices + // already. So, let's be good + // citizens and save them before + // tampering with them. + std::vector old_user_indices; + triangulation.save_user_indices(old_user_indices); + + estimates.block(0).reinit(triangulation.n_active_cells()); + unsigned int i=0; + for (typename Triangulation::active_cell_iterator cell = triangulation.begin_active(); + cell != triangulation.end(); ++cell,++i) + cell->set_user_index(i); + + // This starts like before, + MeshWorker::IntegrationInfoBox info_box; + const unsigned int n_gauss_points = dof_handler.get_fe().tensor_degree()+1; + info_box.initialize_gauss_quadrature(n_gauss_points, n_gauss_points+1, n_gauss_points); + + // but now we need to notify the + // info box of the finite element + // functio we want to evaluate in + // the quadrature points. First, we + // create a NamedData object with + // this vector, which is the + // solution we just computed. + NamedData* > solution_data; + solution_data.add(&solution, "solution"); + + // Then, we tell the Meshworker::VectorSelector + // for cells, that we need the + // second derivatives of this + // solution (to compute the + // Laplacian). Therefore, the + // boolean arguments selecting + // function values and first + // derivatives a false, only the + // last one selecting second + // derivatives is true. + info_box.cell_selector.add("solution", false, false, true); + // On interior and boundary faces, + // we need the function values and + // the first derivatives, but not + // second derivatives. + info_box.boundary_selector.add("solution", true, true, false); + info_box.face_selector.add("solution", true, true, false); + + // And we continue as before, with + // the exception that the default + // update flags are already + // adjusted to the values and + // derivatives we requested above. + info_box.add_update_flags_boundary(update_quadrature_points); + info_box.initialize(fe, mapping, solution_data); - // Here we compare our finite element - // solution with the (known) exact - // solution and compute the mean - // quadratic error of the gradient - // and the function itself. This - // function is a clone of the - // estimation function right above. - - // Since we compute the error in the - // energy and the - // L2-norm, - // respectively, our block vector - // needs two blocks here. -template -void -InteriorPenaltyProblem::error() -{ - BlockVector errors(2); - errors.block(0).reinit(triangulation.n_active_cells()); - errors.block(1).reinit(triangulation.n_active_cells()); - unsigned int i=0; - for (typename Triangulation::active_cell_iterator cell = triangulation.begin_active(); - cell != triangulation.end();++cell,++i) - cell->set_user_index(i); - - MeshWorker::IntegrationInfoBox info_box; - const unsigned int n_gauss_points = dof_handler.get_fe().tensor_degree()+1; - info_box.initialize_gauss_quadrature(n_gauss_points, n_gauss_points+1, n_gauss_points); - - NamedData* > solution_data; - solution_data.add(&solution, "solution"); - - info_box.cell_selector.add("solution", true, true, false); - info_box.boundary_selector.add("solution", true, false, false); - info_box.face_selector.add("solution", true, false, false); - - info_box.add_update_flags_cell(update_quadrature_points); - info_box.add_update_flags_boundary(update_quadrature_points); - info_box.initialize(fe, mapping, solution_data); - - MeshWorker::DoFInfo dof_info(dof_handler); - - MeshWorker::Assembler::CellsAndFaces assembler; - NamedData* > out_data; - BlockVector* est = &errors; - out_data.add(est, "cells"); - assembler.initialize(out_data, false); - - MeshWorker::integration_loop ( - dof_handler.begin_active(), dof_handler.end(), - dof_info, info_box, - &ErrorIntegrator::cell, - &ErrorIntegrator::boundary, - &ErrorIntegrator::face, - assembler); - - deallog << "energy-error: " << errors.block(0).l2_norm() << std::endl; - deallog << "L2-error: " << errors.block(1).l2_norm() << std::endl; -} + MeshWorker::DoFInfo dof_info(dof_handler); + // The assembler stores one number + // per cell, but else this is the + // same as in the computation of + // the right hand side. + MeshWorker::Assembler::CellsAndFaces assembler; + NamedData* > out_data; + BlockVector *est = &estimates; + out_data.add(est, "cells"); + assembler.initialize(out_data, false); - // Some graphical output -template -void InteriorPenaltyProblem::output_results (const unsigned int cycle) const -{ - // Output of the solution in - // gnuplot format. - char * fn = new char[100]; - sprintf(fn, "sol-%02d", cycle); + MeshWorker::integration_loop ( + dof_handler.begin_active(), dof_handler.end(), + dof_info, info_box, + &Estimator::cell, + &Estimator::boundary, + &Estimator::face, + assembler); - std::string filename(fn); - filename += ".gnuplot"; - deallog << "Writing solution to <" << filename << ">..." - << std::endl << std::endl; - std::ofstream gnuplot_output (filename.c_str()); + // Right before we return the + // result of the error estimate, we + // restore the old user indices. + triangulation.load_user_indices(old_user_indices); + return estimates.block(0).l2_norm(); + } - DataOut data_out; - data_out.attach_dof_handler (dof_handler); - data_out.add_data_vector (solution, "u"); - data_out.add_data_vector (estimates.block(0), "est"); + // Here we compare our finite element + // solution with the (known) exact + // solution and compute the mean + // quadratic error of the gradient + // and the function itself. This + // function is a clone of the + // estimation function right above. + + // Since we compute the error in the + // energy and the + // L2-norm, + // respectively, our block vector + // needs two blocks here. + template + void + InteriorPenaltyProblem::error() + { + BlockVector errors(2); + errors.block(0).reinit(triangulation.n_active_cells()); + errors.block(1).reinit(triangulation.n_active_cells()); + unsigned int i=0; + for (typename Triangulation::active_cell_iterator cell = triangulation.begin_active(); + cell != triangulation.end(); ++cell,++i) + cell->set_user_index(i); - data_out.build_patches (); + MeshWorker::IntegrationInfoBox info_box; + const unsigned int n_gauss_points = dof_handler.get_fe().tensor_degree()+1; + info_box.initialize_gauss_quadrature(n_gauss_points, n_gauss_points+1, n_gauss_points); - data_out.write_gnuplot(gnuplot_output); -} + NamedData* > solution_data; + solution_data.add(&solution, "solution"); - // And finally the adaptive loop, - // more or less like in previous - // examples. -template -void -InteriorPenaltyProblem::run(unsigned int n_steps) -{ - deallog << "Element: " << fe.get_name() << std::endl; - for (unsigned int s=0;s dof_info(dof_handler); + + MeshWorker::Assembler::CellsAndFaces assembler; + NamedData* > out_data; + BlockVector *est = &errors; + out_data.add(est, "cells"); + assembler.initialize(out_data, false); + + MeshWorker::integration_loop ( + dof_handler.begin_active(), dof_handler.end(), + dof_info, info_box, + &ErrorIntegrator::cell, + &ErrorIntegrator::boundary, + &ErrorIntegrator::face, + assembler); + + deallog << "energy-error: " << errors.block(0).l2_norm() << std::endl; + deallog << "L2-error: " << errors.block(1).l2_norm() << std::endl; + } + + + // Some graphical output + template + void InteriorPenaltyProblem::output_results (const unsigned int cycle) const + { + // Output of the solution in + // gnuplot format. + char *fn = new char[100]; + sprintf(fn, "sol-%02d", cycle); + + std::string filename(fn); + filename += ".gnuplot"; + deallog << "Writing solution to <" << filename << ">..." + << std::endl << std::endl; + std::ofstream gnuplot_output (filename.c_str()); + + DataOut data_out; + data_out.attach_dof_handler (dof_handler); + data_out.add_data_vector (solution, "u"); + data_out.add_data_vector (estimates.block(0), "est"); + + data_out.build_patches (); + + data_out.write_gnuplot(gnuplot_output); + } + + // And finally the adaptive loop, + // more or less like in previous + // examples. + template + void + InteriorPenaltyProblem::run(unsigned int n_steps) + { + deallog << "Element: " << fe.get_name() << std::endl; + for (unsigned int s=0; s #include #include @@ -40,124 +40,124 @@ #include #include - // This is new, however: in the previous - // example we got some unwanted output from - // the linear solvers. If we want to suppress - // it, we have to include this file and add a - // single line somewhere to the program (see - // the main() function below for that): +// This is new, however: in the previous +// example we got some unwanted output from +// the linear solvers. If we want to suppress +// it, we have to include this file and add a +// single line somewhere to the program (see +// the main() function below for that): #include - // The final step, as in previous - // programs, is to import all the - // deal.II class and function names - // into the global namespace: +// The final step, as in previous +// programs, is to import all the +// deal.II class and function names +// into the global namespace: using namespace dealii; - // @sect3{The Step4 class template} - - // This is again the same - // Step4 class as in the - // previous example. The only - // difference is that we have now - // declared it as a class with a - // template parameter, and the - // template parameter is of course - // the spatial dimension in which we - // would like to solve the Laplace - // equation. Of course, several of - // the member variables depend on - // this dimension as well, in - // particular the Triangulation - // class, which has to represent - // quadrilaterals or hexahedra, - // respectively. Apart from this, - // everything is as before. +// @sect3{The Step4 class template} + +// This is again the same +// Step4 class as in the +// previous example. The only +// difference is that we have now +// declared it as a class with a +// template parameter, and the +// template parameter is of course +// the spatial dimension in which we +// would like to solve the Laplace +// equation. Of course, several of +// the member variables depend on +// this dimension as well, in +// particular the Triangulation +// class, which has to represent +// quadrilaterals or hexahedra, +// respectively. Apart from this, +// everything is as before. template class Step4 { - public: - Step4 (); - void run (); - - private: - void make_grid (); - void setup_system(); - void assemble_system (); - void solve (); - void output_results () const; - - Triangulation triangulation; - FE_Q fe; - DoFHandler dof_handler; - - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - - Vector solution; - Vector system_rhs; +public: + Step4 (); + void run (); + +private: + void make_grid (); + void setup_system(); + void assemble_system (); + void solve (); + void output_results () const; + + Triangulation triangulation; + FE_Q fe; + DoFHandler dof_handler; + + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + + Vector solution; + Vector system_rhs; }; - // @sect3{Right hand side and boundary values} - - // In the following, we declare two more - // classes denoting the right hand side and - // the non-homogeneous Dirichlet boundary - // values. Both are functions of a - // dim-dimensional space variable, so we - // declare them as templates as well. - // - // Each of these classes is derived from a - // common, abstract base class Function, - // which declares the common interface which - // all functions have to follow. In - // particular, concrete classes have to - // overload the value function, - // which takes a point in dim-dimensional - // space as parameters and shall return the - // value at that point as a - // double variable. - // - // The value function takes a - // second argument, which we have here named - // component: This is only meant - // for vector valued functions, where you may - // want to access a certain component of the - // vector at the point - // p. However, our functions are - // scalar, so we need not worry about this - // parameter and we will not use it in the - // implementation of the functions. Inside - // the library's header files, the Function - // base class's declaration of the - // value function has a default - // value of zero for the component, so we - // will access the value - // function of the right hand side with only - // one parameter, namely the point where we - // want to evaluate the function. A value for - // the component can then simply be omitted - // for scalar functions. - // - // Note that the C++ language forces - // us to declare and define a - // constructor to the following - // classes even though they are - // empty. This is due to the fact - // that the base class has no default - // constructor (i.e. one without - // arguments), even though it has a - // constructor which has default - // values for all arguments. +// @sect3{Right hand side and boundary values} + +// In the following, we declare two more +// classes denoting the right hand side and +// the non-homogeneous Dirichlet boundary +// values. Both are functions of a +// dim-dimensional space variable, so we +// declare them as templates as well. +// +// Each of these classes is derived from a +// common, abstract base class Function, +// which declares the common interface which +// all functions have to follow. In +// particular, concrete classes have to +// overload the value function, +// which takes a point in dim-dimensional +// space as parameters and shall return the +// value at that point as a +// double variable. +// +// The value function takes a +// second argument, which we have here named +// component: This is only meant +// for vector valued functions, where you may +// want to access a certain component of the +// vector at the point +// p. However, our functions are +// scalar, so we need not worry about this +// parameter and we will not use it in the +// implementation of the functions. Inside +// the library's header files, the Function +// base class's declaration of the +// value function has a default +// value of zero for the component, so we +// will access the value +// function of the right hand side with only +// one parameter, namely the point where we +// want to evaluate the function. A value for +// the component can then simply be omitted +// for scalar functions. +// +// Note that the C++ language forces +// us to declare and define a +// constructor to the following +// classes even though they are +// empty. This is due to the fact +// that the base class has no default +// constructor (i.e. one without +// arguments), even though it has a +// constructor which has default +// values for all arguments. template class RightHandSide : public Function { - public: - RightHandSide () : Function() {} +public: + RightHandSide () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -165,49 +165,49 @@ class RightHandSide : public Function template class BoundaryValues : public Function { - public: - BoundaryValues () : Function() {} +public: + BoundaryValues () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; - // For this example, we choose as right hand - // side function to function $4(x^4+y^4)$ in - // 2D, or $4(x^4+y^4+z^4)$ in 3D. We could - // write this distinction using an - // if-statement on the space dimension, but - // here is a simple way that also allows us - // to use the same function in 1D (or in 4D, - // if you should desire to do so), by using a - // short loop. Fortunately, the compiler - // knows the size of the loop at compile time - // (remember that at the time when you define - // the template, the compiler doesn't know - // the value of dim, but when it later - // encounters a statement or declaration - // RightHandSide@<2@>, it will take the - // template, replace all occurrences of dim - // by 2 and compile the resulting function); - // in other words, at the time of compiling - // this function, the number of times the - // body will be executed is known, and the - // compiler can optimize away the overhead - // needed for the loop and the result will be - // as fast as if we had used the formulas - // above right away. - // - // The last thing to note is that a - // Point@ denotes a point in - // dim-dimensionsal space, and its individual - // components (i.e. $x$, $y$, - // ... coordinates) can be accessed using the - // () operator (in fact, the [] operator will - // work just as well) with indices starting - // at zero as usual in C and C++. +// For this example, we choose as right hand +// side function to function $4(x^4+y^4)$ in +// 2D, or $4(x^4+y^4+z^4)$ in 3D. We could +// write this distinction using an +// if-statement on the space dimension, but +// here is a simple way that also allows us +// to use the same function in 1D (or in 4D, +// if you should desire to do so), by using a +// short loop. Fortunately, the compiler +// knows the size of the loop at compile time +// (remember that at the time when you define +// the template, the compiler doesn't know +// the value of dim, but when it later +// encounters a statement or declaration +// RightHandSide@<2@>, it will take the +// template, replace all occurrences of dim +// by 2 and compile the resulting function); +// in other words, at the time of compiling +// this function, the number of times the +// body will be executed is known, and the +// compiler can optimize away the overhead +// needed for the loop and the result will be +// as fast as if we had used the formulas +// above right away. +// +// The last thing to note is that a +// Point@ denotes a point in +// dim-dimensionsal space, and its individual +// components (i.e. $x$, $y$, +// ... coordinates) can be accessed using the +// () operator (in fact, the [] operator will +// work just as well) with indices starting +// at zero as usual in C and C++. template double RightHandSide::value (const Point &p, const unsigned int /*component*/) const @@ -220,13 +220,13 @@ double RightHandSide::value (const Point &p, } - // As boundary values, we choose x*x+y*y in - // 2D, and x*x+y*y+z*z in 3D. This happens to - // be equal to the square of the vector from - // the origin to the point at which we would - // like to evaluate the function, - // irrespective of the dimension. So that is - // what we return: +// As boundary values, we choose x*x+y*y in +// 2D, and x*x+y*y+z*z in 3D. This happens to +// be equal to the square of the vector from +// the origin to the point at which we would +// like to evaluate the function, +// irrespective of the dimension. So that is +// what we return: template double BoundaryValues::value (const Point &p, const unsigned int /*component*/) const @@ -236,84 +236,84 @@ double BoundaryValues::value (const Point &p, - // @sect3{Implementation of the Step4 class} - - // Next for the implementation of the class - // template that makes use of the functions - // above. As before, we will write everything - // as templates that have a formal parameter - // dim that we assume unknown at - // the time we define the template - // functions. Only later, the compiler will - // find a declaration of - // Step4@<2@> (in the - // main function, actually) and - // compile the entire class with - // dim replaced by 2, a process - // referred to as `instantiation of a - // template'. When doing so, it will also - // replace instances of - // RightHandSide@ by - // RightHandSide@<2@> and - // instantiate the latter class from the - // class template. - // - // In fact, the compiler will also find a - // declaration - // Step4@<3@> in - // main(). This will cause it to - // again go back to the general - // Step4@ - // template, replace all occurrences of - // dim, this time by 3, and - // compile the class a second time. Note that - // the two instantiations - // Step4@<2@> and - // Step4@<3@> are - // completely independent classes; their only - // common feature is that they are both - // instantiated from the same general - // template, but they are not convertible - // into each other, for example, and share no - // code (both instantiations are compiled - // completely independently). - - - // @sect4{Step4::Step4} - - // After this introduction, here is the - // constructor of the Step4 - // class. It specifies the desired polynomial - // degree of the finite elements and - // associates the DoFHandler to the - // triangulation just as in the previous - // example program, step-3: +// @sect3{Implementation of the Step4 class} + +// Next for the implementation of the class +// template that makes use of the functions +// above. As before, we will write everything +// as templates that have a formal parameter +// dim that we assume unknown at +// the time we define the template +// functions. Only later, the compiler will +// find a declaration of +// Step4@<2@> (in the +// main function, actually) and +// compile the entire class with +// dim replaced by 2, a process +// referred to as `instantiation of a +// template'. When doing so, it will also +// replace instances of +// RightHandSide@ by +// RightHandSide@<2@> and +// instantiate the latter class from the +// class template. +// +// In fact, the compiler will also find a +// declaration +// Step4@<3@> in +// main(). This will cause it to +// again go back to the general +// Step4@ +// template, replace all occurrences of +// dim, this time by 3, and +// compile the class a second time. Note that +// the two instantiations +// Step4@<2@> and +// Step4@<3@> are +// completely independent classes; their only +// common feature is that they are both +// instantiated from the same general +// template, but they are not convertible +// into each other, for example, and share no +// code (both instantiations are compiled +// completely independently). + + +// @sect4{Step4::Step4} + +// After this introduction, here is the +// constructor of the Step4 +// class. It specifies the desired polynomial +// degree of the finite elements and +// associates the DoFHandler to the +// triangulation just as in the previous +// example program, step-3: template Step4::Step4 () - : - fe (1), - dof_handler (triangulation) + : + fe (1), + dof_handler (triangulation) {} - // @sect4{Step4::make_grid} - - // Grid creation is something inherently - // dimension dependent. However, as long as - // the domains are sufficiently similar in 2D - // or 3D, the library can abstract for - // you. In our case, we would like to again - // solve on the square $[-1,1]\times [-1,1]$ - // in 2D, or on the cube $[-1,1] \times - // [-1,1] \times [-1,1]$ in 3D; both can be - // termed GridGenerator::hyper_cube(), so we may - // use the same function in whatever - // dimension we are. Of course, the functions - // that create a hypercube in two and three - // dimensions are very much different, but - // that is something you need not care - // about. Let the library handle the - // difficult things. +// @sect4{Step4::make_grid} + +// Grid creation is something inherently +// dimension dependent. However, as long as +// the domains are sufficiently similar in 2D +// or 3D, the library can abstract for +// you. In our case, we would like to again +// solve on the square $[-1,1]\times [-1,1]$ +// in 2D, or on the cube $[-1,1] \times +// [-1,1] \times [-1,1]$ in 3D; both can be +// termed GridGenerator::hyper_cube(), so we may +// use the same function in whatever +// dimension we are. Of course, the functions +// that create a hypercube in two and three +// dimensions are very much different, but +// that is something you need not care +// about. Let the library handle the +// difficult things. template void Step4::make_grid () { @@ -328,17 +328,17 @@ void Step4::make_grid () << std::endl; } - // @sect4{Step4::setup_system} - - // This function looks - // exactly like in the previous example, - // although it performs actions that in their - // details are quite different if - // dim happens to be 3. The only - // significant difference from a user's - // perspective is the number of cells - // resulting, which is much higher in three - // than in two space dimensions! +// @sect4{Step4::setup_system} + +// This function looks +// exactly like in the previous example, +// although it performs actions that in their +// details are quite different if +// dim happens to be 3. The only +// significant difference from a user's +// perspective is the number of cells +// resulting, which is much higher in three +// than in two space dimensions! template void Step4::setup_system () { @@ -359,78 +359,78 @@ void Step4::setup_system () } - // @sect4{Step4::assemble_system} - - // Unlike in the previous example, we - // would now like to use a - // non-constant right hand side - // function and non-zero boundary - // values. Both are tasks that are - // readily achieved with only a few - // new lines of code in the - // assemblage of the matrix and right - // hand side. - // - // More interesting, though, is the - // way we assemble matrix and right - // hand side vector dimension - // independently: there is simply no - // difference to the - // two-dimensional case. Since the - // important objects used in this - // function (quadrature formula, - // FEValues) depend on the dimension - // by way of a template parameter as - // well, they can take care of - // setting up properly everything for - // the dimension for which this - // function is compiled. By declaring - // all classes which might depend on - // the dimension using a template - // parameter, the library can make - // nearly all work for you and you - // don't have to care about most - // things. +// @sect4{Step4::assemble_system} + +// Unlike in the previous example, we +// would now like to use a +// non-constant right hand side +// function and non-zero boundary +// values. Both are tasks that are +// readily achieved with only a few +// new lines of code in the +// assemblage of the matrix and right +// hand side. +// +// More interesting, though, is the +// way we assemble matrix and right +// hand side vector dimension +// independently: there is simply no +// difference to the +// two-dimensional case. Since the +// important objects used in this +// function (quadrature formula, +// FEValues) depend on the dimension +// by way of a template parameter as +// well, they can take care of +// setting up properly everything for +// the dimension for which this +// function is compiled. By declaring +// all classes which might depend on +// the dimension using a template +// parameter, the library can make +// nearly all work for you and you +// don't have to care about most +// things. template void Step4::assemble_system () { QGauss quadrature_formula(2); - // We wanted to have a non-constant right - // hand side, so we use an object of the - // class declared above to generate the - // necessary data. Since this right hand - // side object is only used locally in the - // present function, we declare it here as - // a local variable: + // We wanted to have a non-constant right + // hand side, so we use an object of the + // class declared above to generate the + // necessary data. Since this right hand + // side object is only used locally in the + // present function, we declare it here as + // a local variable: const RightHandSide right_hand_side; - // Compared to the previous example, in - // order to evaluate the non-constant right - // hand side function we now also need the - // quadrature points on the cell we are - // presently on (previously, we only - // required values and gradients of the - // shape function from the - // FEValues object, as well as - // the quadrature weights, - // FEValues::JxW() ). We can tell the - // FEValues object to do for - // us by also giving it the - // #update_quadrature_points - // flag: + // Compared to the previous example, in + // order to evaluate the non-constant right + // hand side function we now also need the + // quadrature points on the cell we are + // presently on (previously, we only + // required values and gradients of the + // shape function from the + // FEValues object, as well as + // the quadrature weights, + // FEValues::JxW() ). We can tell the + // FEValues object to do for + // us by also giving it the + // #update_quadrature_points + // flag: FEValues fe_values (fe, quadrature_formula, update_values | update_gradients | update_quadrature_points | update_JxW_values); - // We then again define a few - // abbreviations. The values of these - // variables of course depend on the - // dimension which we are presently - // using. However, the FE and Quadrature - // classes do all the necessary work for - // you and you don't have to care about the - // dimension dependent parts: + // We then again define a few + // abbreviations. The values of these + // variables of course depend on the + // dimension which we are presently + // using. However, the FE and Quadrature + // classes do all the necessary work for + // you and you don't have to care about the + // dimension dependent parts: const unsigned int dofs_per_cell = fe.dofs_per_cell; const unsigned int n_q_points = quadrature_formula.size(); @@ -439,22 +439,22 @@ void Step4::assemble_system () std::vector local_dof_indices (dofs_per_cell); - // Next, we again have to loop over all - // cells and assemble local contributions. - // Note, that a cell is a quadrilateral in - // two space dimensions, but a hexahedron - // in 3D. In fact, the - // active_cell_iterator data - // type is something different, depending - // on the dimension we are in, but to the - // outside world they look alike and you - // will probably never see a difference - // although the classes that this typedef - // stands for are in fact completely - // unrelated: + // Next, we again have to loop over all + // cells and assemble local contributions. + // Note, that a cell is a quadrilateral in + // two space dimensions, but a hexahedron + // in 3D. In fact, the + // active_cell_iterator data + // type is something different, depending + // on the dimension we are in, but to the + // outside world they look alike and you + // will probably never see a difference + // although the classes that this typedef + // stands for are in fact completely + // unrelated: typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { @@ -462,28 +462,28 @@ void Step4::assemble_system () cell_matrix = 0; cell_rhs = 0; - // Now we have to assemble the - // local matrix and right hand - // side. This is done exactly - // like in the previous - // example, but now we revert - // the order of the loops - // (which we can safely do - // since they are independent - // of each other) and merge the - // loops for the local matrix - // and the local vector as far - // as possible to make - // things a bit faster. - // - // Assembling the right hand side - // presents the only significant - // difference to how we did things in - // step-3: Instead of using a constant - // right hand side with value 1, we use - // the object representing the right - // hand side and evaluate it at the - // quadrature points: + // Now we have to assemble the + // local matrix and right hand + // side. This is done exactly + // like in the previous + // example, but now we revert + // the order of the loops + // (which we can safely do + // since they are independent + // of each other) and merge the + // loops for the local matrix + // and the local vector as far + // as possible to make + // things a bit faster. + // + // Assembling the right hand side + // presents the only significant + // difference to how we did things in + // step-3: Instead of using a constant + // right hand side with value 1, we use + // the object representing the right + // hand side and evaluate it at the + // quadrature points: for (unsigned int q_point=0; q_point::assemble_system () right_hand_side.value (fe_values.quadrature_point (q_point)) * fe_values.JxW (q_point)); } - // As a final remark to these loops: - // when we assemble the local - // contributions into - // cell_matrix(i,j), we - // have to multiply the gradients of - // shape functions $i$ and $j$ at point - // q_point and multiply it with the - // scalar weights JxW. This is what - // actually happens: - // fe_values.shape_grad(i,q_point) - // returns a dim - // dimensional vector, represented by a - // Tensor@<1,dim@> object, - // and the operator* that multiplies it - // with the result of - // fe_values.shape_grad(j,q_point) - // makes sure that the dim - // components of the two vectors are - // properly contracted, and the result - // is a scalar floating point number - // that then is multiplied with the - // weights. Internally, this operator* - // makes sure that this happens - // correctly for all dim - // components of the vectors, whether - // dim be 2, 3, or any - // other space dimension; from a user's - // perspective, this is not something - // worth bothering with, however, - // making things a lot simpler if one - // wants to write code dimension - // independently. - - // With the local systems assembled, - // the transfer into the global matrix - // and right hand side is done exactly - // as before, but here we have again - // merged some loops for efficiency: + // As a final remark to these loops: + // when we assemble the local + // contributions into + // cell_matrix(i,j), we + // have to multiply the gradients of + // shape functions $i$ and $j$ at point + // q_point and multiply it with the + // scalar weights JxW. This is what + // actually happens: + // fe_values.shape_grad(i,q_point) + // returns a dim + // dimensional vector, represented by a + // Tensor@<1,dim@> object, + // and the operator* that multiplies it + // with the result of + // fe_values.shape_grad(j,q_point) + // makes sure that the dim + // components of the two vectors are + // properly contracted, and the result + // is a scalar floating point number + // that then is multiplied with the + // weights. Internally, this operator* + // makes sure that this happens + // correctly for all dim + // components of the vectors, whether + // dim be 2, 3, or any + // other space dimension; from a user's + // perspective, this is not something + // worth bothering with, however, + // making things a lot simpler if one + // wants to write code dimension + // independently. + + // With the local systems assembled, + // the transfer into the global matrix + // and right hand side is done exactly + // as before, but here we have again + // merged some loops for efficiency: cell->get_dof_indices (local_dof_indices); for (unsigned int i=0; i::assemble_system () } - // As the final step in this function, we - // wanted to have non-homogeneous boundary - // values in this example, unlike the one - // before. This is a simple task, we only - // have to replace the - // ZeroFunction used there by - // an object of the class which describes - // the boundary values we would like to use - // (i.e. the BoundaryValues - // class declared above): + // As the final step in this function, we + // wanted to have non-homogeneous boundary + // values in this example, unlike the one + // before. This is a simple task, we only + // have to replace the + // ZeroFunction used there by + // an object of the class which describes + // the boundary values we would like to use + // (i.e. the BoundaryValues + // class declared above): std::map boundary_values; VectorTools::interpolate_boundary_values (dof_handler, 0, @@ -569,15 +569,15 @@ void Step4::assemble_system () } - // @sect4{Step4::solve} +// @sect4{Step4::solve} - // Solving the linear system of - // equations is something that looks - // almost identical in most - // programs. In particular, it is - // dimension independent, so this - // function is copied verbatim from the - // previous example. +// Solving the linear system of +// equations is something that looks +// almost identical in most +// programs. In particular, it is +// dimension independent, so this +// function is copied verbatim from the +// previous example. template void Step4::solve () { @@ -586,44 +586,44 @@ void Step4::solve () solver.solve (system_matrix, solution, system_rhs, PreconditionIdentity()); - // We have made one addition, - // though: since we suppress output - // from the linear solvers, we have - // to print the number of - // iterations by hand. + // We have made one addition, + // though: since we suppress output + // from the linear solvers, we have + // to print the number of + // iterations by hand. std::cout << " " << solver_control.last_step() << " CG iterations needed to obtain convergence." << std::endl; } - // @sect4{Step4::output_results} - - // This function also does what the - // respective one did in step-3. No changes - // here for dimension independence either. - // - // The only difference to the previous - // example is that we want to write output in - // VTK format, rather than for gnuplot. VTK - // format is currently the most widely used - // one and is supported by a number of - // visualization programs such as Visit and - // Paraview (for ways to obtain these - // programs see the ReadMe file of - // deal.II). To write data in this format, we - // simply replace the - // data_out.write_gnuplot call - // by data_out.write_vtk. - // - // Since the program will run both 2d and 3d - // versions of the laplace solver, we use the - // dimension in the filename to generate - // distinct filenames for each run (in a - // better program, one would check whether - // dim can have other values - // than 2 or 3, but we neglect this here for - // the sake of brevity). +// @sect4{Step4::output_results} + +// This function also does what the +// respective one did in step-3. No changes +// here for dimension independence either. +// +// The only difference to the previous +// example is that we want to write output in +// VTK format, rather than for gnuplot. VTK +// format is currently the most widely used +// one and is supported by a number of +// visualization programs such as Visit and +// Paraview (for ways to obtain these +// programs see the ReadMe file of +// deal.II). To write data in this format, we +// simply replace the +// data_out.write_gnuplot call +// by data_out.write_vtk. +// +// Since the program will run both 2d and 3d +// versions of the laplace solver, we use the +// dimension in the filename to generate +// distinct filenames for each run (in a +// better program, one would check whether +// dim can have other values +// than 2 or 3, but we neglect this here for +// the sake of brevity). template void Step4::output_results () const { @@ -642,13 +642,13 @@ void Step4::output_results () const - // @sect4{Step4::run} +// @sect4{Step4::run} - // This is the function which has the - // top-level control over - // everything. Apart from one line of - // additional output, it is the same - // as for the previous example. +// This is the function which has the +// top-level control over +// everything. Apart from one line of +// additional output, it is the same +// as for the previous example. template void Step4::run () { @@ -662,81 +662,81 @@ void Step4::run () } - // @sect3{The main function} - - // And this is the main function. It also - // looks mostly like in step-3, but if you - // look at the code below, note how we first - // create a variable of type - // Step4@<2@> (forcing - // the compiler to compile the class template - // with dim replaced by - // 2) and run a 2d simulation, - // and then we do the whole thing over in 3d. - // - // In practice, this is probably not what you - // would do very frequently (you probably - // either want to solve a 2d problem, or one - // in 3d, but not both at the same - // time). However, it demonstrates the - // mechanism by which we can simply change - // which dimension we want in a single place, - // and thereby force the compiler to - // recompile the dimension independent class - // templates for the dimension we - // request. The emphasis here lies on the - // fact that we only need to change a single - // place. This makes it rather trivial to - // debug the program in 2d where computations - // are fast, and then switch a single place - // to a 3 to run the much more computing - // intensive program in 3d for `real' - // computations. - // - // Each of the two blocks is enclosed in - // braces to make sure that the - // laplace_problem_2d variable - // goes out of scope (and releases the memory - // it holds) before we move on to allocate - // memory for the 3d case. Without the - // additional braces, the - // laplace_problem_2d variable - // would only be destroyed at the end of the - // function, i.e. after running the 3d - // problem, and would needlessly hog memory - // while the 3d run could actually use it. - // - // Finally, the first line of the function is - // used to suppress some output. Remember - // that in the previous example, we had the - // output from the linear solvers about the - // starting residual and the number of the - // iteration where convergence was - // detected. This can be suppressed through - // the deallog.depth_console(0) - // call. - // - // The rationale here is the following: the - // deallog (i.e. deal-log, not de-allog) - // variable represents a stream to which some - // parts of the library write output. It - // redirects this output to the console and - // if required to a file. The output is - // nested in a way so that each function can - // use a prefix string (separated by colons) - // for each line of output; if it calls - // another function, that may also use its - // prefix which is then printed after the one - // of the calling function. Since output from - // functions which are nested deep below is - // usually not as important as top-level - // output, you can give the deallog variable - // a maximal depth of nested output for - // output to console and file. The depth zero - // which we gave here means that no output is - // written. By changing it you can get more - // information about the innards of the - // library. +// @sect3{The main function} + +// And this is the main function. It also +// looks mostly like in step-3, but if you +// look at the code below, note how we first +// create a variable of type +// Step4@<2@> (forcing +// the compiler to compile the class template +// with dim replaced by +// 2) and run a 2d simulation, +// and then we do the whole thing over in 3d. +// +// In practice, this is probably not what you +// would do very frequently (you probably +// either want to solve a 2d problem, or one +// in 3d, but not both at the same +// time). However, it demonstrates the +// mechanism by which we can simply change +// which dimension we want in a single place, +// and thereby force the compiler to +// recompile the dimension independent class +// templates for the dimension we +// request. The emphasis here lies on the +// fact that we only need to change a single +// place. This makes it rather trivial to +// debug the program in 2d where computations +// are fast, and then switch a single place +// to a 3 to run the much more computing +// intensive program in 3d for `real' +// computations. +// +// Each of the two blocks is enclosed in +// braces to make sure that the +// laplace_problem_2d variable +// goes out of scope (and releases the memory +// it holds) before we move on to allocate +// memory for the 3d case. Without the +// additional braces, the +// laplace_problem_2d variable +// would only be destroyed at the end of the +// function, i.e. after running the 3d +// problem, and would needlessly hog memory +// while the 3d run could actually use it. +// +// Finally, the first line of the function is +// used to suppress some output. Remember +// that in the previous example, we had the +// output from the linear solvers about the +// starting residual and the number of the +// iteration where convergence was +// detected. This can be suppressed through +// the deallog.depth_console(0) +// call. +// +// The rationale here is the following: the +// deallog (i.e. deal-log, not de-allog) +// variable represents a stream to which some +// parts of the library write output. It +// redirects this output to the console and +// if required to a file. The output is +// nested in a way so that each function can +// use a prefix string (separated by colons) +// for each line of output; if it calls +// another function, that may also use its +// prefix which is then printed after the one +// of the calling function. Since output from +// functions which are nested deep below is +// usually not as important as top-level +// output, you can give the deallog variable +// a maximal depth of nested output for +// output to console and file. The depth zero +// which we gave here means that no output is +// written. By changing it you can get more +// information about the innards of the +// library. int main () { deallog.depth_console (0); diff --git a/deal.II/examples/step-40/step-40.cc b/deal.II/examples/step-40/step-40.cc index 229b562d29..89be9d6284 100644 --- a/deal.II/examples/step-40/step-40.cc +++ b/deal.II/examples/step-40/step-40.cc @@ -11,13 +11,13 @@ /* further information on this license. */ - // @sect3{Include files} - // - // Most of the include files we need for this - // program have already been discussed in - // previous programs. In particular, all of - // the following should already be familiar - // friends: +// @sect3{Include files} +// +// Most of the include files we need for this +// program have already been discussed in +// previous programs. In particular, all of +// the following should already be familiar +// friends: #include #include #include @@ -43,81 +43,81 @@ #include #include - // The following, however, will be new or be - // used in new roles. Let's walk through - // them. The first of these will provide the - // tools of the Utilities::System namespace - // that we will use to query things like the - // number of processors associated with the - // current MPI universe, or the number within - // this universe the processor this job runs - // on is: +// The following, however, will be new or be +// used in new roles. Let's walk through +// them. The first of these will provide the +// tools of the Utilities::System namespace +// that we will use to query things like the +// number of processors associated with the +// current MPI universe, or the number within +// this universe the processor this job runs +// on is: #include - // The next one provides a class, - // ConditionOStream that allows us to write - // code that would output things to a stream - // (such as std::cout on every - // processor but throws the text away on all - // but one of them. We could achieve the same - // by simply putting an if - // statement in front of each place where we - // may generate output, but this doesn't make - // the code any prettier. In addition, the - // condition whether this processor should or - // should not produce output to the screen is - // the same every time -- and consequently it - // should be simple enough to put it into the - // statements that generate output itself. +// The next one provides a class, +// ConditionOStream that allows us to write +// code that would output things to a stream +// (such as std::cout on every +// processor but throws the text away on all +// but one of them. We could achieve the same +// by simply putting an if +// statement in front of each place where we +// may generate output, but this doesn't make +// the code any prettier. In addition, the +// condition whether this processor should or +// should not produce output to the screen is +// the same every time -- and consequently it +// should be simple enough to put it into the +// statements that generate output itself. #include - // After these preliminaries, here is where - // it becomes more interesting. As mentioned - // in the @ref distributed module, one of the - // fundamental truths of solving problems on - // large numbers of processors is that there - // is no way for any processor to store - // everything (e.g. information about all - // cells in the mesh, all degrees of freedom, - // or the values of all elements of the - // solution vector). Rather, every processor - // will own a few of each of these - // and, if necessary, may know about a - // few more, for example the ones that are - // located on cells adjacent to the ones this - // processor owns itself. We typically call - // the latter ghost cells, ghost - // nodes or ghost elements of a - // vector. The point of this discussion - // here is that we need to have a way to - // indicate which elements a particular - // processor owns or need to know of. This is - // the realm of the IndexSet class: if there - // are a total of $N$ cells, degrees of - // freedom, or vector elements, associated - // with (non-negative) integral indices - // $[0,N)$, then both the set of elements the - // current processor owns as well as the - // (possibly larger) set of indices it needs - // to know about are subsets of the set - // $[0,N)$. IndexSet is a class that stores - // subsets of this set in an efficient - // format: +// After these preliminaries, here is where +// it becomes more interesting. As mentioned +// in the @ref distributed module, one of the +// fundamental truths of solving problems on +// large numbers of processors is that there +// is no way for any processor to store +// everything (e.g. information about all +// cells in the mesh, all degrees of freedom, +// or the values of all elements of the +// solution vector). Rather, every processor +// will own a few of each of these +// and, if necessary, may know about a +// few more, for example the ones that are +// located on cells adjacent to the ones this +// processor owns itself. We typically call +// the latter ghost cells, ghost +// nodes or ghost elements of a +// vector. The point of this discussion +// here is that we need to have a way to +// indicate which elements a particular +// processor owns or need to know of. This is +// the realm of the IndexSet class: if there +// are a total of $N$ cells, degrees of +// freedom, or vector elements, associated +// with (non-negative) integral indices +// $[0,N)$, then both the set of elements the +// current processor owns as well as the +// (possibly larger) set of indices it needs +// to know about are subsets of the set +// $[0,N)$. IndexSet is a class that stores +// subsets of this set in an efficient +// format: #include - // The next header file is necessary for a - // single function, - // SparsityTools::distribute_sparsity_pattern. The - // role of this function will be explained - // below. +// The next header file is necessary for a +// single function, +// SparsityTools::distribute_sparsity_pattern. The +// role of this function will be explained +// below. #include - // The final two, new header files provide - // the class - // parallel::distributed::Triangulation that - // provides meshes distributed across a - // potentially very large number of - // processors, while the second provides the - // namespace - // parallel::distributed::GridRefinement that - // offers functions that can adaptively - // refine such distributed meshes: +// The final two, new header files provide +// the class +// parallel::distributed::Triangulation that +// provides meshes distributed across a +// potentially very large number of +// processors, while the second provides the +// namespace +// parallel::distributed::GridRefinement that +// offers functions that can adaptively +// refine such distributed meshes: #include #include @@ -128,112 +128,112 @@ namespace Step40 { using namespace dealii; - // @sect3{The LaplaceProblem class template} - - // Next let's declare the main class of this - // program. Its structure is almost exactly - // that of the step-6 tutorial program. The - // only significant differences are: - // - The mpi_communicator - // variable that describes the set of - // processors we want this code to run - // on. In practice, this will be - // MPI_COMM_WORLD, i.e. all processors the - // batch scheduling system has assigned to - // this particular job. - // - The presence of the pcout - // variable of type ConditionOStream. - // - The obvious use of - // parallel::distributed::Triangulation - // instead of Triangulation. - // - The presence of two IndexSet objects - // that denote which sets of degrees of - // freedom (and associated elements of - // solution and right hand side vectors) we - // own on the current processor and which - // we need (as ghost elements) for the - // algorithms in this program to work. - // - The fact that all matrices and - // vectors are now distributed. We - // use their PETScWrapper versions - // for this since deal.II's own - // classes do not provide %parallel - // functionality. Note that as part - // of this class, we store a - // solution vector that does not - // only contain the degrees of - // freedom the current processor - // owns, but also (as ghost - // elements) all those vector - // elements that correspond to - // "locally relevant" degrees of - // freedom (i.e. all those that - // live on locally owned cells or - // the layer of ghost cells that - // surround it). + // @sect3{The LaplaceProblem class template} + + // Next let's declare the main class of this + // program. Its structure is almost exactly + // that of the step-6 tutorial program. The + // only significant differences are: + // - The mpi_communicator + // variable that describes the set of + // processors we want this code to run + // on. In practice, this will be + // MPI_COMM_WORLD, i.e. all processors the + // batch scheduling system has assigned to + // this particular job. + // - The presence of the pcout + // variable of type ConditionOStream. + // - The obvious use of + // parallel::distributed::Triangulation + // instead of Triangulation. + // - The presence of two IndexSet objects + // that denote which sets of degrees of + // freedom (and associated elements of + // solution and right hand side vectors) we + // own on the current processor and which + // we need (as ghost elements) for the + // algorithms in this program to work. + // - The fact that all matrices and + // vectors are now distributed. We + // use their PETScWrapper versions + // for this since deal.II's own + // classes do not provide %parallel + // functionality. Note that as part + // of this class, we store a + // solution vector that does not + // only contain the degrees of + // freedom the current processor + // owns, but also (as ghost + // elements) all those vector + // elements that correspond to + // "locally relevant" degrees of + // freedom (i.e. all those that + // live on locally owned cells or + // the layer of ghost cells that + // surround it). template class LaplaceProblem { - public: - LaplaceProblem (); - ~LaplaceProblem (); + public: + LaplaceProblem (); + ~LaplaceProblem (); - void run (); + void run (); - private: - void setup_system (); - void assemble_system (); - void solve (); - void refine_grid (); - void output_results (const unsigned int cycle) const; + private: + void setup_system (); + void assemble_system (); + void solve (); + void refine_grid (); + void output_results (const unsigned int cycle) const; - MPI_Comm mpi_communicator; + MPI_Comm mpi_communicator; - parallel::distributed::Triangulation triangulation; + parallel::distributed::Triangulation triangulation; - DoFHandler dof_handler; - FE_Q fe; + DoFHandler dof_handler; + FE_Q fe; - IndexSet locally_owned_dofs; - IndexSet locally_relevant_dofs; + IndexSet locally_owned_dofs; + IndexSet locally_relevant_dofs; - ConstraintMatrix constraints; + ConstraintMatrix constraints; - PETScWrappers::MPI::SparseMatrix system_matrix; - PETScWrappers::MPI::Vector locally_relevant_solution; - PETScWrappers::MPI::Vector system_rhs; + PETScWrappers::MPI::SparseMatrix system_matrix; + PETScWrappers::MPI::Vector locally_relevant_solution; + PETScWrappers::MPI::Vector system_rhs; - ConditionalOStream pcout; + ConditionalOStream pcout; }; - // @sect3{The LaplaceProblem class implementation} + // @sect3{The LaplaceProblem class implementation} - // @sect4{Constructors and destructors} + // @sect4{Constructors and destructors} - // Constructors and destructors are rather - // trivial. In addition to what we do in - // step-6, we set the set of processors we - // want to work on to all machines available - // (MPI_COMM_WORLD); ask the triangulation to - // ensure that the mesh remains smooth and - // free to refined islands, for example; and - // initialize the pcout variable - // to only allow processor zero to output - // anything: + // Constructors and destructors are rather + // trivial. In addition to what we do in + // step-6, we set the set of processors we + // want to work on to all machines available + // (MPI_COMM_WORLD); ask the triangulation to + // ensure that the mesh remains smooth and + // free to refined islands, for example; and + // initialize the pcout variable + // to only allow processor zero to output + // anything: template LaplaceProblem::LaplaceProblem () - : - mpi_communicator (MPI_COMM_WORLD), - triangulation (mpi_communicator, - typename Triangulation::MeshSmoothing - (Triangulation::smoothing_on_refinement | - Triangulation::smoothing_on_coarsening)), - dof_handler (triangulation), - fe (2), - pcout (std::cout, - (Utilities::MPI::this_mpi_process(mpi_communicator) - == 0)) + : + mpi_communicator (MPI_COMM_WORLD), + triangulation (mpi_communicator, + typename Triangulation::MeshSmoothing + (Triangulation::smoothing_on_refinement | + Triangulation::smoothing_on_coarsening)), + dof_handler (triangulation), + fe (2), + pcout (std::cout, + (Utilities::MPI::this_mpi_process(mpi_communicator) + == 0)) {} @@ -245,80 +245,80 @@ namespace Step40 } - // @sect4{LaplaceProblem::setup_system} - - // The following function is, arguably, the - // most interesting one in the entire program - // since it goes to the heart of what - // distinguishes %parallel step-40 from - // sequential step-6. - // - // At the top we do what we always do: tell - // the DoFHandler object to distribute - // degrees of freedom. Since the - // triangulation we use here is distributed, - // the DoFHandler object is smart enough to - // recognize that on each processor it can - // only distribute degrees of freedom on - // cells it owns; this is followed by an - // exchange step in which processors tell - // each other about degrees of freedom on - // ghost cell. The result is a DoFHandler - // that knows about the degrees of freedom on - // locally owned cells and ghost cells - // (i.e. cells adjacent to locally owned - // cells) but nothing about cells that are - // further away, consistent with the basic - // philosophy of distributed computing that - // no processor can know everything. + // @sect4{LaplaceProblem::setup_system} + + // The following function is, arguably, the + // most interesting one in the entire program + // since it goes to the heart of what + // distinguishes %parallel step-40 from + // sequential step-6. + // + // At the top we do what we always do: tell + // the DoFHandler object to distribute + // degrees of freedom. Since the + // triangulation we use here is distributed, + // the DoFHandler object is smart enough to + // recognize that on each processor it can + // only distribute degrees of freedom on + // cells it owns; this is followed by an + // exchange step in which processors tell + // each other about degrees of freedom on + // ghost cell. The result is a DoFHandler + // that knows about the degrees of freedom on + // locally owned cells and ghost cells + // (i.e. cells adjacent to locally owned + // cells) but nothing about cells that are + // further away, consistent with the basic + // philosophy of distributed computing that + // no processor can know everything. template void LaplaceProblem::setup_system () { dof_handler.distribute_dofs (fe); - // The next two lines extract some - // informatino we will need later - // on, namely two index sets that - // provide information about which - // degrees of freedom are owned by - // the current processor (this - // information will be used to - // initialize solution and right - // hand side vectors, and the - // system matrix, indicating which - // elements to store on the current - // processor and which to expect to - // be stored somewhere else); and - // an index set that indicates - // which degrees of freedom are - // locally relevant (i.e. live on - // cells that the current processor - // owns or on the layer of ghost - // cells around the locally owned - // cells; we need all of these - // degrees of freedom, for example, - // to estimate the error on the - // local cells). + // The next two lines extract some + // informatino we will need later + // on, namely two index sets that + // provide information about which + // degrees of freedom are owned by + // the current processor (this + // information will be used to + // initialize solution and right + // hand side vectors, and the + // system matrix, indicating which + // elements to store on the current + // processor and which to expect to + // be stored somewhere else); and + // an index set that indicates + // which degrees of freedom are + // locally relevant (i.e. live on + // cells that the current processor + // owns or on the layer of ghost + // cells around the locally owned + // cells; we need all of these + // degrees of freedom, for example, + // to estimate the error on the + // local cells). locally_owned_dofs = dof_handler.locally_owned_dofs (); DoFTools::extract_locally_relevant_dofs (dof_handler, locally_relevant_dofs); - // Next, let us initialize the - // solution and right hand side - // vectors. As mentioned above, the - // solution vector we seek does not - // only store elements we own, but - // also ghost entries; on the other - // hand, the right hand side vector - // only needs to have the entries - // the current processor owns since - // all we will ever do is write - // into it, never read from it on - // locally owned cells (of course - // the linear solvers will read - // from it, but they do not care - // about the geometric location of - // degrees of freedom). + // Next, let us initialize the + // solution and right hand side + // vectors. As mentioned above, the + // solution vector we seek does not + // only store elements we own, but + // also ghost entries; on the other + // hand, the right hand side vector + // only needs to have the entries + // the current processor owns since + // all we will ever do is write + // into it, never read from it on + // locally owned cells (of course + // the linear solvers will read + // from it, but they do not care + // about the geometric location of + // degrees of freedom). locally_relevant_solution.reinit (mpi_communicator, locally_owned_dofs, locally_relevant_dofs); @@ -328,35 +328,35 @@ namespace Step40 dof_handler.n_locally_owned_dofs()); system_rhs = 0; - // The next step is to compute hanging node - // and boundary value constraints, which we - // combine into a single object storing all - // constraints. - // - // As with all other things in %parallel, - // the mantra must be that no processor can - // store all information about the entire - // universe. As a consequence, we need to - // tell the constraints object for which - // degrees of freedom it can store - // constraints and for which it may not - // expect any information to store. In our - // case, as explained in the @ref - // distributed module, the degrees of - // freedom we need to care about on each - // processor are the locally relevant ones, - // so we pass this to the - // ConstraintMatrix::reinit function. As a - // side note, if you forget to pass this - // argument, the ConstraintMatrix class - // will allocate an array with length equal - // to the largest DoF index it has seen so - // far. For processors with high MPI - // process number, this may be very large - // -- maybe on the order of billions. The - // program would then allocate more memory - // than for likely all other operations - // combined for this single array. + // The next step is to compute hanging node + // and boundary value constraints, which we + // combine into a single object storing all + // constraints. + // + // As with all other things in %parallel, + // the mantra must be that no processor can + // store all information about the entire + // universe. As a consequence, we need to + // tell the constraints object for which + // degrees of freedom it can store + // constraints and for which it may not + // expect any information to store. In our + // case, as explained in the @ref + // distributed module, the degrees of + // freedom we need to care about on each + // processor are the locally relevant ones, + // so we pass this to the + // ConstraintMatrix::reinit function. As a + // side note, if you forget to pass this + // argument, the ConstraintMatrix class + // will allocate an array with length equal + // to the largest DoF index it has seen so + // far. For processors with high MPI + // process number, this may be very large + // -- maybe on the order of billions. The + // program would then allocate more memory + // than for likely all other operations + // combined for this single array. constraints.clear (); constraints.reinit (locally_relevant_dofs); DoFTools::make_hanging_node_constraints (dof_handler, constraints); @@ -366,44 +366,44 @@ namespace Step40 constraints); constraints.close (); - // The last part of this function deals - // with initializing the matrix with - // accompanying sparsity pattern. As in - // previous tutorial programs, we use the - // CompressedSimpleSparsityPattern as an - // intermediate with which we then - // initialize the PETSc matrix. To do so we - // have to tell the sparsity pattern its - // size but as above there is no way the - // resulting object will be able to store - // even a single pointer for each global - // degree of freedom; the best we can hope - // for is that it stores information about - // each locally relevant degree of freedom, - // i.e. all those that we may ever touch in - // the process of assembling the matrix - // (the @ref distributed_paper - // "distributed computing paper" has a long - // discussion why one really needs the - // locally relevant, and not the small set - // of locally active degrees of freedom in - // this context). - // - // So we tell the sparsity pattern its size - // and what DoFs to store anything for and - // then ask DoFTools::make_sparsity_pattern - // to fill it (this function ignores all - // cells that are not locally owned, - // mimicking what we will do below in the - // assembly process). After this, we call a - // function that exchanges entries in these - // sparsity pattern between processors so - // that in the end each processor really - // knows about all the entries that will - // exist in that part of the finite element - // matrix that it will own. The final step - // is to initialize the matrix with the - // sparsity pattern. + // The last part of this function deals + // with initializing the matrix with + // accompanying sparsity pattern. As in + // previous tutorial programs, we use the + // CompressedSimpleSparsityPattern as an + // intermediate with which we then + // initialize the PETSc matrix. To do so we + // have to tell the sparsity pattern its + // size but as above there is no way the + // resulting object will be able to store + // even a single pointer for each global + // degree of freedom; the best we can hope + // for is that it stores information about + // each locally relevant degree of freedom, + // i.e. all those that we may ever touch in + // the process of assembling the matrix + // (the @ref distributed_paper + // "distributed computing paper" has a long + // discussion why one really needs the + // locally relevant, and not the small set + // of locally active degrees of freedom in + // this context). + // + // So we tell the sparsity pattern its size + // and what DoFs to store anything for and + // then ask DoFTools::make_sparsity_pattern + // to fill it (this function ignores all + // cells that are not locally owned, + // mimicking what we will do below in the + // assembly process). After this, we call a + // function that exchanges entries in these + // sparsity pattern between processors so + // that in the end each processor really + // knows about all the entries that will + // exist in that part of the finite element + // matrix that it will own. The final step + // is to initialize the matrix with the + // sparsity pattern. CompressedSimpleSparsityPattern csp (dof_handler.n_dofs(), dof_handler.n_dofs(), locally_relevant_dofs); @@ -423,47 +423,47 @@ namespace Step40 - // @sect4{LaplaceProblem::assemble_system} - - // The function that then assembles the - // linear system is comparatively boring, - // being almost exactly what we've seen - // before. The points to watch out for are: - // - Assembly must only loop over locally - // owned cells. There are multiple ways to - // test that; for example, we could - // compare - // a cell's subdomain_id against - // information from the triangulation - // as in cell->subdomain_id() == - // triangulation.locally_owned_subdomain(), - // or skip all cells for which - // the condition cell->is_ghost() - // || cell->is_artificial() is - // true. The simplest way, however, is - // to simply ask the cell whether it is - // owned by the local processor. - // - Copying local contributions into the - // global matrix must include distributing - // constraints and boundary values. In - // other words, we can now (as we did in - // step-6) first copy every local - // contribution into the global matrix and - // only in a later step take care of - // hanging node constraints and boundary - // values. The reason is, as discussed in - // step-17, that PETSc does not provide - // access to arbitrary elements of the - // matrix once they have been assembled - // into it -- in parts because they may - // simple no longer reside on the current - // processor but have instead been shipped - // to a different machine. - // - The way we compute the right hand side - // (given the formula stated in the - // introduction) may not be the most - // elegant but will do for a program whose - // focus lies somewhere entirely different. + // @sect4{LaplaceProblem::assemble_system} + + // The function that then assembles the + // linear system is comparatively boring, + // being almost exactly what we've seen + // before. The points to watch out for are: + // - Assembly must only loop over locally + // owned cells. There are multiple ways to + // test that; for example, we could + // compare + // a cell's subdomain_id against + // information from the triangulation + // as in cell->subdomain_id() == + // triangulation.locally_owned_subdomain(), + // or skip all cells for which + // the condition cell->is_ghost() + // || cell->is_artificial() is + // true. The simplest way, however, is + // to simply ask the cell whether it is + // owned by the local processor. + // - Copying local contributions into the + // global matrix must include distributing + // constraints and boundary values. In + // other words, we can now (as we did in + // step-6) first copy every local + // contribution into the global matrix and + // only in a later step take care of + // hanging node constraints and boundary + // values. The reason is, as discussed in + // step-17, that PETSc does not provide + // access to arbitrary elements of the + // matrix once they have been assembled + // into it -- in parts because they may + // simple no longer reside on the current + // processor but have instead been shipped + // to a different machine. + // - The way we compute the right hand side + // (given the formula stated in the + // introduction) may not be the most + // elegant but will do for a program whose + // focus lies somewhere entirely different. template void LaplaceProblem::assemble_system () { @@ -483,8 +483,8 @@ namespace Step40 std::vector local_dof_indices (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) if (cell->is_locally_owned()) { @@ -496,7 +496,7 @@ namespace Step40 for (unsigned int q_point=0; q_point 0.5+0.25*std::sin(4.0 * numbers::PI * @@ -530,82 +530,82 @@ namespace Step40 - // @sect4{LaplaceProblem::solve} - - // Even though solving linear systems - // on potentially tens of thousands - // of processors is by far not a - // trivial job, the function that - // does this is -- at least at the - // outside -- relatively simple. Most - // of the parts you've seen - // before. There are really only two - // things worth mentioning: - // - Solvers and preconditioners are - // built on the deal.II wrappers of - // PETSc functionality. It is - // relatively well known that the - // primary bottleneck of massively - // %parallel linear solvers is not - // actually the communication - // between processors, but the fact - // that it is difficult to produce - // preconditioners that scale well - // to large numbers of - // processors. Over the second half - // of the first decade of the 21st - // century, it has become clear - // that algebraic multigrid (AMG) - // methods turn out to be extremely - // efficient in this context, and - // we will use one of them -- the - // BoomerAMG implementation of the - // Hypre package that can be - // interfaced to through PETSc -- - // for the current program. The - // rest of the solver itself is - // boilerplate and has been shown - // before. Since the linear system - // is symmetric and positive - // definite, we can use the CG - // method as the outer solver. - // - Ultimately, we want a vector - // that stores not only the - // elements of the solution for - // degrees of freedom the current - // processor owns, but also all - // other locally relevant degrees - // of freedom. On the other hand, - // the solver itself needs a vector - // that is uniquely split between - // processors, without any - // overlap. We therefore create a - // vector at the beginning of this - // function that has these - // properties, use it to solve the - // linear system, and only assign - // it to the vector we want at the - // very end. This last step ensures - // that all ghost elements are also - // copied as necessary. + // @sect4{LaplaceProblem::solve} + + // Even though solving linear systems + // on potentially tens of thousands + // of processors is by far not a + // trivial job, the function that + // does this is -- at least at the + // outside -- relatively simple. Most + // of the parts you've seen + // before. There are really only two + // things worth mentioning: + // - Solvers and preconditioners are + // built on the deal.II wrappers of + // PETSc functionality. It is + // relatively well known that the + // primary bottleneck of massively + // %parallel linear solvers is not + // actually the communication + // between processors, but the fact + // that it is difficult to produce + // preconditioners that scale well + // to large numbers of + // processors. Over the second half + // of the first decade of the 21st + // century, it has become clear + // that algebraic multigrid (AMG) + // methods turn out to be extremely + // efficient in this context, and + // we will use one of them -- the + // BoomerAMG implementation of the + // Hypre package that can be + // interfaced to through PETSc -- + // for the current program. The + // rest of the solver itself is + // boilerplate and has been shown + // before. Since the linear system + // is symmetric and positive + // definite, we can use the CG + // method as the outer solver. + // - Ultimately, we want a vector + // that stores not only the + // elements of the solution for + // degrees of freedom the current + // processor owns, but also all + // other locally relevant degrees + // of freedom. On the other hand, + // the solver itself needs a vector + // that is uniquely split between + // processors, without any + // overlap. We therefore create a + // vector at the beginning of this + // function that has these + // properties, use it to solve the + // linear system, and only assign + // it to the vector we want at the + // very end. This last step ensures + // that all ghost elements are also + // copied as necessary. template void LaplaceProblem::solve () { PETScWrappers::MPI::Vector - completely_distributed_solution (mpi_communicator, - dof_handler.n_dofs(), - dof_handler.n_locally_owned_dofs()); + completely_distributed_solution (mpi_communicator, + dof_handler.n_dofs(), + dof_handler.n_locally_owned_dofs()); SolverControl solver_control (dof_handler.n_dofs(), 1e-12); PETScWrappers::SolverCG solver(solver_control, mpi_communicator); - // Ask for a symmetric preconditioner by - // setting the first parameter in - // AdditionalData to true. + // Ask for a symmetric preconditioner by + // setting the first parameter in + // AdditionalData to true. PETScWrappers::PreconditionBoomerAMG - preconditioner(system_matrix, - PETScWrappers::PreconditionBoomerAMG::AdditionalData(true)); + preconditioner(system_matrix, + PETScWrappers::PreconditionBoomerAMG::AdditionalData(true)); solver.solve (system_matrix, completely_distributed_solution, system_rhs, preconditioner); @@ -621,33 +621,33 @@ namespace Step40 - // @sect4{LaplaceProblem::refine_grid} - - // The function that estimates the - // error and refines the grid is - // again almost exactly like the one - // in step-6. The only difference is - // that the function that flags cells - // to be refined is now in namespace - // parallel::distributed::GridRefinement - // -- a namespace that has functions - // that can communicate between all - // involved processors and determine - // global thresholds to use in - // deciding which cells to refine and - // which to coarsen. - // - // Note that we didn't have to do - // anything special about the - // KellyErrorEstimator class: we just - // give it a vector with as many - // elements as the local - // triangulation has cells (locally - // owned cells, ghost cells, and - // artificial ones), but it only - // fills those entries that - // correspond to cells that are - // locally owned. + // @sect4{LaplaceProblem::refine_grid} + + // The function that estimates the + // error and refines the grid is + // again almost exactly like the one + // in step-6. The only difference is + // that the function that flags cells + // to be refined is now in namespace + // parallel::distributed::GridRefinement + // -- a namespace that has functions + // that can communicate between all + // involved processors and determine + // global thresholds to use in + // deciding which cells to refine and + // which to coarsen. + // + // Note that we didn't have to do + // anything special about the + // KellyErrorEstimator class: we just + // give it a vector with as many + // elements as the local + // triangulation has cells (locally + // owned cells, ghost cells, and + // artificial ones), but it only + // fills those entries that + // correspond to cells that are + // locally owned. template void LaplaceProblem::refine_grid () { @@ -658,81 +658,81 @@ namespace Step40 locally_relevant_solution, estimated_error_per_cell); parallel::distributed::GridRefinement:: - refine_and_coarsen_fixed_number (triangulation, - estimated_error_per_cell, - 0.3, 0.03); + refine_and_coarsen_fixed_number (triangulation, + estimated_error_per_cell, + 0.3, 0.03); triangulation.execute_coarsening_and_refinement (); } - // @sect4{LaplaceProblem::output_results} - - // Compared to the corresponding - // function in step-6, the one here - // is a tad more complicated. There - // are two reasons: the first one is - // that we do not just want to output - // the solution but also for each - // cell which processor owns it - // (i.e. which "subdomain" it is - // in). Secondly, as discussed at - // length in step-17 and step-18, - // generating graphical data can be a - // bottleneck in parallelizing. In - // step-18, we have moved this step - // out of the actual computation but - // shifted it into a separate program - // that later combined the output - // from various processors into a - // single file. But this doesn't - // scale: if the number of processors - // is large, this may mean that the - // step of combining data on a single - // processor later becomes the - // longest running part of the - // program, or it may produce a file - // that's so large that it can't be - // visualized any more. We here - // follow a more sensible approach, - // namely creating individual files - // for each MPI process and leaving - // it to the visualization program to - // make sense of that. - // - // To start, the top of the function - // looks like always. In addition to - // attaching the solution vector (the - // one that has entries for all - // locally relevant, not only the - // locally owned, elements), we - // attach a data vector that stores, - // for each cell, the subdomain the - // cell belongs to. This is slightly - // tricky, because of course not - // every processor knows about every - // cell. The vector we attach - // therefore has an entry for every - // cell that the current processor - // has in its mesh (locally owned - // onces, ghost cells, and artificial - // cells), but the DataOut class will - // ignore all entries that correspond - // to cells that are not owned by the - // current processor. As a - // consequence, it doesn't actually - // matter what values we write into - // these vector entries: we simply - // fill the entire vector with the - // number of the current MPI process - // (i.e. the subdomain_id of the - // current process); this correctly - // sets the values we care for, - // i.e. the entries that correspond - // to locally owned cells, while - // providing the wrong value for all - // other elements -- but these are - // then ignored anyway. + // @sect4{LaplaceProblem::output_results} + + // Compared to the corresponding + // function in step-6, the one here + // is a tad more complicated. There + // are two reasons: the first one is + // that we do not just want to output + // the solution but also for each + // cell which processor owns it + // (i.e. which "subdomain" it is + // in). Secondly, as discussed at + // length in step-17 and step-18, + // generating graphical data can be a + // bottleneck in parallelizing. In + // step-18, we have moved this step + // out of the actual computation but + // shifted it into a separate program + // that later combined the output + // from various processors into a + // single file. But this doesn't + // scale: if the number of processors + // is large, this may mean that the + // step of combining data on a single + // processor later becomes the + // longest running part of the + // program, or it may produce a file + // that's so large that it can't be + // visualized any more. We here + // follow a more sensible approach, + // namely creating individual files + // for each MPI process and leaving + // it to the visualization program to + // make sense of that. + // + // To start, the top of the function + // looks like always. In addition to + // attaching the solution vector (the + // one that has entries for all + // locally relevant, not only the + // locally owned, elements), we + // attach a data vector that stores, + // for each cell, the subdomain the + // cell belongs to. This is slightly + // tricky, because of course not + // every processor knows about every + // cell. The vector we attach + // therefore has an entry for every + // cell that the current processor + // has in its mesh (locally owned + // onces, ghost cells, and artificial + // cells), but the DataOut class will + // ignore all entries that correspond + // to cells that are not owned by the + // current processor. As a + // consequence, it doesn't actually + // matter what values we write into + // these vector entries: we simply + // fill the entire vector with the + // number of the current MPI process + // (i.e. the subdomain_id of the + // current process); this correctly + // sets the values we care for, + // i.e. the entries that correspond + // to locally owned cells, while + // providing the wrong value for all + // other elements -- but these are + // then ignored anyway. template void LaplaceProblem::output_results (const unsigned int cycle) const { @@ -747,22 +747,22 @@ namespace Step40 data_out.build_patches (); - // The next step is to write this - // data to disk. We choose file - // names of the form - // solution-XX-PPPP.vtu - // where XX indicates - // the refinement cycle, - // PPPP refers to the - // processor number (enough for up - // to 10,000 processors, though we - // hope that nobody ever tries to - // generate this much data -- you - // would likely overflow all file - // system quotas), and - // .vtu indicates the - // XML-based Visualization Toolkit - // (VTK) file format. + // The next step is to write this + // data to disk. We choose file + // names of the form + // solution-XX-PPPP.vtu + // where XX indicates + // the refinement cycle, + // PPPP refers to the + // processor number (enough for up + // to 10,000 processors, though we + // hope that nobody ever tries to + // generate this much data -- you + // would likely overflow all file + // system quotas), and + // .vtu indicates the + // XML-based Visualization Toolkit + // (VTK) file format. const std::string filename = ("solution-" + Utilities::int_to_string (cycle, 2) + "." + @@ -771,21 +771,21 @@ namespace Step40 std::ofstream output ((filename + ".vtu").c_str()); data_out.write_vtu (output); - // The last step is to write a - // "master record" that lists for - // the visualization program the - // names of the various files that - // combined represents the - // graphical data for the entire - // domain. The - // DataOutBase::write_pvtu_record - // does this, and it needs a list - // of filenames that we create - // first. Note that only one - // processor needs to generate this - // file; we arbitrarily choose - // processor zero to take over this - // job. + // The last step is to write a + // "master record" that lists for + // the visualization program the + // names of the various files that + // combined represents the + // graphical data for the entire + // domain. The + // DataOutBase::write_pvtu_record + // does this, and it needs a list + // of filenames that we create + // first. Note that only one + // processor needs to generate this + // file; we arbitrarily choose + // processor zero to take over this + // job. if (Utilities::MPI::this_mpi_process(mpi_communicator) == 0) { std::vector filenames; @@ -805,35 +805,35 @@ namespace Step40 - // @sect4{LaplaceProblem::run} - - // The function that controls the - // overall behavior of the program is - // again like the one in step-6. The - // minor difference are the use of - // pcout instead of - // std::cout for output - // to the console (see also step-17) - // and that we only generate - // graphical output if at most 32 - // processors are involved. Without - // this limit, it would be just too - // easy for people carelessly running - // this program without reading it - // first to bring down the cluster - // interconnect and fill any file - // system available :-) - // - // A functional difference to step-6 - // is the use of a square domain and - // that we start with a slightly - // finer mesh (5 global refinement - // cycles) -- there just isn't much - // of a point showing a massively - // %parallel program starting on 4 - // cells (although admittedly the - // point is only slightly stronger - // starting on 1024). + // @sect4{LaplaceProblem::run} + + // The function that controls the + // overall behavior of the program is + // again like the one in step-6. The + // minor difference are the use of + // pcout instead of + // std::cout for output + // to the console (see also step-17) + // and that we only generate + // graphical output if at most 32 + // processors are involved. Without + // this limit, it would be just too + // easy for people carelessly running + // this program without reading it + // first to bring down the cluster + // interconnect and fill any file + // system available :-) + // + // A functional difference to step-6 + // is the use of a square domain and + // that we start with a slightly + // finer mesh (5 global refinement + // cycles) -- there just isn't much + // of a point showing a massively + // %parallel program starting on 4 + // cells (although admittedly the + // point is only slightly stronger + // starting on 1024). template void LaplaceProblem::run () { @@ -872,32 +872,32 @@ namespace Step40 - // @sect4{main()} - - // The final function, - // main(), again has the - // same structure as in all other - // programs, in particular - // step-6. Like in the other programs - // that use PETSc, we have to - // inialize and finalize PETSc, which - // is done using the helper object - // MPI_InitFinalize. - // - // Note how we enclose the use the - // use of the LaplaceProblem class in - // a pair of braces. This makes sure - // that all member variables of the - // object are destroyed by the time - // we destroy the mpi_intialization - // object. Not doing this will lead to - // strange and hard to debug errors - // when PetscFinalize - // first deletes all PETSc vectors - // that are still around, and the - // destructor of the LaplaceProblem - // class then tries to delete them - // again. +// @sect4{main()} + +// The final function, +// main(), again has the +// same structure as in all other +// programs, in particular +// step-6. Like in the other programs +// that use PETSc, we have to +// inialize and finalize PETSc, which +// is done using the helper object +// MPI_InitFinalize. +// +// Note how we enclose the use the +// use of the LaplaceProblem class in +// a pair of braces. This makes sure +// that all member variables of the +// object are destroyed by the time +// we destroy the mpi_intialization +// object. Not doing this will lead to +// strange and hard to debug errors +// when PetscFinalize +// first deletes all PETSc vectors +// that are still around, and the +// destructor of the LaplaceProblem +// class then tries to delete them +// again. int main(int argc, char *argv[]) { try diff --git a/deal.II/examples/step-41/step-41.cc b/deal.II/examples/step-41/step-41.cc index a3d015cb24..00b8ebf75d 100644 --- a/deal.II/examples/step-41/step-41.cc +++ b/deal.II/examples/step-41/step-41.cc @@ -11,14 +11,14 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} - - // As usual, at the beginning we - // include all the header files we - // need in here. With the exception - // of the various files that provide - // interfaces to the Trilinos - // library, there are no surprises: +// @sect3{Include files} + +// As usual, at the beginning we +// include all the header files we +// need in here. With the exception +// of the various files that provide +// interfaces to the Trilinos +// library, there are no surprises: #include #include #include @@ -55,93 +55,93 @@ namespace Step41 { using namespace dealii; - // @sect3{The ObstacleProblem class template} - - // This class supplies all function - // and variables needed to describe - // the obstacle problem. It is - // close to what we had to do in - // step-4, and so relatively - // simple. The only real new - // components are the - // update_solution_and_constraints - // function that computes the - // active set and a number of - // variables that are necessary to - // describe the original - // (unconstrained) form of the - // linear system - // (complete_system_matrix - // and - // complete_system_rhs) - // as well as the active set itself - // and the diagonal of the mass - // matrix $B$ used in scaling - // Lagrange multipliers in the - // active set formulation. The rest - // is as in step-4: + // @sect3{The ObstacleProblem class template} + + // This class supplies all function + // and variables needed to describe + // the obstacle problem. It is + // close to what we had to do in + // step-4, and so relatively + // simple. The only real new + // components are the + // update_solution_and_constraints + // function that computes the + // active set and a number of + // variables that are necessary to + // describe the original + // (unconstrained) form of the + // linear system + // (complete_system_matrix + // and + // complete_system_rhs) + // as well as the active set itself + // and the diagonal of the mass + // matrix $B$ used in scaling + // Lagrange multipliers in the + // active set formulation. The rest + // is as in step-4: template class ObstacleProblem { - public: - ObstacleProblem (); - void run (); - - private: - void make_grid (); - void setup_system(); - void assemble_system (); - void assemble_mass_matrix_diagonal (TrilinosWrappers::SparseMatrix &mass_matrix); - void update_solution_and_constraints (); - void solve (); - void output_results (const unsigned int iteration) const; - - Triangulation triangulation; - FE_Q fe; - DoFHandler dof_handler; - ConstraintMatrix constraints; - IndexSet active_set; - - TrilinosWrappers::SparseMatrix system_matrix; - TrilinosWrappers::SparseMatrix complete_system_matrix; - - TrilinosWrappers::Vector solution; - TrilinosWrappers::Vector system_rhs; - TrilinosWrappers::Vector complete_system_rhs; - TrilinosWrappers::Vector diagonal_of_mass_matrix; - TrilinosWrappers::Vector contact_force; + public: + ObstacleProblem (); + void run (); + + private: + void make_grid (); + void setup_system(); + void assemble_system (); + void assemble_mass_matrix_diagonal (TrilinosWrappers::SparseMatrix &mass_matrix); + void update_solution_and_constraints (); + void solve (); + void output_results (const unsigned int iteration) const; + + Triangulation triangulation; + FE_Q fe; + DoFHandler dof_handler; + ConstraintMatrix constraints; + IndexSet active_set; + + TrilinosWrappers::SparseMatrix system_matrix; + TrilinosWrappers::SparseMatrix complete_system_matrix; + + TrilinosWrappers::Vector solution; + TrilinosWrappers::Vector system_rhs; + TrilinosWrappers::Vector complete_system_rhs; + TrilinosWrappers::Vector diagonal_of_mass_matrix; + TrilinosWrappers::Vector contact_force; }; - // @sect3{Right hand side, boundary values, and the obstacle} - - // In the following, we define - // classes that describe the right - // hand side function, the - // Dirichlet boundary values, and - // the height of the obstacle as a - // function of $\mathbf x$. In all - // three cases, we derive these - // classes from Function@, - // although in the case of - // RightHandSide and - // Obstacle this is - // more out of convention than - // necessity since we never pass - // such objects to the library. In - // any case, the definition of the - // right hand side and boundary - // values classes is obvious given - // our choice of $f=-10$, - // $u|_{\partial\Omega}=0$: + // @sect3{Right hand side, boundary values, and the obstacle} + + // In the following, we define + // classes that describe the right + // hand side function, the + // Dirichlet boundary values, and + // the height of the obstacle as a + // function of $\mathbf x$. In all + // three cases, we derive these + // classes from Function@, + // although in the case of + // RightHandSide and + // Obstacle this is + // more out of convention than + // necessity since we never pass + // such objects to the library. In + // any case, the definition of the + // right hand side and boundary + // values classes is obvious given + // our choice of $f=-10$, + // $u|_{\partial\Omega}=0$: template class RightHandSide : public Function { - public: - RightHandSide () : Function() {} + public: + RightHandSide () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; template @@ -158,11 +158,11 @@ namespace Step41 template class BoundaryValues : public Function { - public: - BoundaryValues () : Function() {} + public: + BoundaryValues () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; template @@ -176,16 +176,16 @@ namespace Step41 - // We describe the obstacle function by a cascaded - // barrier (think: stair steps): + // We describe the obstacle function by a cascaded + // barrier (think: stair steps): template class Obstacle : public Function { - public: - Obstacle () : Function() {} + public: + Obstacle () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; template @@ -206,30 +206,30 @@ namespace Step41 - // @sect3{Implementation of the ObstacleProblem class} + // @sect3{Implementation of the ObstacleProblem class} - // @sect4{ObstacleProblem::ObstacleProblem} + // @sect4{ObstacleProblem::ObstacleProblem} - // To everyone who has taken a look - // at the first few tutorial - // programs, the constructor is - // completely obvious: + // To everyone who has taken a look + // at the first few tutorial + // programs, the constructor is + // completely obvious: template ObstacleProblem::ObstacleProblem () - : - fe (1), - dof_handler (triangulation) + : + fe (1), + dof_handler (triangulation) {} - // @sect4{ObstacleProblem::make_grid} + // @sect4{ObstacleProblem::make_grid} - // We solve our obstacle problem on - // the square $[-1,1]\times [-1,1]$ - // in 2D. This function therefore - // just sets up one of the simplest - // possible meshes. + // We solve our obstacle problem on + // the square $[-1,1]\times [-1,1]$ + // in 2D. This function therefore + // just sets up one of the simplest + // possible meshes. template void ObstacleProblem::make_grid () { @@ -245,17 +245,17 @@ namespace Step41 } - // @sect4{ObstacleProblem::setup_system} + // @sect4{ObstacleProblem::setup_system} - // In this first function of note, - // we set up the degrees of freedom - // handler, resize vectors and - // matrices, and deal with the - // constraints. Initially, the - // constraints are, of course, only - // given by boundary values, so we - // interpolate them towards the top - // of the function. + // In this first function of note, + // we set up the degrees of freedom + // handler, resize vectors and + // matrices, and deal with the + // constraints. Initially, the + // constraints are, of course, only + // given by boundary values, so we + // interpolate them towards the top + // of the function. template void ObstacleProblem::setup_system () { @@ -287,18 +287,18 @@ namespace Step41 complete_system_rhs.reinit (dof_handler.n_dofs()); contact_force.reinit (dof_handler.n_dofs()); - // The only other thing to do - // here is to compute the factors - // in the $B$ matrix which is - // used to scale the residual. As - // discussed in the introduction, - // we'll use a little trick to - // make this mass matrix - // diagonal, and in the following - // then first compute all of this - // as a matrix and then extract - // the diagonal elements for - // later use: + // The only other thing to do + // here is to compute the factors + // in the $B$ matrix which is + // used to scale the residual. As + // discussed in the introduction, + // we'll use a little trick to + // make this mass matrix + // diagonal, and in the following + // then first compute all of this + // as a matrix and then extract + // the diagonal elements for + // later use: TrilinosWrappers::SparseMatrix mass_matrix; mass_matrix.reinit (c_sparsity); assemble_mass_matrix_diagonal (mass_matrix); @@ -308,18 +308,18 @@ namespace Step41 } - // @sect4{ObstacleProblem::assemble_system} + // @sect4{ObstacleProblem::assemble_system} - // This function at once assembles - // the system matrix and - // right-hand-side and applied the - // constraints (both due to the - // active set as well as from - // boundary values) to our - // system. Otherwise, it is - // functionally equivalent to the - // corresponding function in, for - // example, step-4. + // This function at once assembles + // the system matrix and + // right-hand-side and applied the + // constraints (both due to the + // active set as well as from + // boundary values) to our + // system. Otherwise, it is + // functionally equivalent to the + // corresponding function in, for + // example, step-4. template void ObstacleProblem::assemble_system () { @@ -345,8 +345,8 @@ namespace Step41 std::vector local_dof_indices (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { @@ -380,53 +380,53 @@ namespace Step41 - // @sect4{ObstacleProblem::assemble_mass_matrix_diagonal} - - // The next function is used in the - // computation of the diagonal mass - // matrix $B$ used to scale - // variables in the active set - // method. As discussed in the - // introduction, we get the mass - // matrix to be diagonal by - // choosing the trapezoidal rule - // for quadrature. Doing so we - // don't really need the triple - // loop over quadrature points, - // indices $i$ and indices $j$ any - // more and can, instead, just use - // a double loop. The rest of the - // function is obvious given what - // we have discussed in many of the - // previous tutorial programs. - // - // Note that at the time this - // function is called, the - // constraints object only contains - // boundary value constraints; we - // therefore do not have to pay - // attention in the last - // copy-local-to-global step to - // preserve the values of matrix - // entries that may later on be - // constrained by the active set. - // - // Note also that the trick with - // the trapezoidal rule only works - // if we have in fact $Q_1$ - // elements. For higher order - // elements, one would need to use - // a quadrature formula that has - // quadrature points at all the - // support points of the finite - // element. Constructing such a - // quadrature formula isn't really - // difficult, but not the point - // here, and so we simply assert at - // the top of the function that our - // implicit assumption about the - // finite element is in fact - // satisfied. + // @sect4{ObstacleProblem::assemble_mass_matrix_diagonal} + + // The next function is used in the + // computation of the diagonal mass + // matrix $B$ used to scale + // variables in the active set + // method. As discussed in the + // introduction, we get the mass + // matrix to be diagonal by + // choosing the trapezoidal rule + // for quadrature. Doing so we + // don't really need the triple + // loop over quadrature points, + // indices $i$ and indices $j$ any + // more and can, instead, just use + // a double loop. The rest of the + // function is obvious given what + // we have discussed in many of the + // previous tutorial programs. + // + // Note that at the time this + // function is called, the + // constraints object only contains + // boundary value constraints; we + // therefore do not have to pay + // attention in the last + // copy-local-to-global step to + // preserve the values of matrix + // entries that may later on be + // constrained by the active set. + // + // Note also that the trick with + // the trapezoidal rule only works + // if we have in fact $Q_1$ + // elements. For higher order + // elements, one would need to use + // a quadrature formula that has + // quadrature points at all the + // support points of the finite + // element. Constructing such a + // quadrature formula isn't really + // difficult, but not the point + // here, and so we simply assert at + // the top of the function that our + // implicit assumption about the + // finite element is in fact + // satisfied. template void ObstacleProblem:: @@ -447,8 +447,8 @@ namespace Step41 std::vector local_dof_indices (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { @@ -470,45 +470,45 @@ namespace Step41 } - // @sect4{ObstacleProblem::update_solution_and_constraints} - - // In a sense, this is the central - // function of this program. It - // updates the active set of - // constrained degrees of freedom - // as discussed in the introduction - // and computes a ConstraintMatrix - // object from it that can then be - // used to eliminate constrained - // degrees of freedom from the - // solution of the next - // iteration. At the same time we - // set the constrained degrees of - // freedom of the solution to the - // correct value, namely the height - // of the obstacle. - // - // Fundamentally, the function is - // rather simple: We have to loop - // over all degrees of freedom and - // check the sign of the function - // $\Lambda^k_i + c([BU^k]_i - - // G_i) = \Lambda^k_i + cB_i(U^k_i - - // [g_h]_i)$ because in our case - // $G_i = B_i[g_h]_i$. To this end, - // we use the formula given in the - // introduction by which we can - // compute the Lagrange multiplier - // as the residual of the original - // linear system (given via the - // variables - // complete_system_matrix - // and - // complete_system_rhs. - // At the top of this function, we - // compute this residual using a - // function that is part of the - // matrix classes. + // @sect4{ObstacleProblem::update_solution_and_constraints} + + // In a sense, this is the central + // function of this program. It + // updates the active set of + // constrained degrees of freedom + // as discussed in the introduction + // and computes a ConstraintMatrix + // object from it that can then be + // used to eliminate constrained + // degrees of freedom from the + // solution of the next + // iteration. At the same time we + // set the constrained degrees of + // freedom of the solution to the + // correct value, namely the height + // of the obstacle. + // + // Fundamentally, the function is + // rather simple: We have to loop + // over all degrees of freedom and + // check the sign of the function + // $\Lambda^k_i + c([BU^k]_i - + // G_i) = \Lambda^k_i + cB_i(U^k_i - + // [g_h]_i)$ because in our case + // $G_i = B_i[g_h]_i$. To this end, + // we use the formula given in the + // introduction by which we can + // compute the Lagrange multiplier + // as the residual of the original + // linear system (given via the + // variables + // complete_system_matrix + // and + // complete_system_rhs. + // At the top of this function, we + // compute this residual using a + // function that is part of the + // matrix classes. template void ObstacleProblem::update_solution_and_constraints () @@ -523,63 +523,63 @@ namespace Step41 contact_force.ratio (lambda, diagonal_of_mass_matrix); contact_force *= -1; - // The next step is to reset the - // active set and constraints - // objects and to start the loop - // over all degrees of - // freedom. This is made slightly - // more complicated by the fact - // that we can't just loop over - // all elements of the solution - // vector since there is no way - // for us then to find out what - // location a DoF is associated - // with; however, we need this - // location to test whether the - // displacement of a DoF is - // larger or smaller than the - // height of the obstacle at this - // location. - // - // We work around this by looping - // over all cells and DoFs - // defined on each of these - // cells. We use here that the - // displacement is described - // using a $Q_1$ function for - // which degrees of freedom are - // always located on the vertices - // of the cell; thus, we can get - // the index of each degree of - // freedom and its location by - // asking the vertex for this - // information. On the other - // hand, this clearly wouldn't - // work for higher order - // elements, and so we add an - // assertion that makes sure that - // we only deal with elements for - // which all degrees of freedom - // are located in vertices to - // avoid tripping ourselves with - // non-functional code in case - // someone wants to play with - // increasing the polynomial - // degree of the solution. - // - // The price to pay for having to - // loop over cells rather than - // DoFs is that we may encounter - // some degrees of freedom more - // than once, namely each time we - // visit one of the cells - // adjacent to a given vertex. We - // will therefore have to keep - // track which vertices we have - // already touched and which we - // haven't so far. We do so by - // using an array of flags - // dof_touched: + // The next step is to reset the + // active set and constraints + // objects and to start the loop + // over all degrees of + // freedom. This is made slightly + // more complicated by the fact + // that we can't just loop over + // all elements of the solution + // vector since there is no way + // for us then to find out what + // location a DoF is associated + // with; however, we need this + // location to test whether the + // displacement of a DoF is + // larger or smaller than the + // height of the obstacle at this + // location. + // + // We work around this by looping + // over all cells and DoFs + // defined on each of these + // cells. We use here that the + // displacement is described + // using a $Q_1$ function for + // which degrees of freedom are + // always located on the vertices + // of the cell; thus, we can get + // the index of each degree of + // freedom and its location by + // asking the vertex for this + // information. On the other + // hand, this clearly wouldn't + // work for higher order + // elements, and so we add an + // assertion that makes sure that + // we only deal with elements for + // which all degrees of freedom + // are located in vertices to + // avoid tripping ourselves with + // non-functional code in case + // someone wants to play with + // increasing the polynomial + // degree of the solution. + // + // The price to pay for having to + // loop over cells rather than + // DoFs is that we may encounter + // some degrees of freedom more + // than once, namely each time we + // visit one of the cells + // adjacent to a given vertex. We + // will therefore have to keep + // track which vertices we have + // already touched and which we + // haven't so far. We do so by + // using an array of flags + // dof_touched: constraints.clear(); active_set.clear (); @@ -587,8 +587,8 @@ namespace Step41 std::vector dof_touched (dof_handler.n_dofs(), false); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) for (unsigned int v=0; v::vertices_per_cell; ++v) { @@ -603,56 +603,56 @@ namespace Step41 else continue; - // Now that we know that we - // haven't touched this DoF - // yet, let's get the value - // of the displacement - // function there as well - // as the value of the - // obstacle function and - // use this to decide - // whether the current DoF - // belongs to the active - // set. For that we use the - // function given above and - // in the introduction. - // - // If we decide that the - // DoF should be part of - // the active set, we add - // its index to the active - // set, introduce a - // nonhomogeneous equality - // constraint in the - // ConstraintMatrix object, - // and reset the solution - // value to the height of - // the obstacle. Finally, - // the residual of the - // non-contact part of the - // system serves as an - // additional control (the - // residual equals the - // remaining, unaccounted - // forces, and should be - // zero outside the contact - // zone), so we zero out - // the components of the - // residual vector (i.e., - // the Lagrange multiplier - // lambda) that correspond - // to the area where the - // body is in contact; at - // the end of the loop over - // all cells, the residual - // will therefore only - // consist of the residual - // in the non-contact - // zone. We output the norm - // of this residual along - // with the size of the - // active set after the - // loop. + // Now that we know that we + // haven't touched this DoF + // yet, let's get the value + // of the displacement + // function there as well + // as the value of the + // obstacle function and + // use this to decide + // whether the current DoF + // belongs to the active + // set. For that we use the + // function given above and + // in the introduction. + // + // If we decide that the + // DoF should be part of + // the active set, we add + // its index to the active + // set, introduce a + // nonhomogeneous equality + // constraint in the + // ConstraintMatrix object, + // and reset the solution + // value to the height of + // the obstacle. Finally, + // the residual of the + // non-contact part of the + // system serves as an + // additional control (the + // residual equals the + // remaining, unaccounted + // forces, and should be + // zero outside the contact + // zone), so we zero out + // the components of the + // residual vector (i.e., + // the Lagrange multiplier + // lambda) that correspond + // to the area where the + // body is in contact; at + // the end of the loop over + // all cells, the residual + // will therefore only + // consist of the residual + // in the non-contact + // zone. We output the norm + // of this residual along + // with the size of the + // active set after the + // loop. const double obstacle_value = obstacle.value (cell->vertex(v)); const double solution_value = solution (dof_index); @@ -679,12 +679,12 @@ namespace Step41 << lambda.l2_norm() << std::endl; - // In a final step, we add to the - // set of constraints on DoFs we - // have so far from the active - // set those that result from - // Dirichlet boundary values, and - // close the constraints object: + // In a final step, we add to the + // set of constraints on DoFs we + // have so far from the active + // set those that result from + // Dirichlet boundary values, and + // close the constraints object: VectorTools::interpolate_boundary_values (dof_handler, 0, BoundaryValues(), @@ -692,25 +692,25 @@ namespace Step41 constraints.close (); } - // @sect4{ObstacleProblem::solve} - - // There is nothing to say really - // about the solve function. In the - // context of a Newton method, we - // are not typically interested in - // very high accuracy (why ask for - // a highly accurate solution of a - // linear problem that we know only - // gives us an approximation of the - // solution of the nonlinear - // problem), and so we use the - // ReductionControl class that - // stops iterations when either an - // absolute tolerance is reached - // (for which we choose $10^{-12}$) - // or when the residual is reduced - // by a certain factor (here, - // $10^{-3}$). + // @sect4{ObstacleProblem::solve} + + // There is nothing to say really + // about the solve function. In the + // context of a Newton method, we + // are not typically interested in + // very high accuracy (why ask for + // a highly accurate solution of a + // linear problem that we know only + // gives us an approximation of the + // solution of the nonlinear + // problem), and so we use the + // ReductionControl class that + // stops iterations when either an + // absolute tolerance is reached + // (for which we choose $10^{-12}$) + // or when the residual is reduced + // by a certain factor (here, + // $10^{-3}$). template void ObstacleProblem::solve () { @@ -733,22 +733,22 @@ namespace Step41 } - // @sect4{ObstacleProblem::output_results} - - // We use the vtk-format for the - // output. The file contains the - // displacement and a numerical - // represenation of the active - // set. The function looks standard - // but note that we can add an - // IndexSet object to the DataOut - // object in exactly the same way - // as a regular solution vector: it - // is simply interpreted as a - // function that is either zero - // (when a degree of freedom is not - // part of the IndexSet) or one (if - // it is). + // @sect4{ObstacleProblem::output_results} + + // We use the vtk-format for the + // output. The file contains the + // displacement and a numerical + // represenation of the active + // set. The function looks standard + // but note that we can add an + // IndexSet object to the DataOut + // object in exactly the same way + // as a regular solution vector: it + // is simply interpreted as a + // function that is either zero + // (when a degree of freedom is not + // part of the IndexSet) or one (if + // it is). template void ObstacleProblem::output_results (const unsigned int iteration) const { @@ -771,44 +771,44 @@ namespace Step41 - // @sect4{ObstacleProblem::run} - - // This is the function which has - // the top-level control over - // everything. It is not very - // long, and in fact rather - // straightforward: in every - // iteration of the active set - // method, we assemble the linear - // system, solve it, update the - // active set and project the - // solution back to the feasible - // set, and then output the - // results. The iteration is - // terminated whenever the active - // set has not changed in the - // previous iteration. - // - // The only trickier part is that - // we have to save the linear - // system (i.e., the matrix and - // right hand side) after - // assembling it in the first - // iteration. The reason is that - // this is the only step where we - // can access the linear system as - // built without any of the contact - // constraints active. We need this - // to compute the residual of the - // solution at other iterations, - // but in other iterations that - // linear system we form has the - // rows and columns that correspond - // to constrained degrees of - // freedom eliminated, and so we - // can no longer access the full - // residual of the original - // equation. + // @sect4{ObstacleProblem::run} + + // This is the function which has + // the top-level control over + // everything. It is not very + // long, and in fact rather + // straightforward: in every + // iteration of the active set + // method, we assemble the linear + // system, solve it, update the + // active set and project the + // solution back to the feasible + // set, and then output the + // results. The iteration is + // terminated whenever the active + // set has not changed in the + // previous iteration. + // + // The only trickier part is that + // we have to save the linear + // system (i.e., the matrix and + // right hand side) after + // assembling it in the first + // iteration. The reason is that + // this is the only step where we + // can access the linear system as + // built without any of the contact + // constraints active. We need this + // to compute the residual of the + // solution at other iterations, + // but in other iterations that + // linear system we form has the + // rows and columns that correspond + // to constrained degrees of + // freedom eliminated, and so we + // can no longer access the full + // residual of the original + // equation. template void ObstacleProblem::run () { @@ -843,15 +843,15 @@ namespace Step41 } - // @sect3{The main function} +// @sect3{The main function} - // And this is the main function. It - // follows the pattern of all other - // main functions. The call to - // initialize MPI exists because the - // Trilinos library upon which we - // build our linear solvers in this - // program requires it. +// And this is the main function. It +// follows the pattern of all other +// main functions. The call to +// initialize MPI exists because the +// Trilinos library upon which we +// build our linear solvers in this +// program requires it. int main (int argc, char *argv[]) { try diff --git a/deal.II/examples/step-42/step-42.cc b/deal.II/examples/step-42/step-42.cc index aca98b16e2..4f1f7df1f3 100644 --- a/deal.II/examples/step-42/step-42.cc +++ b/deal.II/examples/step-42/step-42.cc @@ -10,10 +10,10 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // We are using the the same - // include files as in step-41: +// We are using the the same +// include files as in step-41: #include #include @@ -68,32 +68,32 @@ namespace Step42 { using namespace dealii; - // @sect3{The PlasticityContactProblem class template} + // @sect3{The PlasticityContactProblem class template} - // This class provides an interface - // for a constitutive law. In this - // example we are using an elastic - // plastic material with linear, - // isotropic hardening. + // This class provides an interface + // for a constitutive law. In this + // example we are using an elastic + // plastic material with linear, + // isotropic hardening. template class ConstitutiveLaw; - // @sect3{The PlasticityContactProblem class template} - - // This class supplies all function - // and variables needed to describe - // the nonlinear contact problem. It is - // close to step-41 but with some additional - // features like: handling hanging nodes, - // a newton method, using Trilinos and p4est - // for parallel distributed computing. - // To deal with hanging nodes makes - // life a bit more complicated since - // we need an other ConstraintMatrix now. - // We create a newton method for the - // active set method for the contact - // situation and to handle the nonlinear - // operator for the constitutive law. + // @sect3{The PlasticityContactProblem class template} + + // This class supplies all function + // and variables needed to describe + // the nonlinear contact problem. It is + // close to step-41 but with some additional + // features like: handling hanging nodes, + // a newton method, using Trilinos and p4est + // for parallel distributed computing. + // To deal with hanging nodes makes + // life a bit more complicated since + // we need an other ConstraintMatrix now. + // We create a newton method for the + // active set method for the contact + // situation and to handle the nonlinear + // operator for the constitutive law. template class PlasticityContactProblem @@ -114,7 +114,7 @@ namespace Step42 void solve_newton (); void refine_grid (); void move_mesh (const TrilinosWrappers::MPI::Vector &_complete_displacement) const; - void output_results (const std::string& title) const; + void output_results (const std::string &title) const; int n_refinements_global; int n_refinements_local; @@ -172,8 +172,8 @@ namespace Step42 void plast_linear_hardening (SymmetricTensor<4,dim> &stress_strain_tensor, SymmetricTensor<2,dim> &strain_tensor, - unsigned int &elast_points, - unsigned int &plast_points, + unsigned int &elast_points, + unsigned int &plast_points, double &yield); void linearized_plast_linear_hardening (SymmetricTensor<4,dim> &stress_strain_tensor_linearized, SymmetricTensor<4,dim> &stress_strain_tensor, @@ -197,12 +197,12 @@ namespace Step42 template ConstitutiveLaw::ConstitutiveLaw(double _E, double _nu, double _sigma_0, double _gamma, MPI_Comm _mpi_communicator, ConditionalOStream _pcout) - :E (_E), - nu (_nu), - sigma_0 (_sigma_0), - gamma (_gamma), - mpi_communicator (_mpi_communicator), - pcout (_pcout) + :E (_E), + nu (_nu), + sigma_0 (_sigma_0), + gamma (_gamma), + mpi_communicator (_mpi_communicator), + pcout (_pcout) { mu = E/(2*(1+nu)); kappa = E/(3*(1-2*nu)); @@ -214,8 +214,8 @@ namespace Step42 template inline SymmetricTensor<2,dim> ConstitutiveLaw::get_strain (const FEValues &fe_values, - const unsigned int shape_func, - const unsigned int q_point) const + const unsigned int shape_func, + const unsigned int q_point) const { const FEValuesExtractors::Vector displacement (0); SymmetricTensor<2,dim> tmp; @@ -233,63 +233,63 @@ namespace Step42 double &yield) { if (dim == 3) - { - SymmetricTensor<2,dim> stress_tensor; - stress_tensor = (stress_strain_tensor_kappa + stress_strain_tensor_mu)*strain_tensor; - double tmp = E/((1+nu)*(1-2*nu)); + { + SymmetricTensor<2,dim> stress_tensor; + stress_tensor = (stress_strain_tensor_kappa + stress_strain_tensor_mu)*strain_tensor; + double tmp = E/((1+nu)*(1-2*nu)); - SymmetricTensor<2,dim> deviator_stress_tensor = deviator(stress_tensor); + SymmetricTensor<2,dim> deviator_stress_tensor = deviator(stress_tensor); - double deviator_stress_tensor_norm = deviator_stress_tensor.norm (); + double deviator_stress_tensor_norm = deviator_stress_tensor.norm (); - yield = 0; - stress_strain_tensor = stress_strain_tensor_mu; - double beta = 1.0; - if (deviator_stress_tensor_norm >= sigma_0) - { - beta = (sigma_0 + gamma)/deviator_stress_tensor_norm; - stress_strain_tensor *= beta; - yield = 1; - plast_points += 1; - } - else - elast_points += 1; + yield = 0; + stress_strain_tensor = stress_strain_tensor_mu; + double beta = 1.0; + if (deviator_stress_tensor_norm >= sigma_0) + { + beta = (sigma_0 + gamma)/deviator_stress_tensor_norm; + stress_strain_tensor *= beta; + yield = 1; + plast_points += 1; + } + else + elast_points += 1; - stress_strain_tensor += stress_strain_tensor_kappa; - } + stress_strain_tensor += stress_strain_tensor_kappa; + } } template void ConstitutiveLaw::linearized_plast_linear_hardening (SymmetricTensor<4,dim> &stress_strain_tensor_linearized, - SymmetricTensor<4,dim> &stress_strain_tensor, - SymmetricTensor<2,dim> &strain_tensor) + SymmetricTensor<4,dim> &stress_strain_tensor, + SymmetricTensor<2,dim> &strain_tensor) { if (dim == 3) - { - SymmetricTensor<2,dim> stress_tensor; - stress_tensor = (stress_strain_tensor_kappa + stress_strain_tensor_mu)*strain_tensor; - double tmp = E/((1+nu)*(1-2*nu)); + { + SymmetricTensor<2,dim> stress_tensor; + stress_tensor = (stress_strain_tensor_kappa + stress_strain_tensor_mu)*strain_tensor; + double tmp = E/((1+nu)*(1-2*nu)); - stress_strain_tensor = stress_strain_tensor_mu; - stress_strain_tensor_linearized = stress_strain_tensor_mu; + stress_strain_tensor = stress_strain_tensor_mu; + stress_strain_tensor_linearized = stress_strain_tensor_mu; - SymmetricTensor<2,dim> deviator_stress_tensor = deviator(stress_tensor); + SymmetricTensor<2,dim> deviator_stress_tensor = deviator(stress_tensor); - double deviator_stress_tensor_norm = deviator_stress_tensor.norm (); + double deviator_stress_tensor_norm = deviator_stress_tensor.norm (); - double beta = 1.0; - if (deviator_stress_tensor_norm >= sigma_0) - { - beta = (sigma_0 + gamma)/deviator_stress_tensor_norm; - stress_strain_tensor *= beta; - stress_strain_tensor_linearized *= beta; - deviator_stress_tensor /= deviator_stress_tensor_norm; - stress_strain_tensor_linearized -= beta*2*mu*outer_product(deviator_stress_tensor, deviator_stress_tensor); - } + double beta = 1.0; + if (deviator_stress_tensor_norm >= sigma_0) + { + beta = (sigma_0 + gamma)/deviator_stress_tensor_norm; + stress_strain_tensor *= beta; + stress_strain_tensor_linearized *= beta; + deviator_stress_tensor /= deviator_stress_tensor_norm; + stress_strain_tensor_linearized -= beta*2*mu*outer_product(deviator_stress_tensor, deviator_stress_tensor); + } - stress_strain_tensor += stress_strain_tensor_kappa; - stress_strain_tensor_linearized += stress_strain_tensor_kappa; - } + stress_strain_tensor += stress_strain_tensor_kappa; + stress_strain_tensor_linearized += stress_strain_tensor_kappa; + } } namespace EquationData @@ -319,7 +319,7 @@ namespace Step42 return_value = 0.0; if (component == 2) // if ((p(0)-0.5)*(p(0)-0.5)+(p(1)-0.5)*(p(1)-0.5) < 0.2) - // return_value = -5000; + // return_value = -5000; // else return_value = 0.0; // for (unsigned int i=0; i void BoundaryValues::vector_value (const Point &p, - Vector &values) const + Vector &values) const { for (unsigned int c=0; cn_components; ++c) values(c) = BoundaryValues::value (p, c); @@ -400,21 +400,21 @@ namespace Step42 return_value = p(1); if (component == 2) { - // double hz = 0.98; - // double position_x = 0.5; - // double alpha = 12.0; - // double s_x = 0.5039649116; - // double s_y = hz + 0.00026316298; - // if (p(0) > position_x - R && p(0) < s_x) - // { - // return_value = -sqrt(R*R - (p(0)-position_x)*(p(0)-position_x)) + hz + R; - // } - // else if (p(0) >= s_x) - // { - // return_value = 12.0/90.0*p(0) + (s_y - alpha/90.0*s_x); - // } - // else - // return_value = 1e+10; + // double hz = 0.98; + // double position_x = 0.5; + // double alpha = 12.0; + // double s_x = 0.5039649116; + // double s_y = hz + 0.00026316298; + // if (p(0) > position_x - R && p(0) < s_x) + // { + // return_value = -sqrt(R*R - (p(0)-position_x)*(p(0)-position_x)) + hz + R; + // } + // else if (p(0) >= s_x) + // { + // return_value = 12.0/90.0*p(0) + (s_y - alpha/90.0*s_x); + // } + // else + // return_value = 1e+10; // Hindernis Dortmund double x1 = p(0); @@ -433,7 +433,7 @@ namespace Step42 // double R = 1.0; // if (std::pow ((p(0)-1.0/2.0), 2) + std::pow ((p(1)-1.0/2.0), 2) < R*R) // return_value = 1.0 + R - 0.01 - sqrt (R*R - std::pow ((p(0)-1.0/2.0), 2) - // - std::pow ((p(1)-1.0/2.0), 2)); + // - std::pow ((p(1)-1.0/2.0), 2)); // else // return_value = 1e+5; } @@ -450,11 +450,11 @@ namespace Step42 } - // @sect3{Implementation of the PlasticityContactProblem class} + // @sect3{Implementation of the PlasticityContactProblem class} - // Next for the implementation of the class - // template that makes use of the functions - // above. As before, we will write everything + // Next for the implementation of the class + // template that makes use of the functions + // above. As before, we will write everything template PlasticityContactProblem::PlasticityContactProblem (int _n_refinements_global, int _n_refinements_local) @@ -489,8 +489,8 @@ namespace Step42 GridGenerator::subdivided_hyper_rectangle (triangulation, repet, p1, p2); Triangulation<3>::active_cell_iterator - cell = triangulation.begin_active(), - endc = triangulation.end(); + cell = triangulation.begin_active(), + endc = triangulation.end(); /* boundary_indicators: _______ @@ -526,30 +526,30 @@ namespace Step42 // for (; cell!=endc; ++cell) // for (unsigned int face=0; face::faces_per_cell; ++face) // { -// // if (cell->face (face)->at_boundary() -// // && cell->face (face)->boundary_indicator () == 9) -// // { -// // cell->set_refine_flag (); -// // break; -// // } -// // else if (cell->level () == n_refinements + n_refinements_local - 1) -// // { -// // cell->set_refine_flag (); -// // break; -// // } +// // if (cell->face (face)->at_boundary() +// // && cell->face (face)->boundary_indicator () == 9) +// // { +// // cell->set_refine_flag (); +// // break; +// // } +// // else if (cell->level () == n_refinements + n_refinements_local - 1) +// // { +// // cell->set_refine_flag (); +// // break; +// // } // -// // if (cell->face (face)->at_boundary() -// // && cell->face (face)->boundary_indicator () == 9) -// // { -// // if (cell->face (face)->vertex (0)(0) <= 0.7 && -// // cell->face (face)->vertex (1)(0) >= 0.3 && -// // cell->face (face)->vertex (0)(1) <= 0.875 && -// // cell->face (face)->vertex (2)(1) >= 0.125) -// // { -// // cell->set_refine_flag (); -// // break; -// // } -// // } +// // if (cell->face (face)->at_boundary() +// // && cell->face (face)->boundary_indicator () == 9) +// // { +// // if (cell->face (face)->vertex (0)(0) <= 0.7 && +// // cell->face (face)->vertex (1)(0) >= 0.3 && +// // cell->face (face)->vertex (0)(1) <= 0.875 && +// // cell->face (face)->vertex (2)(1) >= 0.125) +// // { +// // cell->set_refine_flag (); +// // break; +// // } +// // } // // if (step == 0 && // cell->center ()(2) < n_refinements_local*9.0/64.0) @@ -584,14 +584,14 @@ namespace Step42 constraints_hanging_nodes.close (); pcout << "Number of active cells: " - << triangulation.n_active_cells() - << std::endl - << "Total number of cells: " - << triangulation.n_cells() - << std::endl - << "Number of degrees of freedom: " - << dof_handler.n_dofs () - << std::endl; + << triangulation.n_active_cells() + << std::endl + << "Total number of cells: " + << triangulation.n_cells() + << std::endl + << "Number of degrees of freedom: " + << dof_handler.n_dofs () + << std::endl; dirichlet_constraints (); } @@ -623,8 +623,8 @@ namespace Step42 mass_matrix.reinit (sp); assemble_mass_matrix_diagonal (mass_matrix); const unsigned int - start = (system_rhs_newton.local_range().first), - end = (system_rhs_newton.local_range().second); + start = (system_rhs_newton.local_range().first), + end = (system_rhs_newton.local_range().second); for (unsigned int j=start; jget_strain(fe_values, i, q_point); @@ -856,8 +856,8 @@ namespace Step42 cell->get_dof_indices (local_dof_indices); constraints_dirichlet_hanging_nodes.distribute_local_to_global (cell_rhs, - local_dof_indices, - system_rhs_newton); + local_dof_indices, + system_rhs_newton); cell_number += 1; }; @@ -890,8 +890,8 @@ namespace Step42 const FEValuesExtractors::Vector displacement (0); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) if (cell->is_locally_owned()) @@ -911,17 +911,17 @@ namespace Step42 cell->get_dof_indices (local_dof_indices); constraints_dirichlet_hanging_nodes.distribute_local_to_global (cell_matrix, - local_dof_indices, - mass_matrix); + local_dof_indices, + mass_matrix); } mass_matrix.compress (); } - // @sect4{PlasticityContactProblem::update_solution_and_constraints} + // @sect4{PlasticityContactProblem::update_solution_and_constraints} - // Projection and updating of the active set - // for the dofs which penetrates the obstacle. + // Projection and updating of the active set + // for the dofs which penetrates the obstacle. template void PlasticityContactProblem::update_solution_and_constraints () { @@ -975,10 +975,10 @@ namespace Step42 diag_mass_matrix_vector_relevant (index_z) * (solution_index_z - gap) > 0 && - !(constraints_hanging_nodes.is_constrained(index_z))) + !(constraints_hanging_nodes.is_constrained(index_z))) { - constraints.add_line (index_z); - constraints.set_inhomogeneity (index_z, gap); + constraints.add_line (index_z); + constraints.set_inhomogeneity (index_z, gap); distributed_solution (index_z) = gap; @@ -1002,12 +1002,12 @@ namespace Step42 constraints.merge (constraints_dirichlet_hanging_nodes); } - // @sect4{PlasticityContactProblem::dirichlet_constraints} + // @sect4{PlasticityContactProblem::dirichlet_constraints} - // This function defines the new ConstraintMatrix - // constraints_dirichlet_hanging_nodes. It contains - // the dirichlet boundary values as well as the - // hanging nodes constraints. + // This function defines the new ConstraintMatrix + // constraints_dirichlet_hanging_nodes. It contains + // the dirichlet boundary values as well as the + // hanging nodes constraints. template void PlasticityContactProblem::dirichlet_constraints () { @@ -1024,54 +1024,54 @@ namespace Step42 constraints_dirichlet_hanging_nodes.reinit (locally_relevant_dofs); constraints_dirichlet_hanging_nodes.merge (constraints_hanging_nodes); - // interpolate all components of the solution + // interpolate all components of the solution VectorTools::interpolate_boundary_values (dof_handler, 6, EquationData::BoundaryValues(), constraints_dirichlet_hanging_nodes, - ComponentMask()); + ComponentMask()); - // interpolate x- and y-components of the - // solution (this is a bit mask, so apply - // operator| ) + // interpolate x- and y-components of the + // solution (this is a bit mask, so apply + // operator| ) FEValuesExtractors::Scalar x_displacement(0); FEValuesExtractors::Scalar y_displacement(1); VectorTools::interpolate_boundary_values (dof_handler, 8, EquationData::BoundaryValues(), constraints_dirichlet_hanging_nodes, - (fe.component_mask(x_displacement) - | - fe.component_mask(y_displacement))); + (fe.component_mask(x_displacement) + | + fe.component_mask(y_displacement))); constraints_dirichlet_hanging_nodes.close (); } - // @sect4{PlasticityContactProblem::solve} - - // In addition to step-41 we have - // to deal with the hanging node - // constraints. Again we also consider - // the locally_owned_dofs only by - // creating the vector distributed_solution. - // - // For the hanging nodes we have to apply - // the set_zero function to system_rhs_newton. - // This is necessary if a hanging node value x_0 - // has one neighbor which is in contact with - // value x_0 and one neighbor which is not with - // value x_1. This leads to an inhomogeneity - // constraint with value x_1/2 = gap/2 in the - // ConstraintMatrix. - // So the corresponding entries in the - // ride-hang-side are non-zero with a - // meaningless value. These values have to - // to set to zero. - - // The rest of the funtion is smiliar to - // step-41 except that we use a FGMRES-solver - // instead of CG. For a very small hardening - // value gamma the linear system becomes - // almost semi definite but still symmetric. + // @sect4{PlasticityContactProblem::solve} + + // In addition to step-41 we have + // to deal with the hanging node + // constraints. Again we also consider + // the locally_owned_dofs only by + // creating the vector distributed_solution. + // + // For the hanging nodes we have to apply + // the set_zero function to system_rhs_newton. + // This is necessary if a hanging node value x_0 + // has one neighbor which is in contact with + // value x_0 and one neighbor which is not with + // value x_1. This leads to an inhomogeneity + // constraint with value x_1/2 = gap/2 in the + // ConstraintMatrix. + // So the corresponding entries in the + // ride-hang-side are non-zero with a + // meaningless value. These values have to + // to set to zero. + + // The rest of the funtion is smiliar to + // step-41 except that we use a FGMRES-solver + // instead of CG. For a very small hardening + // value gamma the linear system becomes + // almost semi definite but still symmetric. template void PlasticityContactProblem::solve () { @@ -1092,7 +1092,7 @@ namespace Step42 MPI_Barrier (mpi_communicator); t.stop(); if (Utilities::MPI::this_mpi_process(mpi_communicator) == 0) - run_time[6] += t.wall_time(); + run_time[6] += t.wall_time(); MPI_Barrier (mpi_communicator); t.restart(); @@ -1100,25 +1100,25 @@ namespace Step42 PrimitiveVectorMemory mem; TrilinosWrappers::MPI::Vector tmp (system_rhs_newton); const double solver_tolerance = 1e-4 * - system_matrix_newton.residual (tmp, distributed_solution, system_rhs_newton); + system_matrix_newton.residual (tmp, distributed_solution, system_rhs_newton); SolverControl solver_control (system_matrix_newton.m(), solver_tolerance); SolverFGMRES - solver(solver_control, mem, - SolverFGMRES:: - AdditionalData(30, true)); + solver(solver_control, mem, + SolverFGMRES:: + AdditionalData(30, true)); solver.solve(system_matrix_newton, distributed_solution, system_rhs_newton, preconditioner_u); pcout << "Initial error: " << solver_control.initial_value() < > constant_modes; DoFTools::extract_constant_modes (dof_handler, - ComponentMask(), + ComponentMask(), constant_modes); additional_data.elliptic = true; @@ -1153,7 +1153,7 @@ namespace Step42 IndexSet active_set_old (active_set); unsigned int j = 0; unsigned int number_assemble_system = 0; - for (; j<=100;j++) + for (; j<=100; j++) { pcout<< " " <::type_dof_data, data_component_interpretation); @@ -1424,7 +1424,7 @@ namespace Step42 } } - // @sect3{The main function} +// @sect3{The main function} int main (int argc, char *argv[]) { @@ -1443,10 +1443,10 @@ int main (int argc, char *argv[]) int _n_refinements_local = 1; if (argc == 3) - { - _n_refinements_global = atoi(argv[1]); - _n_refinements_local = atoi(argv[2]); - } + { + _n_refinements_global = atoi(argv[1]); + _n_refinements_local = atoi(argv[2]); + } PlasticityContactProblem<3> laplace_problem_3d (_n_refinements_global, _n_refinements_local); laplace_problem_3d.run (); diff --git a/deal.II/examples/step-43/step-43.cc b/deal.II/examples/step-43/step-43.cc index 6876372425..ec3d2f2fa3 100644 --- a/deal.II/examples/step-43/step-43.cc +++ b/deal.II/examples/step-43/step-43.cc @@ -11,20 +11,20 @@ /* further information on this license. */ - // @sect3{Include files} - - // The first step, as always, is to - // include the functionality of a - // number of deal.II and C++ header - // files. - // - // The list includes some header - // files that provide vector, matrix, - // and preconditioner classes that - // implement interfaces to the - // respective Trilinos classes; some - // more information on these may be - // found in step-31. +// @sect3{Include files} + +// The first step, as always, is to +// include the functionality of a +// number of deal.II and C++ header +// files. +// +// The list includes some header +// files that provide vector, matrix, +// and preconditioner classes that +// implement interfaces to the +// respective Trilinos classes; some +// more information on these may be +// found in step-31. #include #include #include @@ -67,31 +67,31 @@ #include - // At the end of this top-matter, we - // open a namespace for the current - // project into which all the - // following material will go, and - // then import all deal.II names into - // this namespace: +// At the end of this top-matter, we +// open a namespace for the current +// project into which all the +// following material will go, and +// then import all deal.II names into +// this namespace: namespace Step43 { using namespace dealii; - // @sect3{Pressure right hand side, pressure boundary values and saturation initial value classes} + // @sect3{Pressure right hand side, pressure boundary values and saturation initial value classes} - // The following part is taken - // directly from step-21 so there is - // no need to repeat the - // descriptions found there. + // The following part is taken + // directly from step-21 so there is + // no need to repeat the + // descriptions found there. template class PressureRightHandSide : public Function { - public: - PressureRightHandSide () : Function(1) {} + public: + PressureRightHandSide () : Function(1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -108,11 +108,11 @@ namespace Step43 template class PressureBoundaryValues : public Function { - public: - PressureBoundaryValues () : Function(1) {} + public: + PressureBoundaryValues () : Function(1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -128,11 +128,11 @@ namespace Step43 template class SaturationBoundaryValues : public Function { - public: - SaturationBoundaryValues () : Function(1) {} + public: + SaturationBoundaryValues () : Function(1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; @@ -152,14 +152,14 @@ namespace Step43 template class SaturationInitialValues : public Function { - public: - SaturationInitialValues () : Function(1) {} + public: + SaturationInitialValues () : Function(1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void vector_value (const Point &p, - Vector &value) const; + virtual void vector_value (const Point &p, + Vector &value) const; }; @@ -182,26 +182,26 @@ namespace Step43 } - // @sect3{Permeability models} + // @sect3{Permeability models} - // In this tutorial, we still use - // the two permeability models - // previously used in step-21 so we - // again refrain from commenting in - // detail about them. + // In this tutorial, we still use + // the two permeability models + // previously used in step-21 so we + // again refrain from commenting in + // detail about them. namespace SingleCurvingCrack { template class KInverse : public TensorFunction<2,dim> { - public: - KInverse () - : - TensorFunction<2,dim> () - {} - - virtual void value_list (const std::vector > &points, - std::vector > &values) const; + public: + KInverse () + : + TensorFunction<2,dim> () + {} + + virtual void value_list (const std::vector > &points, + std::vector > &values) const; }; @@ -237,19 +237,19 @@ namespace Step43 template class KInverse : public TensorFunction<2,dim> { - public: - KInverse () - : - TensorFunction<2,dim> () - {} + public: + KInverse () + : + TensorFunction<2,dim> () + {} - virtual void value_list (const std::vector > &points, - std::vector > &values) const; + virtual void value_list (const std::vector > &points, + std::vector > &values) const; - private: - static std::vector > centers; + private: + static std::vector > centers; - static std::vector > get_centers (); + static std::vector > get_centers (); }; @@ -306,28 +306,28 @@ namespace Step43 } - // @sect3{Physical quantities} - - // The implementations of all the - // physical quantities such as - // total mobility $\lambda_t$ and - // fractional flow of water $F$ are - // taken from step-21 so again we - // don't have do any comment about - // them. Compared to step-21 we - // have added checks that the - // saturation passed to these - // functions is in fact within the - // physically valid - // range. Furthermore, given that - // the wetting phase moves at speed - // $\mathbf u F'(S)$ it is clear - // that $F'(S)$ must be greater or - // equal to zero, so we assert that - // as well to make sure that our - // calculations to get at the - // formula for the derivative made - // sense. + // @sect3{Physical quantities} + + // The implementations of all the + // physical quantities such as + // total mobility $\lambda_t$ and + // fractional flow of water $F$ are + // taken from step-21 so again we + // don't have do any comment about + // them. Compared to step-21 we + // have added checks that the + // saturation passed to these + // functions is in fact within the + // physically valid + // range. Furthermore, given that + // the wetting phase moves at speed + // $\mathbf u F'(S)$ it is clear + // that $F'(S)$ must be greater or + // equal to zero, so we assert that + // as well to make sure that our + // calculations to get at the + // formula for the derivative made + // sense. double mobility_inverse (const double S, const double viscosity) { @@ -367,37 +367,37 @@ namespace Step43 } - // @sect3{Helper classes for solvers and preconditioners} - - // In this first part we define a - // number of classes that we need - // in the construction of linear - // solvers and - // preconditioners. This part is - // essentially the same as that - // used in step-31. The only - // difference is that the original - // variable name stokes_matrix is - // replaced by another name - // darcy_matrix to match our - // problem. + // @sect3{Helper classes for solvers and preconditioners} + + // In this first part we define a + // number of classes that we need + // in the construction of linear + // solvers and + // preconditioners. This part is + // essentially the same as that + // used in step-31. The only + // difference is that the original + // variable name stokes_matrix is + // replaced by another name + // darcy_matrix to match our + // problem. namespace LinearSolvers { template class InverseMatrix : public Subscriptor { - public: - InverseMatrix (const Matrix &m, - const Preconditioner &preconditioner); + public: + InverseMatrix (const Matrix &m, + const Preconditioner &preconditioner); - template - void vmult (VectorType &dst, - const VectorType &src) const; + template + void vmult (VectorType &dst, + const VectorType &src) const; - private: - const SmartPointer matrix; - const Preconditioner &preconditioner; + private: + const SmartPointer matrix; + const Preconditioner &preconditioner; }; @@ -405,9 +405,9 @@ namespace Step43 InverseMatrix:: InverseMatrix (const Matrix &m, const Preconditioner &preconditioner) - : - matrix (&m), - preconditioner (preconditioner) + : + matrix (&m), + preconditioner (preconditioner) {} @@ -437,23 +437,23 @@ namespace Step43 template class BlockSchurPreconditioner : public Subscriptor { - public: - BlockSchurPreconditioner ( - const TrilinosWrappers::BlockSparseMatrix &S, - const InverseMatrix &Mpinv, - const PreconditionerA &Apreconditioner); - - void vmult (TrilinosWrappers::BlockVector &dst, - const TrilinosWrappers::BlockVector &src) const; - - private: - const SmartPointer darcy_matrix; - const SmartPointer > m_inverse; - const PreconditionerA &a_preconditioner; - - mutable TrilinosWrappers::Vector tmp; + public: + BlockSchurPreconditioner ( + const TrilinosWrappers::BlockSparseMatrix &S, + const InverseMatrix &Mpinv, + const PreconditionerA &Apreconditioner); + + void vmult (TrilinosWrappers::BlockVector &dst, + const TrilinosWrappers::BlockVector &src) const; + + private: + const SmartPointer darcy_matrix; + const SmartPointer > m_inverse; + const PreconditionerA &a_preconditioner; + + mutable TrilinosWrappers::Vector tmp; }; @@ -462,13 +462,13 @@ namespace Step43 BlockSchurPreconditioner:: BlockSchurPreconditioner(const TrilinosWrappers::BlockSparseMatrix &S, const InverseMatrix &Mpinv, + PreconditionerMp> &Mpinv, const PreconditionerA &Apreconditioner) - : - darcy_matrix (&S), - m_inverse (&Mpinv), - a_preconditioner (Apreconditioner), - tmp (darcy_matrix->block(1,1).m()) + : + darcy_matrix (&S), + m_inverse (&Mpinv), + a_preconditioner (Apreconditioner), + tmp (darcy_matrix->block(1,1).m()) {} @@ -485,318 +485,318 @@ namespace Step43 } - // @sect3{The TwoPhaseFlowProblem class} - - // The definition of the class that - // defines the top-level logic of - // solving the time-dependent - // advection-dominated two-phase - // flow problem (or - // Buckley-Leverett problem - // [Buckley 1942]) is mainly based - // on tutorial programs step-21 and - // step-33, and in particular on - // step-31 where we have used - // basically the same general - // structure as done here. As in - // step-31, the key routines to - // look for in the implementation - // below are the run() - // and solve() - // functions. - // - // The main difference to step-31 - // is that, since adaptive operator - // splitting is considered, we need - // a couple more member variables - // to hold the last two computed - // Darcy (velocity/pressure) - // solutions in addition to the - // current one (which is either - // computed directly, or - // extrapolated from the previous - // two), and we need to remember - // the last two times we computed - // the Darcy solution. We also need - // a helper function that figures - // out whether we do indeed need to - // recompute the Darcy solution. - // - // Unlike step-31, this step uses - // one more ConstraintMatrix object - // called - // darcy_preconditioner_constraints. This - // constraint object is used only - // for assembling the matrix for - // the Darcy preconditioner and - // includes hanging node constrants - // as well as Dirichlet boundary - // value constraints for the - // pressure variable. We need this - // because we are building a - // Laplace matrix for the pressure - // as an approximation of the Schur - // complement) which is only - // positive definite if boundary - // conditions are applied. - // - // The collection of member - // functions and variables thus - // declared in this class is then - // rather similar to those in - // step-31: + // @sect3{The TwoPhaseFlowProblem class} + + // The definition of the class that + // defines the top-level logic of + // solving the time-dependent + // advection-dominated two-phase + // flow problem (or + // Buckley-Leverett problem + // [Buckley 1942]) is mainly based + // on tutorial programs step-21 and + // step-33, and in particular on + // step-31 where we have used + // basically the same general + // structure as done here. As in + // step-31, the key routines to + // look for in the implementation + // below are the run() + // and solve() + // functions. + // + // The main difference to step-31 + // is that, since adaptive operator + // splitting is considered, we need + // a couple more member variables + // to hold the last two computed + // Darcy (velocity/pressure) + // solutions in addition to the + // current one (which is either + // computed directly, or + // extrapolated from the previous + // two), and we need to remember + // the last two times we computed + // the Darcy solution. We also need + // a helper function that figures + // out whether we do indeed need to + // recompute the Darcy solution. + // + // Unlike step-31, this step uses + // one more ConstraintMatrix object + // called + // darcy_preconditioner_constraints. This + // constraint object is used only + // for assembling the matrix for + // the Darcy preconditioner and + // includes hanging node constrants + // as well as Dirichlet boundary + // value constraints for the + // pressure variable. We need this + // because we are building a + // Laplace matrix for the pressure + // as an approximation of the Schur + // complement) which is only + // positive definite if boundary + // conditions are applied. + // + // The collection of member + // functions and variables thus + // declared in this class is then + // rather similar to those in + // step-31: template class TwoPhaseFlowProblem { - public: - TwoPhaseFlowProblem (const unsigned int degree); - void run (); - - private: - void setup_dofs (); - void assemble_darcy_preconditioner (); - void build_darcy_preconditioner (); - void assemble_darcy_system (); - void assemble_saturation_system (); - void assemble_saturation_matrix (); - void assemble_saturation_rhs (); - void assemble_saturation_rhs_cell_term (const FEValues &saturation_fe_values, - const FEValues &darcy_fe_values, - const double global_max_u_F_prime, - const double global_S_variation, - const std::vector &local_dof_indices); - void assemble_saturation_rhs_boundary_term (const FEFaceValues &saturation_fe_face_values, - const FEFaceValues &darcy_fe_face_values, - const std::vector &local_dof_indices); - void solve (); - void refine_mesh (const unsigned int min_grid_level, - const unsigned int max_grid_level); - void output_results () const; - - // We follow with a number of - // helper functions that are - // used in a variety of places - // throughout the program: - double get_max_u_F_prime () const; - std::pair get_extrapolated_saturation_range () const; - bool determine_whether_to_solve_for_pressure_and_velocity () const; - void project_back_saturation (); - double compute_viscosity (const std::vector &old_saturation, - const std::vector &old_old_saturation, - const std::vector > &old_saturation_grads, - const std::vector > &old_old_saturation_grads, - const std::vector > &present_darcy_values, - const double global_max_u_F_prime, - const double global_S_variation, - const double cell_diameter) const; - - - // This all is followed by the - // member variables, most of - // which are similar to the - // ones in step-31, with the - // exception of the ones that - // pertain to the macro time - // stepping for the - // velocity/pressure system: - Triangulation triangulation; - double global_Omega_diameter; - - const unsigned int degree; - - const unsigned int darcy_degree; - FESystem darcy_fe; - DoFHandler darcy_dof_handler; - ConstraintMatrix darcy_constraints; - - ConstraintMatrix darcy_preconditioner_constraints; - - TrilinosWrappers::BlockSparseMatrix darcy_matrix; - TrilinosWrappers::BlockSparseMatrix darcy_preconditioner_matrix; - - TrilinosWrappers::BlockVector darcy_solution; - TrilinosWrappers::BlockVector darcy_rhs; - - TrilinosWrappers::BlockVector last_computed_darcy_solution; - TrilinosWrappers::BlockVector second_last_computed_darcy_solution; - - - const unsigned int saturation_degree; - FE_Q saturation_fe; - DoFHandler saturation_dof_handler; - ConstraintMatrix saturation_constraints; - - TrilinosWrappers::SparseMatrix saturation_matrix; - - - TrilinosWrappers::Vector saturation_solution; - TrilinosWrappers::Vector old_saturation_solution; - TrilinosWrappers::Vector old_old_saturation_solution; - TrilinosWrappers::Vector saturation_rhs; - - TrilinosWrappers::Vector saturation_matching_last_computed_darcy_solution; - - const double saturation_refinement_threshold; - - double time; - const double end_time; - - double current_macro_time_step; - double old_macro_time_step; - - double time_step; - double old_time_step; - unsigned int timestep_number; - - const double viscosity; - const double porosity; - const double AOS_threshold; - - std_cxx1x::shared_ptr Amg_preconditioner; - std_cxx1x::shared_ptr Mp_preconditioner; - - bool rebuild_saturation_matrix; - - // At the very end we declare a - // variable that denotes the - // material model. Compared to - // step-21, we do this here as - // a member variable since we - // will want to use it in a - // variety of places and so - // having a central place where - // such a variable is declared - // will make it simpler to - // replace one class by another - // (e.g. replace - // RandomMedium::KInverse by - // SingleCurvingCrack::KInverse). - const RandomMedium::KInverse k_inverse; + public: + TwoPhaseFlowProblem (const unsigned int degree); + void run (); + + private: + void setup_dofs (); + void assemble_darcy_preconditioner (); + void build_darcy_preconditioner (); + void assemble_darcy_system (); + void assemble_saturation_system (); + void assemble_saturation_matrix (); + void assemble_saturation_rhs (); + void assemble_saturation_rhs_cell_term (const FEValues &saturation_fe_values, + const FEValues &darcy_fe_values, + const double global_max_u_F_prime, + const double global_S_variation, + const std::vector &local_dof_indices); + void assemble_saturation_rhs_boundary_term (const FEFaceValues &saturation_fe_face_values, + const FEFaceValues &darcy_fe_face_values, + const std::vector &local_dof_indices); + void solve (); + void refine_mesh (const unsigned int min_grid_level, + const unsigned int max_grid_level); + void output_results () const; + + // We follow with a number of + // helper functions that are + // used in a variety of places + // throughout the program: + double get_max_u_F_prime () const; + std::pair get_extrapolated_saturation_range () const; + bool determine_whether_to_solve_for_pressure_and_velocity () const; + void project_back_saturation (); + double compute_viscosity (const std::vector &old_saturation, + const std::vector &old_old_saturation, + const std::vector > &old_saturation_grads, + const std::vector > &old_old_saturation_grads, + const std::vector > &present_darcy_values, + const double global_max_u_F_prime, + const double global_S_variation, + const double cell_diameter) const; + + + // This all is followed by the + // member variables, most of + // which are similar to the + // ones in step-31, with the + // exception of the ones that + // pertain to the macro time + // stepping for the + // velocity/pressure system: + Triangulation triangulation; + double global_Omega_diameter; + + const unsigned int degree; + + const unsigned int darcy_degree; + FESystem darcy_fe; + DoFHandler darcy_dof_handler; + ConstraintMatrix darcy_constraints; + + ConstraintMatrix darcy_preconditioner_constraints; + + TrilinosWrappers::BlockSparseMatrix darcy_matrix; + TrilinosWrappers::BlockSparseMatrix darcy_preconditioner_matrix; + + TrilinosWrappers::BlockVector darcy_solution; + TrilinosWrappers::BlockVector darcy_rhs; + + TrilinosWrappers::BlockVector last_computed_darcy_solution; + TrilinosWrappers::BlockVector second_last_computed_darcy_solution; + + + const unsigned int saturation_degree; + FE_Q saturation_fe; + DoFHandler saturation_dof_handler; + ConstraintMatrix saturation_constraints; + + TrilinosWrappers::SparseMatrix saturation_matrix; + + + TrilinosWrappers::Vector saturation_solution; + TrilinosWrappers::Vector old_saturation_solution; + TrilinosWrappers::Vector old_old_saturation_solution; + TrilinosWrappers::Vector saturation_rhs; + + TrilinosWrappers::Vector saturation_matching_last_computed_darcy_solution; + + const double saturation_refinement_threshold; + + double time; + const double end_time; + + double current_macro_time_step; + double old_macro_time_step; + + double time_step; + double old_time_step; + unsigned int timestep_number; + + const double viscosity; + const double porosity; + const double AOS_threshold; + + std_cxx1x::shared_ptr Amg_preconditioner; + std_cxx1x::shared_ptr Mp_preconditioner; + + bool rebuild_saturation_matrix; + + // At the very end we declare a + // variable that denotes the + // material model. Compared to + // step-21, we do this here as + // a member variable since we + // will want to use it in a + // variety of places and so + // having a central place where + // such a variable is declared + // will make it simpler to + // replace one class by another + // (e.g. replace + // RandomMedium::KInverse by + // SingleCurvingCrack::KInverse). + const RandomMedium::KInverse k_inverse; }; - // @sect3{TwoPhaseFlowProblem::TwoPhaseFlowProblem} - - // The constructor of this class is an - // extension of the constructors in step-21 - // and step-31. We need to add the various - // variables that concern the saturation. As - // discussed in the introduction, we are - // going to use $Q_2 \times Q_1$ - // (Taylor-Hood) elements again for the Darcy - // system, an element combination that fulfills - // the Ladyzhenskaya-Babuska-Brezzi (LBB) - // conditions - // [Brezzi and Fortin 1991, Chen 2005], and $Q_1$ - // elements for the saturation. However, by - // using variables that store the polynomial - // degree of the Darcy and temperature finite - // elements, it is easy to consistently - // modify the degree of the elements as well - // as all quadrature formulas used on them - // downstream. Moreover, we initialize the - // time stepping variables related to - // operator splitting as well as the option - // for matrix assembly and preconditioning: + // @sect3{TwoPhaseFlowProblem::TwoPhaseFlowProblem} + + // The constructor of this class is an + // extension of the constructors in step-21 + // and step-31. We need to add the various + // variables that concern the saturation. As + // discussed in the introduction, we are + // going to use $Q_2 \times Q_1$ + // (Taylor-Hood) elements again for the Darcy + // system, an element combination that fulfills + // the Ladyzhenskaya-Babuska-Brezzi (LBB) + // conditions + // [Brezzi and Fortin 1991, Chen 2005], and $Q_1$ + // elements for the saturation. However, by + // using variables that store the polynomial + // degree of the Darcy and temperature finite + // elements, it is easy to consistently + // modify the degree of the elements as well + // as all quadrature formulas used on them + // downstream. Moreover, we initialize the + // time stepping variables related to + // operator splitting as well as the option + // for matrix assembly and preconditioning: template TwoPhaseFlowProblem::TwoPhaseFlowProblem (const unsigned int degree) - : - triangulation (Triangulation::maximum_smoothing), + : + triangulation (Triangulation::maximum_smoothing), - degree (degree), - darcy_degree (degree), - darcy_fe (FE_Q(darcy_degree+1), dim, - FE_Q(darcy_degree), 1), - darcy_dof_handler (triangulation), + degree (degree), + darcy_degree (degree), + darcy_fe (FE_Q(darcy_degree+1), dim, + FE_Q(darcy_degree), 1), + darcy_dof_handler (triangulation), - saturation_degree (degree+1), - saturation_fe (saturation_degree), - saturation_dof_handler (triangulation), + saturation_degree (degree+1), + saturation_fe (saturation_degree), + saturation_dof_handler (triangulation), - saturation_refinement_threshold (0.5), + saturation_refinement_threshold (0.5), - time (0), - end_time (10), + time (0), + end_time (10), - current_macro_time_step (0), - old_macro_time_step (0), + current_macro_time_step (0), + old_macro_time_step (0), - time_step (0), - old_time_step (0), - viscosity (0.2), - porosity (1.0), - AOS_threshold (3.0), + time_step (0), + old_time_step (0), + viscosity (0.2), + porosity (1.0), + AOS_threshold (3.0), - rebuild_saturation_matrix (true) + rebuild_saturation_matrix (true) {} - // @sect3{TwoPhaseFlowProblem::setup_dofs} - - // This is the function that sets up the - // DoFHandler objects we have here (one for - // the Darcy part and one for the saturation - // part) as well as set to the right sizes - // the various objects required for the - // linear algebra in this program. Its basic - // operations are similar to what - // step-31 did. - // - // The body of the function first enumerates - // all degrees of freedom for the Darcy and - // saturation systems. For the Darcy part, - // degrees of freedom are then sorted to - // ensure that velocities precede pressure - // DoFs so that we can partition the Darcy - // matrix into a $2 \times 2$ matrix. - // - // Then, we need to incorporate - // hanging node constraints and - // Dirichlet boundary value - // constraints into - // darcy_preconditioner_constraints. - // The boundary condition - // constraints are only set on the - // pressure component since the - // Schur complement preconditioner - // that corresponds to the porous - // media flow operator in non-mixed - // form, $-\nabla \cdot [\mathbf K - // \lambda_t(S)]\nabla$, acts only - // on the pressure - // variable. Therefore, we use a - // component_mask that filters out - // the velocity component, so that - // the condensation is performed on - // pressure degrees of freedom - // only. - // - // After having done so, we count - // the number of degrees of freedom - // in the various blocks. This - // information is then used to - // create the sparsity pattern for - // the Darcy and saturation system - // matrices as well as the - // preconditioner matrix from which - // we build the Darcy - // preconditioner. As in step-31, - // we choose to create the pattern - // not as in the first few tutorial - // programs, but by using the - // blocked version of - // CompressedSimpleSparsityPattern. The - // reason for doing this is mainly - // memory, that is, the - // SparsityPattern class would - // consume too much memory when - // used in three spatial dimensions - // as we intend to do for this - // program. So, for this, we follow - // the same way as step-31 did and - // we don't have to repeat - // descriptions again for the rest - // of the member function. + // @sect3{TwoPhaseFlowProblem::setup_dofs} + + // This is the function that sets up the + // DoFHandler objects we have here (one for + // the Darcy part and one for the saturation + // part) as well as set to the right sizes + // the various objects required for the + // linear algebra in this program. Its basic + // operations are similar to what + // step-31 did. + // + // The body of the function first enumerates + // all degrees of freedom for the Darcy and + // saturation systems. For the Darcy part, + // degrees of freedom are then sorted to + // ensure that velocities precede pressure + // DoFs so that we can partition the Darcy + // matrix into a $2 \times 2$ matrix. + // + // Then, we need to incorporate + // hanging node constraints and + // Dirichlet boundary value + // constraints into + // darcy_preconditioner_constraints. + // The boundary condition + // constraints are only set on the + // pressure component since the + // Schur complement preconditioner + // that corresponds to the porous + // media flow operator in non-mixed + // form, $-\nabla \cdot [\mathbf K + // \lambda_t(S)]\nabla$, acts only + // on the pressure + // variable. Therefore, we use a + // component_mask that filters out + // the velocity component, so that + // the condensation is performed on + // pressure degrees of freedom + // only. + // + // After having done so, we count + // the number of degrees of freedom + // in the various blocks. This + // information is then used to + // create the sparsity pattern for + // the Darcy and saturation system + // matrices as well as the + // preconditioner matrix from which + // we build the Darcy + // preconditioner. As in step-31, + // we choose to create the pattern + // not as in the first few tutorial + // programs, but by using the + // blocked version of + // CompressedSimpleSparsityPattern. The + // reason for doing this is mainly + // memory, that is, the + // SparsityPattern class would + // consume too much memory when + // used in three spatial dimensions + // as we intend to do for this + // program. So, for this, we follow + // the same way as step-31 did and + // we don't have to repeat + // descriptions again for the rest + // of the member function. template void TwoPhaseFlowProblem::setup_dofs () { @@ -825,7 +825,7 @@ namespace Step43 DoFTools::make_hanging_node_constraints (darcy_dof_handler, darcy_preconditioner_constraints); DoFTools::make_zero_boundary_constraints (darcy_dof_handler, darcy_preconditioner_constraints, - darcy_fe.component_mask(pressure)); + darcy_fe.component_mask(pressure)); darcy_preconditioner_constraints.close (); } @@ -948,84 +948,84 @@ namespace Step43 } - // @sect3{Assembling matrices and preconditioners} - - // The next few functions are - // devoted to setting up the - // various system and - // preconditioner matrices and - // right hand sides that we have to - // deal with in this program. - - // @sect4{TwoPhaseFlowProblem::assemble_darcy_preconditioner} - - // This function assembles the matrix we use - // for preconditioning the Darcy system. What - // we need are a vector mass matrix weighted by - // $\left(\mathbf{K} \lambda_t\right)^{-1}$ - // on the velocity components and a mass - // matrix weighted by $\left(\mathbf{K} - // \lambda_t\right)$ on the pressure - // component. We start by generating a - // quadrature object of appropriate order, - // the FEValues object that can give values - // and gradients at the quadrature points - // (together with quadrature weights). Next - // we create data structures for the cell - // matrix and the relation between local and - // global DoFs. The vectors phi_u and - // grad_phi_p are going to hold the values of - // the basis functions in order to faster - // build up the local matrices, as was - // already done in step-22. Before we start - // the loop over all active cells, we have to - // specify which components are pressure and - // which are velocity. - // - // The creation of the local matrix - // is rather simple. There are only - // a term weighted by - // $\left(\mathbf{K} - // \lambda_t\right)^{-1}$ (on the - // velocity) and a Laplace matrix - // weighted by $\left(\mathbf{K} - // \lambda_t\right)$ to be - // generated, so the creation of - // the local matrix is done in - // essentially two lines. Since the - // material model functions at the - // top of this file only provide - // the inverses of the permeability - // and mobility, we have to compute - // $\mathbf K$ and $\lambda_t$ by - // hand from the given values, once - // per quadrature point. - // - // Once the - // local matrix is ready (loop over - // rows and columns in the local - // matrix on each quadrature - // point), we get the local DoF - // indices and write the local - // information into the global - // matrix. We do this by directly - // applying the constraints - // (i.e. darcy_preconditioner_constraints) - // that takes care of hanging node - // and zero Dirichlet boundary - // condition constraints. By doing - // so, we don't have to do that - // afterwards, and we later don't - // have to use - // ConstraintMatrix::condense and - // MatrixTools::apply_boundary_values, - // both functions that would need - // to modify matrix and vector - // entries and so are difficult to - // write for the Trilinos classes - // where we don't immediately have - // access to individual memory - // locations. + // @sect3{Assembling matrices and preconditioners} + + // The next few functions are + // devoted to setting up the + // various system and + // preconditioner matrices and + // right hand sides that we have to + // deal with in this program. + + // @sect4{TwoPhaseFlowProblem::assemble_darcy_preconditioner} + + // This function assembles the matrix we use + // for preconditioning the Darcy system. What + // we need are a vector mass matrix weighted by + // $\left(\mathbf{K} \lambda_t\right)^{-1}$ + // on the velocity components and a mass + // matrix weighted by $\left(\mathbf{K} + // \lambda_t\right)$ on the pressure + // component. We start by generating a + // quadrature object of appropriate order, + // the FEValues object that can give values + // and gradients at the quadrature points + // (together with quadrature weights). Next + // we create data structures for the cell + // matrix and the relation between local and + // global DoFs. The vectors phi_u and + // grad_phi_p are going to hold the values of + // the basis functions in order to faster + // build up the local matrices, as was + // already done in step-22. Before we start + // the loop over all active cells, we have to + // specify which components are pressure and + // which are velocity. + // + // The creation of the local matrix + // is rather simple. There are only + // a term weighted by + // $\left(\mathbf{K} + // \lambda_t\right)^{-1}$ (on the + // velocity) and a Laplace matrix + // weighted by $\left(\mathbf{K} + // \lambda_t\right)$ to be + // generated, so the creation of + // the local matrix is done in + // essentially two lines. Since the + // material model functions at the + // top of this file only provide + // the inverses of the permeability + // and mobility, we have to compute + // $\mathbf K$ and $\lambda_t$ by + // hand from the given values, once + // per quadrature point. + // + // Once the + // local matrix is ready (loop over + // rows and columns in the local + // matrix on each quadrature + // point), we get the local DoF + // indices and write the local + // information into the global + // matrix. We do this by directly + // applying the constraints + // (i.e. darcy_preconditioner_constraints) + // that takes care of hanging node + // and zero Dirichlet boundary + // condition constraints. By doing + // so, we don't have to do that + // afterwards, and we later don't + // have to use + // ConstraintMatrix::condense and + // MatrixTools::apply_boundary_values, + // both functions that would need + // to modify matrix and vector + // entries and so are difficult to + // write for the Trilinos classes + // where we don't immediately have + // access to individual memory + // locations. template void TwoPhaseFlowProblem::assemble_darcy_preconditioner () @@ -1060,10 +1060,10 @@ namespace Step43 const FEValuesExtractors::Scalar pressure (dim); typename DoFHandler::active_cell_iterator - cell = darcy_dof_handler.begin_active(), - endc = darcy_dof_handler.end(); + cell = darcy_dof_handler.begin_active(), + endc = darcy_dof_handler.end(); typename DoFHandler::active_cell_iterator - saturation_cell = saturation_dof_handler.begin_active(); + saturation_cell = saturation_dof_handler.begin_active(); for (; cell!=endc; ++cell, ++saturation_cell) { @@ -1105,51 +1105,51 @@ namespace Step43 cell->get_dof_indices (local_dof_indices); darcy_preconditioner_constraints.distribute_local_to_global (local_matrix, - local_dof_indices, - darcy_preconditioner_matrix); + local_dof_indices, + darcy_preconditioner_matrix); } } - // @sect4{TwoPhaseFlowProblem::build_darcy_preconditioner} - - // After calling the above - // functions to assemble the - // preconditioner matrix, this - // function generates the inner - // preconditioners that are going - // to be used for the Schur - // complement block - // preconditioner. The - // preconditioners need to be - // regenerated at every saturation - // time step since they depend on - // the saturation $S$ that varies - // with time. - // - // In here, we set up the - // preconditioner for the - // velocity-velocity matrix - // $\mathbf{M}^{\mathbf{u}}$ and - // the Schur complement - // $\mathbf{S}$. As explained in - // the introduction, we are going - // to use an IC preconditioner - // based on the vector matrix - // $\mathbf{M}^{\mathbf{u}}$ and - // another based on the scalar - // Laplace matrix - // $\tilde{\mathbf{S}}^p$ (which is - // spectrally close to the Schur - // complement of the Darcy - // matrix). Usually, the - // TrilinosWrappers::PreconditionIC - // class can be seen as a good - // black-box preconditioner which - // does not need any special - // knowledge of the matrix - // structure and/or the operator - // that's behind it. + // @sect4{TwoPhaseFlowProblem::build_darcy_preconditioner} + + // After calling the above + // functions to assemble the + // preconditioner matrix, this + // function generates the inner + // preconditioners that are going + // to be used for the Schur + // complement block + // preconditioner. The + // preconditioners need to be + // regenerated at every saturation + // time step since they depend on + // the saturation $S$ that varies + // with time. + // + // In here, we set up the + // preconditioner for the + // velocity-velocity matrix + // $\mathbf{M}^{\mathbf{u}}$ and + // the Schur complement + // $\mathbf{S}$. As explained in + // the introduction, we are going + // to use an IC preconditioner + // based on the vector matrix + // $\mathbf{M}^{\mathbf{u}}$ and + // another based on the scalar + // Laplace matrix + // $\tilde{\mathbf{S}}^p$ (which is + // spectrally close to the Schur + // complement of the Darcy + // matrix). Usually, the + // TrilinosWrappers::PreconditionIC + // class can be seen as a good + // black-box preconditioner which + // does not need any special + // knowledge of the matrix + // structure and/or the operator + // that's behind it. template void TwoPhaseFlowProblem::build_darcy_preconditioner () @@ -1167,40 +1167,40 @@ namespace Step43 } - // @sect4{TwoPhaseFlowProblem::assemble_darcy_system} - - // This is the function that assembles the - // linear system for the Darcy system. - // - // Regarding the technical details of - // implementation, the procedures are similar - // to those in step-22 and step-31. We reset - // matrix and vector, create a quadrature - // formula on the cells, and then create the - // respective FEValues object. - // - // There is one thing that needs to be - // commented: since we have a separate - // finite element and DoFHandler for the - // saturation, we need to generate a second - // FEValues object for the proper evaluation - // of the saturation solution. This isn't too - // complicated to realize here: just use the - // saturation structures and set an update - // flag for the basis function values which - // we need for evaluation of the saturation - // solution. The only important part to - // remember here is that the same quadrature - // formula is used for both FEValues objects - // to ensure that we get matching information - // when we loop over the quadrature points of - // the two objects. - // - // The declarations proceed with some - // shortcuts for array sizes, the creation of - // the local matrix, right hand side as well - // as the vector for the indices of the local - // dofs compared to the global system. + // @sect4{TwoPhaseFlowProblem::assemble_darcy_system} + + // This is the function that assembles the + // linear system for the Darcy system. + // + // Regarding the technical details of + // implementation, the procedures are similar + // to those in step-22 and step-31. We reset + // matrix and vector, create a quadrature + // formula on the cells, and then create the + // respective FEValues object. + // + // There is one thing that needs to be + // commented: since we have a separate + // finite element and DoFHandler for the + // saturation, we need to generate a second + // FEValues object for the proper evaluation + // of the saturation solution. This isn't too + // complicated to realize here: just use the + // saturation structures and set an update + // flag for the basis function values which + // we need for evaluation of the saturation + // solution. The only important part to + // remember here is that the same quadrature + // formula is used for both FEValues objects + // to ensure that we get matching information + // when we loop over the quadrature points of + // the two objects. + // + // The declarations proceed with some + // shortcuts for array sizes, the creation of + // the local matrix, right hand side as well + // as the vector for the indices of the local + // dofs compared to the global system. template void TwoPhaseFlowProblem::assemble_darcy_system () { @@ -1238,33 +1238,33 @@ namespace Step43 std::vector boundary_values (n_face_q_points); std::vector > k_inverse_values (n_q_points); - // Next we need a vector that - // will contain the values of the - // saturation solution at the - // previous time level at the - // quadrature points to assemble - // the saturation dependent - // coefficients in the Darcy - // equations. - // - // The set of vectors we create - // next hold the evaluations of - // the basis functions as well as - // their gradients that will be - // used for creating the - // matrices. Putting these into - // their own arrays rather than - // asking the FEValues object for - // this information each time it - // is needed is an optimization - // to accelerate the assembly - // process, see step-22 for - // details. - // - // The last two declarations are used to - // extract the individual blocks (velocity, - // pressure, saturation) from the total FE - // system. + // Next we need a vector that + // will contain the values of the + // saturation solution at the + // previous time level at the + // quadrature points to assemble + // the saturation dependent + // coefficients in the Darcy + // equations. + // + // The set of vectors we create + // next hold the evaluations of + // the basis functions as well as + // their gradients that will be + // used for creating the + // matrices. Putting these into + // their own arrays rather than + // asking the FEValues object for + // this information each time it + // is needed is an optimization + // to accelerate the assembly + // process, see step-22 for + // details. + // + // The last two declarations are used to + // extract the individual blocks (velocity, + // pressure, saturation) from the total FE + // system. std::vector old_saturation_values (n_q_points); std::vector > phi_u (dofs_per_cell); @@ -1274,77 +1274,77 @@ namespace Step43 const FEValuesExtractors::Vector velocities (0); const FEValuesExtractors::Scalar pressure (dim); - // Now start the loop over all - // cells in the problem. We are - // working on two different - // DoFHandlers for this assembly - // routine, so we must have two - // different cell iterators for - // the two objects in use. This - // might seem a bit peculiar, but - // since both the Darcy system - // and the saturation system use - // the same grid we can assume - // that the two iterators run in - // sync over the cells of the two - // DoFHandler objects. - // - // The first statements within - // the loop are again all very - // familiar, doing the update of - // the finite element data as - // specified by the update flags, - // zeroing out the local arrays - // and getting the values of the - // old solution at the quadrature - // points. At this point we also - // have to get the values of the - // saturation function of the - // previous time step at the - // quadrature points. To this - // end, we can use the - // FEValues::get_function_values - // (previously already used in - // step-9, step-14 and step-15), - // a function that takes a - // solution vector and returns a - // list of function values at the - // quadrature points of the - // present cell. In fact, it - // returns the complete - // vector-valued solution at each - // quadrature point, i.e. not - // only the saturation but also - // the velocities and pressure. - // - // Then we are ready to loop over - // the quadrature points on the - // cell to do the - // integration. The formula for - // this follows in a - // straightforward way from what - // has been discussed in the - // introduction. - // - // Once this is done, we start the loop over - // the rows and columns of the local matrix - // and feed the matrix with the relevant - // products. - // - // The last step in the loop over all cells - // is to enter the local contributions into - // the global matrix and vector structures to - // the positions specified in - // local_dof_indices. Again, we let the - // ConstraintMatrix class do the insertion of - // the cell matrix elements to the global - // matrix, which already condenses the - // hanging node constraints. + // Now start the loop over all + // cells in the problem. We are + // working on two different + // DoFHandlers for this assembly + // routine, so we must have two + // different cell iterators for + // the two objects in use. This + // might seem a bit peculiar, but + // since both the Darcy system + // and the saturation system use + // the same grid we can assume + // that the two iterators run in + // sync over the cells of the two + // DoFHandler objects. + // + // The first statements within + // the loop are again all very + // familiar, doing the update of + // the finite element data as + // specified by the update flags, + // zeroing out the local arrays + // and getting the values of the + // old solution at the quadrature + // points. At this point we also + // have to get the values of the + // saturation function of the + // previous time step at the + // quadrature points. To this + // end, we can use the + // FEValues::get_function_values + // (previously already used in + // step-9, step-14 and step-15), + // a function that takes a + // solution vector and returns a + // list of function values at the + // quadrature points of the + // present cell. In fact, it + // returns the complete + // vector-valued solution at each + // quadrature point, i.e. not + // only the saturation but also + // the velocities and pressure. + // + // Then we are ready to loop over + // the quadrature points on the + // cell to do the + // integration. The formula for + // this follows in a + // straightforward way from what + // has been discussed in the + // introduction. + // + // Once this is done, we start the loop over + // the rows and columns of the local matrix + // and feed the matrix with the relevant + // products. + // + // The last step in the loop over all cells + // is to enter the local contributions into + // the global matrix and vector structures to + // the positions specified in + // local_dof_indices. Again, we let the + // ConstraintMatrix class do the insertion of + // the cell matrix elements to the global + // matrix, which already condenses the + // hanging node constraints. typename DoFHandler::active_cell_iterator - cell = darcy_dof_handler.begin_active(), - endc = darcy_dof_handler.end(); + cell = darcy_dof_handler.begin_active(), + endc = darcy_dof_handler.end(); typename DoFHandler::active_cell_iterator - saturation_cell = saturation_dof_handler.begin_active(); + saturation_cell = saturation_dof_handler.begin_active(); for (; cell!=endc; ++cell, ++saturation_cell) { @@ -1394,14 +1394,14 @@ namespace Step43 darcy_fe_face_values.reinit (cell, face_no); pressure_boundary_values - .value_list (darcy_fe_face_values.get_quadrature_points(), - boundary_values); + .value_list (darcy_fe_face_values.get_quadrature_points(), + boundary_values); for (unsigned int q=0; q - phi_i_u = darcy_fe_face_values[velocities].value (i, q); + phi_i_u = darcy_fe_face_values[velocities].value (i, q); local_rhs(i) += -(phi_i_u * darcy_fe_face_values.normal_vector(q) * @@ -1426,20 +1426,20 @@ namespace Step43 } - // @sect4{TwoPhaseFlowProblem::assemble_saturation_system} - - // This function is to assemble the linear - // system for the saturation transport - // equation. It calls, if necessary, two - // other member functions: - // assemble_saturation_matrix() and - // assemble_saturation_rhs(). The former - // function then assembles the saturation - // matrix that only needs to be changed - // occasionally. On the other hand, the - // latter function that assembles the right - // hand side must be called at every - // saturation time step. + // @sect4{TwoPhaseFlowProblem::assemble_saturation_system} + + // This function is to assemble the linear + // system for the saturation transport + // equation. It calls, if necessary, two + // other member functions: + // assemble_saturation_matrix() and + // assemble_saturation_rhs(). The former + // function then assembles the saturation + // matrix that only needs to be changed + // occasionally. On the other hand, the + // latter function that assembles the right + // hand side must be called at every + // saturation time step. template void TwoPhaseFlowProblem::assemble_saturation_system () { @@ -1455,20 +1455,20 @@ namespace Step43 - // @sect4{TwoPhaseFlowProblem::assemble_saturation_matrix} + // @sect4{TwoPhaseFlowProblem::assemble_saturation_matrix} - // This function is easily understood since - // it only forms a simple mass matrix for the - // left hand side of the saturation linear - // system by basis functions phi_i_s and - // phi_j_s only. Finally, as usual, we enter - // the local contribution into the global - // matrix by specifying the position in - // local_dof_indices. This is done by letting - // the ConstraintMatrix class do the - // insertion of the cell matrix elements to - // the global matrix, which already condenses - // the hanging node constraints. + // This function is easily understood since + // it only forms a simple mass matrix for the + // left hand side of the saturation linear + // system by basis functions phi_i_s and + // phi_j_s only. Finally, as usual, we enter + // the local contribution into the global + // matrix by specifying the position in + // local_dof_indices. This is done by letting + // the ConstraintMatrix class do the + // insertion of the cell matrix elements to + // the global matrix, which already condenses + // the hanging node constraints. template void TwoPhaseFlowProblem::assemble_saturation_matrix () { @@ -1487,8 +1487,8 @@ namespace Step43 std::vector local_dof_indices (dofs_per_cell); typename DoFHandler::active_cell_iterator - cell = saturation_dof_handler.begin_active(), - endc = saturation_dof_handler.end(); + cell = saturation_dof_handler.begin_active(), + endc = saturation_dof_handler.end(); for (; cell!=endc; ++cell) { saturation_fe_values.reinit (cell); @@ -1516,42 +1516,42 @@ namespace Step43 - // @sect4{TwoPhaseFlowProblem::assemble_saturation_rhs} - - // This function is to assemble the right - // hand side of the saturation transport - // equation. Before going about it, we have to - // create two FEValues objects for the Darcy - // and saturation systems respectively and, - // in addition, two FEFaceValues objects for - // the two systems because we have a - // boundary integral term in the weak form of - // saturation equation. For the FEFaceValues - // object of the saturation system, we also - // require normal vectors, which we request - // using the update_normal_vectors flag. - // - // Next, before looping over all the cells, - // we have to compute some parameters - // (e.g. global_u_infty, global_S_variation, - // and global_Omega_diameter) that the - // artificial viscosity $\nu$ needs. This is - // largely the same as was done in - // step-31, so you may see there for more - // information. - // - // The real works starts with the loop over all the - // saturation and Darcy cells to put the - // local contributions into the global - // vector. In this loop, in order to simplify - // the implementation, we split some of the - // work into two helper functions: - // assemble_saturation_rhs_cell_term and - // assemble_saturation_rhs_boundary_term. - // We note that we insert cell or boundary - // contributions into the global vector in - // the two functions rather than in this - // present function. + // @sect4{TwoPhaseFlowProblem::assemble_saturation_rhs} + + // This function is to assemble the right + // hand side of the saturation transport + // equation. Before going about it, we have to + // create two FEValues objects for the Darcy + // and saturation systems respectively and, + // in addition, two FEFaceValues objects for + // the two systems because we have a + // boundary integral term in the weak form of + // saturation equation. For the FEFaceValues + // object of the saturation system, we also + // require normal vectors, which we request + // using the update_normal_vectors flag. + // + // Next, before looping over all the cells, + // we have to compute some parameters + // (e.g. global_u_infty, global_S_variation, + // and global_Omega_diameter) that the + // artificial viscosity $\nu$ needs. This is + // largely the same as was done in + // step-31, so you may see there for more + // information. + // + // The real works starts with the loop over all the + // saturation and Darcy cells to put the + // local contributions into the global + // vector. In this loop, in order to simplify + // the implementation, we split some of the + // work into two helper functions: + // assemble_saturation_rhs_cell_term and + // assemble_saturation_rhs_boundary_term. + // We note that we insert cell or boundary + // contributions into the global vector in + // the two functions rather than in this + // present function. template void TwoPhaseFlowProblem::assemble_saturation_rhs () { @@ -1579,10 +1579,10 @@ namespace Step43 const double global_S_variation = global_S_range.second - global_S_range.first; typename DoFHandler::active_cell_iterator - cell = saturation_dof_handler.begin_active(), - endc = saturation_dof_handler.end(); + cell = saturation_dof_handler.begin_active(), + endc = saturation_dof_handler.end(); typename DoFHandler::active_cell_iterator - darcy_cell = darcy_dof_handler.begin_active(); + darcy_cell = darcy_dof_handler.begin_active(); for (; cell!=endc; ++cell, ++darcy_cell) { saturation_fe_values.reinit (cell); @@ -1611,24 +1611,24 @@ namespace Step43 - // @sect4{TwoPhaseFlowProblem::assemble_saturation_rhs_cell_term} - - // This function takes care of integrating - // the cell terms of the right hand side of - // the saturation equation, and then - // assembling it into the global right hand - // side vector. Given the discussion in the - // introduction, the form of these - // contributions is clear. The only tricky - // part is getting the artificial viscosity - // and all that is necessary to compute - // it. The first half of the function is - // devoted to this task. - // - // The last part of the function is copying - // the local contributions into the global - // vector with position specified in - // local_dof_indices. + // @sect4{TwoPhaseFlowProblem::assemble_saturation_rhs_cell_term} + + // This function takes care of integrating + // the cell terms of the right hand side of + // the saturation equation, and then + // assembling it into the global right hand + // side vector. Given the discussion in the + // introduction, the form of these + // contributions is clear. The only tricky + // part is getting the artificial viscosity + // and all that is necessary to compute + // it. The first half of the function is + // devoted to this task. + // + // The last part of the function is copying + // the local contributions into the global + // vector with position specified in + // local_dof_indices. template void TwoPhaseFlowProblem:: @@ -1696,19 +1696,19 @@ namespace Step43 } - // @sect4{TwoPhaseFlowProblem::assemble_saturation_rhs_boundary_term} + // @sect4{TwoPhaseFlowProblem::assemble_saturation_rhs_boundary_term} - // The next function is responsible for the - // boundary integral terms in the right - // hand side form of the saturation - // equation. For these, we have to compute - // the upwinding flux on the global - // boundary faces, i.e. we impose Dirichlet - // boundary conditions weakly only on - // inflow parts of the global boundary. As - // before, this has been described in - // step-21 so we refrain from giving more - // descriptions about that. + // The next function is responsible for the + // boundary integral terms in the right + // hand side form of the saturation + // equation. For these, we have to compute + // the upwinding flux on the global + // boundary faces, i.e. we impose Dirichlet + // boundary conditions weakly only on + // inflow parts of the global boundary. As + // before, this has been described in + // step-21 so we refrain from giving more + // descriptions about that. template void TwoPhaseFlowProblem:: @@ -1723,7 +1723,7 @@ namespace Step43 std::vector old_saturation_solution_values_face(n_face_q_points); std::vector > present_darcy_solution_values_face(n_face_q_points, - Vector(dim+1)); + Vector(dim+1)); std::vector neighbor_saturation (n_face_q_points); saturation_fe_face_values.get_function_values (old_saturation_solution, @@ -1733,8 +1733,8 @@ namespace Step43 SaturationBoundaryValues saturation_boundary_values; saturation_boundary_values - .value_list (saturation_fe_face_values.get_quadrature_points(), - neighbor_saturation); + .value_list (saturation_fe_face_values.get_quadrature_points(), + neighbor_saturation); for (unsigned int q=0; q::solve} - - // This function implements the operator - // splitting algorithm, i.e. in each time - // step it either re-computes the solution - // of the Darcy system or extrapolates - // velocity/pressure from previous time - // steps, then determines the size of the - // time step, and then updates the - // saturation variable. The implementation - // largely follows similar code in - // step-31. It is, next to the run() - // function, the central one in this - // program. - // - // At the beginning of the function, we ask - // whether to solve the pressure-velocity - // part by evaluating the posteriori - // criterion (see the following - // function). If necessary, we will solve - // the pressure-velocity part using the - // GMRES solver with the Schur complement - // block preconditioner as is described in - // the introduction. + // @sect3{TwoPhaseFlowProblem::solve} + + // This function implements the operator + // splitting algorithm, i.e. in each time + // step it either re-computes the solution + // of the Darcy system or extrapolates + // velocity/pressure from previous time + // steps, then determines the size of the + // time step, and then updates the + // saturation variable. The implementation + // largely follows similar code in + // step-31. It is, next to the run() + // function, the central one in this + // program. + // + // At the beginning of the function, we ask + // whether to solve the pressure-velocity + // part by evaluating the posteriori + // criterion (see the following + // function). If necessary, we will solve + // the pressure-velocity part using the + // GMRES solver with the Schur complement + // block preconditioner as is described in + // the introduction. template void TwoPhaseFlowProblem::solve () { const bool - solve_for_pressure_and_velocity = determine_whether_to_solve_for_pressure_and_velocity (); + solve_for_pressure_and_velocity = determine_whether_to_solve_for_pressure_and_velocity (); if (solve_for_pressure_and_velocity == true) { @@ -1804,19 +1804,19 @@ namespace Step43 { const LinearSolvers::InverseMatrix - mp_inverse (darcy_preconditioner_matrix.block(1,1), *Mp_preconditioner); + TrilinosWrappers::PreconditionIC> + mp_inverse (darcy_preconditioner_matrix.block(1,1), *Mp_preconditioner); const LinearSolvers::BlockSchurPreconditioner - preconditioner (darcy_matrix, mp_inverse, *Amg_preconditioner); + TrilinosWrappers::PreconditionIC> + preconditioner (darcy_matrix, mp_inverse, *Amg_preconditioner); SolverControl solver_control (darcy_matrix.m(), 1e-16*darcy_rhs.l2_norm()); SolverGMRES - gmres (solver_control, - SolverGMRES::AdditionalData(100)); + gmres (solver_control, + SolverGMRES::AdditionalData(100)); for (unsigned int i=0; icurrent_macro_time_step), - // and $DT$ the length of the last macro - // time step (given by - // old_macro_time_step), - // then we get - // $u^\ast = u_p + dt \frac{u_p-u_{pp}}{DT} - // = (1+dt/DT)u_p - dt/DT u_{pp}$, where - // $u_p$ and $u_{pp}$ are the last two - // computed Darcy solutions. We can - // implement this formula using just - // two lines of code. - // - // Note that the algorithm here only - // works if we have at least two - // previously computed Darcy solutions - // from which we can extrapolate to the - // current time, and this is ensured by - // requiring re-computation of the Darcy - // solution for the first 2 time steps. + // On the other hand, if we have decided + // that we don't want to compute the + // solution of the Darcy system for the + // current time step, then we need to + // simply extrapolate the previous two + // Darcy solutions to the same time as we + // would have computed the + // velocity/pressure at. We do a simple + // linear extrapolation, i.e. given the + // current length $dt$ of the macro time + // step from the time when we last + // computed the Darcy solution to now + // (given by + // current_macro_time_step), + // and $DT$ the length of the last macro + // time step (given by + // old_macro_time_step), + // then we get + // $u^\ast = u_p + dt \frac{u_p-u_{pp}}{DT} + // = (1+dt/DT)u_p - dt/DT u_{pp}$, where + // $u_p$ and $u_{pp}$ are the last two + // computed Darcy solutions. We can + // implement this formula using just + // two lines of code. + // + // Note that the algorithm here only + // works if we have at least two + // previously computed Darcy solutions + // from which we can extrapolate to the + // current time, and this is ensured by + // requiring re-computation of the Darcy + // solution for the first 2 time steps. else { darcy_solution = last_computed_darcy_solution; @@ -1880,11 +1880,11 @@ namespace Step43 } - // With the so computed velocity - // vector, compute the optimal - // time step based on the CFL - // criterion discussed in the - // introduction... + // With the so computed velocity + // vector, compute the optimal + // time step based on the CFL + // criterion discussed in the + // introduction... { old_time_step = time_step; @@ -1900,24 +1900,24 @@ namespace Step43 - // ...and then also update the - // length of the macro time steps - // we use while we're dealing - // with time step sizes. In - // particular, this involves: (i) - // If we have just recomputed the - // Darcy solution, then the - // length of the previous macro - // time step is now fixed and the - // length of the current macro - // time step is, up to now, - // simply the length of the - // current (micro) time - // step. (ii) If we have not - // recomputed the Darcy solution, - // then the length of the current - // macro time step has just grown - // by time_step. + // ...and then also update the + // length of the macro time steps + // we use while we're dealing + // with time step sizes. In + // particular, this involves: (i) + // If we have just recomputed the + // Darcy solution, then the + // length of the previous macro + // time step is now fixed and the + // length of the current macro + // time step is, up to now, + // simply the length of the + // current (micro) time + // step. (ii) If we have not + // recomputed the Darcy solution, + // then the length of the current + // macro time step has just grown + // by time_step. if (solve_for_pressure_and_velocity == true) { old_macro_time_step = current_macro_time_step; @@ -1926,18 +1926,18 @@ namespace Step43 else current_macro_time_step += time_step; - // The last step in this function - // is to recompute the saturation - // solution based on the velocity - // field we've just - // obtained. This naturally - // happens in every time step, - // and we don't skip any of these - // computations. At the end of - // computing the saturation, we - // project back into the allowed - // interval $[0,1]$ to make sure - // our solution remains physical. + // The last step in this function + // is to recompute the saturation + // solution based on the velocity + // field we've just + // obtained. This naturally + // happens in every time step, + // and we don't skip any of these + // computations. At the end of + // computing the saturation, we + // project back into the allowed + // interval $[0,1]$ to make sure + // our solution remains physical. { std::cout << " Solving saturation transport equation..." << std::endl; @@ -1964,29 +1964,29 @@ namespace Step43 } - // @sect3{TwoPhaseFlowProblem::refine_mesh} - - // The next function does the - // refinement and coarsening of the - // mesh. It does its work in three - // blocks: (i) Compute refinement - // indicators by looking at the - // gradient of a solution vector - // extrapolated linearly from the - // previous two using the - // respective sizes of the time - // step (or taking the only - // solution we have if this is the - // first time step). (ii) Flagging - // those cells for refinement and - // coarsening where the gradient is - // larger or smaller than a certain - // threshold, preserving minimal - // and maximal levels of mesh - // refinement. (iii) Transferring - // the solution from the old to the - // new mesh. None of this is - // particularly difficult. + // @sect3{TwoPhaseFlowProblem::refine_mesh} + + // The next function does the + // refinement and coarsening of the + // mesh. It does its work in three + // blocks: (i) Compute refinement + // indicators by looking at the + // gradient of a solution vector + // extrapolated linearly from the + // previous two using the + // respective sizes of the time + // step (or taking the only + // solution we have if this is the + // first time step). (ii) Flagging + // those cells for refinement and + // coarsening where the gradient is + // larger or smaller than a certain + // threshold, preserving minimal + // and maximal levels of mesh + // refinement. (iii) Transferring + // the solution from the old to the + // new mesh. None of this is + // particularly difficult. template void TwoPhaseFlowProblem:: @@ -2005,8 +2005,8 @@ namespace Step43 time_step/old_time_step, old_saturation_solution); typename DoFHandler::active_cell_iterator - cell = saturation_dof_handler.begin_active(), - endc = saturation_dof_handler.end(); + cell = saturation_dof_handler.begin_active(), + endc = saturation_dof_handler.end(); for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no) { fe_values.reinit(cell); @@ -2019,8 +2019,8 @@ namespace Step43 { typename DoFHandler::active_cell_iterator - cell = saturation_dof_handler.begin_active(), - endc = saturation_dof_handler.end(); + cell = saturation_dof_handler.begin_active(), + endc = saturation_dof_handler.end(); for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no) { @@ -2030,10 +2030,9 @@ namespace Step43 if ((static_cast(cell->level()) < max_grid_level) && (std::fabs(refinement_indicators(cell_no)) > saturation_refinement_threshold)) cell->set_refine_flag(); - else - if ((static_cast(cell->level()) > min_grid_level) && - (std::fabs(refinement_indicators(cell_no)) < 0.5 * saturation_refinement_threshold)) - cell->set_coarsen_flag(); + else if ((static_cast(cell->level()) > min_grid_level) && + (std::fabs(refinement_indicators(cell_no)) < 0.5 * saturation_refinement_threshold)) + cell->set_coarsen_flag(); } } @@ -2086,12 +2085,12 @@ namespace Step43 - // @sect3{TwoPhaseFlowProblem::output_results} + // @sect3{TwoPhaseFlowProblem::output_results} - // This function generates - // graphical output. It is in - // essence a copy of the - // implementation in step-31. + // This function generates + // graphical output. It is in + // essence a copy of the + // implementation in step-31. template void TwoPhaseFlowProblem::output_results () const { @@ -2111,10 +2110,10 @@ namespace Step43 std::vector local_saturation_dof_indices (saturation_fe.dofs_per_cell); typename DoFHandler::active_cell_iterator - joint_cell = joint_dof_handler.begin_active(), - joint_endc = joint_dof_handler.end(), - darcy_cell = darcy_dof_handler.begin_active(), - saturation_cell = saturation_dof_handler.begin_active(); + joint_cell = joint_dof_handler.begin_active(), + joint_endc = joint_dof_handler.end(), + darcy_cell = darcy_dof_handler.begin_active(), + saturation_cell = saturation_dof_handler.begin_active(); for (; joint_cell!=joint_endc; ++joint_cell, ++darcy_cell, ++saturation_cell) { @@ -2151,12 +2150,12 @@ namespace Step43 joint_solution_names.push_back ("saturation"); std::vector - data_component_interpretation - (dim, DataComponentInterpretation::component_is_part_of_vector); data_component_interpretation - .push_back (DataComponentInterpretation::component_is_scalar); + (dim, DataComponentInterpretation::component_is_part_of_vector); + data_component_interpretation + .push_back (DataComponentInterpretation::component_is_scalar); data_component_interpretation - .push_back (DataComponentInterpretation::component_is_scalar); + .push_back (DataComponentInterpretation::component_is_scalar); DataOut data_out; @@ -2175,41 +2174,41 @@ namespace Step43 - // @sect3{Tool functions} - - // @sect4{TwoPhaseFlowProblem::determine_whether_to_solve_for_pressure_and_velocity} - - // This function implements the a - // posteriori criterion for - // adaptive operator splitting. The - // function is relatively - // straightforward given the way we - // have implemented other functions - // above and given the formula for - // the criterion derived in the - // paper. - // - // If one decides that one wants - // the original IMPES method in - // which the Darcy equation is - // solved in every time step, then - // this can be achieved by setting - // the threshold value - // AOS_threshold (with - // a default of $5.0$) to zero, - // thereby forcing the function to - // always return true. - // - // Finally, note that the function - // returns true unconditionally for - // the first two time steps to - // ensure that we have always - // solved the Darcy system at least - // twice when skipping its - // solution, thereby allowing us to - // extrapolate the velocity from - // the last two solutions in - // solve(). + // @sect3{Tool functions} + + // @sect4{TwoPhaseFlowProblem::determine_whether_to_solve_for_pressure_and_velocity} + + // This function implements the a + // posteriori criterion for + // adaptive operator splitting. The + // function is relatively + // straightforward given the way we + // have implemented other functions + // above and given the formula for + // the criterion derived in the + // paper. + // + // If one decides that one wants + // the original IMPES method in + // which the Darcy equation is + // solved in every time step, then + // this can be achieved by setting + // the threshold value + // AOS_threshold (with + // a default of $5.0$) to zero, + // thereby forcing the function to + // always return true. + // + // Finally, note that the function + // returns true unconditionally for + // the first two time steps to + // ensure that we have always + // solved the Darcy system at least + // twice when skipping its + // solution, thereby allowing us to + // extrapolate the velocity from + // the last two solutions in + // solve(). template bool TwoPhaseFlowProblem::determine_whether_to_solve_for_pressure_and_velocity () const @@ -2231,8 +2230,8 @@ namespace Step43 double max_global_aop_indicator = 0.0; typename DoFHandler::active_cell_iterator - cell = saturation_dof_handler.begin_active(), - endc = saturation_dof_handler.end(); + cell = saturation_dof_handler.begin_active(), + endc = saturation_dof_handler.end(); for (; cell!=endc; ++cell) { double max_local_mobility_reciprocal_difference = 0.0; @@ -2271,30 +2270,30 @@ namespace Step43 - // @sect4{TwoPhaseFlowProblem::project_back_saturation} - - // The next function simply makes - // sure that the saturation values - // always remain within the - // physically reasonable range of - // $[0,1]$. While the continuous - // equations guarantee that this is - // so, the discrete equations - // don't. However, if we allow the - // discrete solution to escape this - // range we get into trouble - // because terms like $F(S)$ and - // $F'(S)$ will produce - // unreasonable results - // (e.g. $F'(S)<0$ for $S<0$, which - // would imply that the wetting - // fluid phase flows against - // the direction of the bulk fluid - // velocity)). Consequently, at the - // end of each time step, we simply - // project the saturation field - // back into the physically - // reasonable region. + // @sect4{TwoPhaseFlowProblem::project_back_saturation} + + // The next function simply makes + // sure that the saturation values + // always remain within the + // physically reasonable range of + // $[0,1]$. While the continuous + // equations guarantee that this is + // so, the discrete equations + // don't. However, if we allow the + // discrete solution to escape this + // range we get into trouble + // because terms like $F(S)$ and + // $F'(S)$ will produce + // unreasonable results + // (e.g. $F'(S)<0$ for $S<0$, which + // would imply that the wetting + // fluid phase flows against + // the direction of the bulk fluid + // velocity)). Consequently, at the + // end of each time step, we simply + // project the saturation field + // back into the physically + // reasonable region. template void TwoPhaseFlowProblem::project_back_saturation () @@ -2302,26 +2301,25 @@ namespace Step43 for (unsigned int i=0; i 1) - saturation_solution(i) = 1; + else if (saturation_solution(i) > 1) + saturation_solution(i) = 1; } - // @sect4{TwoPhaseFlowProblem::get_max_u_F_prime} - // - // Another simpler helper function: - // Compute the maximum of the total - // velocity times the derivative of - // the fraction flow function, - // i.e., compute $\|\mathbf{u} - // F'(S)\|_{L_\infty(\Omega)}$. This - // term is used in both the - // computation of the time step as - // well as in normalizing the - // entropy-residual term in the - // artificial viscosity. + // @sect4{TwoPhaseFlowProblem::get_max_u_F_prime} + // + // Another simpler helper function: + // Compute the maximum of the total + // velocity times the derivative of + // the fraction flow function, + // i.e., compute $\|\mathbf{u} + // F'(S)\|_{L_\infty(\Omega)}$. This + // term is used in both the + // computation of the time step as + // well as in normalizing the + // entropy-residual term in the + // artificial viscosity. template double TwoPhaseFlowProblem::get_max_u_F_prime () const @@ -2341,10 +2339,10 @@ namespace Step43 double max_velocity_times_dF_dS = 0; typename DoFHandler::active_cell_iterator - cell = darcy_dof_handler.begin_active(), - endc = darcy_dof_handler.end(); + cell = darcy_dof_handler.begin_active(), + endc = darcy_dof_handler.end(); typename DoFHandler::active_cell_iterator - saturation_cell = saturation_dof_handler.begin_active(); + saturation_cell = saturation_dof_handler.begin_active(); for (; cell!=endc; ++cell, ++saturation_cell) { darcy_fe_values.reinit (cell); @@ -2370,26 +2368,26 @@ namespace Step43 } - // @sect4{TwoPhaseFlowProblem::get_extrapolated_saturation_range} - // - // For computing the stabilization - // term, we need to know the range - // of the saturation - // variable. Unlike in step-31, - // this range is trivially bounded - // by the interval $[0,1]$ but we - // can do a bit better by looping - // over a collection of quadrature - // points and seeing what the - // values are there. If we can, - // i.e., if there are at least two - // timesteps around, we can even - // take the values extrapolated to - // the next time step. - // - // As before, the function is taken - // with minimal modifications from - // step-31. + // @sect4{TwoPhaseFlowProblem::get_extrapolated_saturation_range} + // + // For computing the stabilization + // term, we need to know the range + // of the saturation + // variable. Unlike in step-31, + // this range is trivially bounded + // by the interval $[0,1]$ but we + // can do a bit better by looping + // over a collection of quadrature + // points and seeing what the + // values are there. If we can, + // i.e., if there are at least two + // timesteps around, we can even + // take the values extrapolated to + // the next time step. + // + // As before, the function is taken + // with minimal modifications from + // step-31. template std::pair TwoPhaseFlowProblem::get_extrapolated_saturation_range () const @@ -2408,8 +2406,8 @@ namespace Step43 max_saturation = -std::numeric_limits::max(); typename DoFHandler::active_cell_iterator - cell = saturation_dof_handler.begin_active(), - endc = saturation_dof_handler.end(); + cell = saturation_dof_handler.begin_active(), + endc = saturation_dof_handler.end(); for (; cell!=endc; ++cell) { fe_values.reinit (cell); @@ -2437,8 +2435,8 @@ namespace Step43 max_saturation = -std::numeric_limits::max(); typename DoFHandler::active_cell_iterator - cell = saturation_dof_handler.begin_active(), - endc = saturation_dof_handler.end(); + cell = saturation_dof_handler.begin_active(), + endc = saturation_dof_handler.end(); for (; cell!=endc; ++cell) { fe_values.reinit (cell); @@ -2460,21 +2458,21 @@ namespace Step43 - // @sect4{TwoPhaseFlowProblem::compute_viscosity} - // - // The final tool function is used - // to compute the artificial - // viscosity on a given cell. This - // isn't particularly complicated - // if you have the formula for it - // in front of you, and looking at - // the implementation in - // step-31. The major difference to - // that tutorial program is that - // the velocity here is not simply - // $\mathbf u$ but $\mathbf u - // F'(S)$ and some of the formulas - // need to be adjusted accordingly. + // @sect4{TwoPhaseFlowProblem::compute_viscosity} + // + // The final tool function is used + // to compute the artificial + // viscosity on a given cell. This + // isn't particularly complicated + // if you have the formula for it + // in front of you, and looking at + // the implementation in + // step-31. The major difference to + // that tutorial program is that + // the velocity here is not simply + // $\mathbf u$ but $\mathbf u + // F'(S)$ and some of the formulas + // need to be adjusted accordingly. template double TwoPhaseFlowProblem:: @@ -2545,27 +2543,27 @@ namespace Step43 } - // @sect3{TwoPhaseFlowProblem::run} - - // This function is, besides - // solve(), the - // primary function of this program - // as it controls the time - // iteration as well as when the - // solution is written into output - // files and when to do mesh - // refinement. - // - // With the exception of the - // startup code that loops back to - // the beginning of the function - // through the goto - // start_time_iteration - // label, everything should be - // relatively straightforward. In - // any case, it mimicks the - // corresponding function in - // step-31. + // @sect3{TwoPhaseFlowProblem::run} + + // This function is, besides + // solve(), the + // primary function of this program + // as it controls the time + // iteration as well as when the + // solution is written into output + // files and when to do mesh + // refinement. + // + // With the exception of the + // startup code that loops back to + // the beginning of the function + // through the goto + // start_time_iteration + // label, everything should be + // relatively straightforward. In + // any case, it mimicks the + // corresponding function in + // step-31. template void TwoPhaseFlowProblem::run () { @@ -2581,7 +2579,7 @@ namespace Step43 unsigned int pre_refinement_step = 0; - start_time_iteration: +start_time_iteration: VectorTools::project (saturation_dof_handler, saturation_constraints, @@ -2632,14 +2630,14 @@ namespace Step43 - // @sect3{The main() function} - // - // The main function looks almost the - // same as in all other programs. In - // particular, it is essentially the - // same as in step-31 where we also - // explain the need to initialize the - // MPI subsystem. +// @sect3{The main() function} +// +// The main function looks almost the +// same as in all other programs. In +// particular, it is essentially the +// same as in step-31 where we also +// explain the need to initialize the +// MPI subsystem. int main (int argc, char *argv[]) { try diff --git a/deal.II/examples/step-44/step-44.cc b/deal.II/examples/step-44/step-44.cc index 43fc6e54d2..1f06021f57 100644 --- a/deal.II/examples/step-44/step-44.cc +++ b/deal.II/examples/step-44/step-44.cc @@ -9,11 +9,11 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // We start by including all the necessary - // deal.II header files and some C++ related - // ones. They have been discussed in detail - // in previous tutorial programs, so you need - // only refer to past tutorials for details. +// We start by including all the necessary +// deal.II header files and some C++ related +// ones. They have been discussed in detail +// in previous tutorial programs, so you need +// only refer to past tutorials for details. #include #include #include @@ -55,10 +55,10 @@ #include - // We then stick everything that relates to - // this tutorial program into a namespace of - // its own, and import all the deal.II - // function and class names into it: +// We then stick everything that relates to +// this tutorial program into a namespace of +// its own, and import all the deal.II +// function and class names into it: namespace Step44 { using namespace dealii; @@ -85,14 +85,14 @@ namespace Step44 // The quadrature order should be adjusted accordingly. struct FESystem { - unsigned int poly_degree; - unsigned int quad_order; + unsigned int poly_degree; + unsigned int quad_order; - static void - declare_parameters(ParameterHandler &prm); + static void + declare_parameters(ParameterHandler &prm); - void - parse_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); }; @@ -128,15 +128,15 @@ namespace Step44 // results given in the literature. struct Geometry { - unsigned int global_refinement; - double scale; - double p_p0; + unsigned int global_refinement; + double scale; + double p_p0; - static void - declare_parameters(ParameterHandler &prm); + static void + declare_parameters(ParameterHandler &prm); - void - parse_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); }; void Geometry::declare_parameters(ParameterHandler &prm) @@ -175,14 +175,14 @@ namespace Step44 // for the neo-Hookean material. struct Materials { - double nu; - double mu; + double nu; + double mu; - static void - declare_parameters(ParameterHandler &prm); + static void + declare_parameters(ParameterHandler &prm); - void - parse_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); }; void Materials::declare_parameters(ParameterHandler &prm) @@ -217,17 +217,17 @@ namespace Step44 // within a Newton increment. struct LinearSolver { - std::string type_lin; - double tol_lin; - double max_iterations_lin; - std::string preconditioner_type; - double preconditioner_relaxation; + std::string type_lin; + double tol_lin; + double max_iterations_lin; + std::string preconditioner_type; + double preconditioner_relaxation; - static void - declare_parameters(ParameterHandler &prm); + static void + declare_parameters(ParameterHandler &prm); - void - parse_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); }; void LinearSolver::declare_parameters(ParameterHandler &prm) @@ -277,15 +277,15 @@ namespace Step44 // iterations for the Newton-Raphson nonlinear solver. struct NonlinearSolver { - unsigned int max_iterations_NR; - double tol_f; - double tol_u; + unsigned int max_iterations_NR; + double tol_f; + double tol_u; - static void - declare_parameters(ParameterHandler &prm); + static void + declare_parameters(ParameterHandler &prm); - void - parse_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); }; void NonlinearSolver::declare_parameters(ParameterHandler &prm) @@ -323,14 +323,14 @@ namespace Step44 // and the simulation end-time. struct Time { - double delta_t; - double end_time; + double delta_t; + double end_time; - static void - declare_parameters(ParameterHandler &prm); + static void + declare_parameters(ParameterHandler &prm); - void - parse_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); }; void Time::declare_parameters(ParameterHandler &prm) @@ -362,23 +362,23 @@ namespace Step44 // Finally we consolidate all of the above structures into // a single container that holds all of our run-time selections. struct AllParameters : public FESystem, - public Geometry, - public Materials, - public LinearSolver, - public NonlinearSolver, - public Time + public Geometry, + public Materials, + public LinearSolver, + public NonlinearSolver, + public Time { - AllParameters(const std::string & input_file); + AllParameters(const std::string &input_file); - static void - declare_parameters(ParameterHandler &prm); + static void + declare_parameters(ParameterHandler &prm); - void - parse_parameters(ParameterHandler &prm); + void + parse_parameters(ParameterHandler &prm); }; - AllParameters::AllParameters(const std::string & input_file) + AllParameters::AllParameters(const std::string &input_file) { ParameterHandler prm; declare_parameters(prm); @@ -413,21 +413,21 @@ namespace Step44 template class StandardTensors { - public: - - // $\mathbf{I}$ - static const SymmetricTensor<2, dim> I; - // $\mathbf{I} \otimes \mathbf{I}$ - static const SymmetricTensor<4, dim> IxI; - // $\mathcal{S}$, note that as we only use - // this fourth-order unit tensor to operate - // on symmetric second-order tensors. - // To maintain notation consistent with Holzapfel (2001) - // we name the tensor $\mathcal{I}$ - static const SymmetricTensor<4, dim> II; - // Fourth-order deviatoric tensor such that - // $\textrm{dev} \{ \bullet \} = \{ \bullet \} - [1/\textrm{dim}][ \{ \bullet\} :\mathbf{I}]\mathbf{I}$ - static const SymmetricTensor<4, dim> dev_P; + public: + + // $\mathbf{I}$ + static const SymmetricTensor<2, dim> I; + // $\mathbf{I} \otimes \mathbf{I}$ + static const SymmetricTensor<4, dim> IxI; + // $\mathcal{S}$, note that as we only use + // this fourth-order unit tensor to operate + // on symmetric second-order tensors. + // To maintain notation consistent with Holzapfel (2001) + // we name the tensor $\mathcal{I}$ + static const SymmetricTensor<4, dim> II; + // Fourth-order deviatoric tensor such that + // $\textrm{dev} \{ \bullet \} = \{ \bullet \} - [1/\textrm{dim}][ \{ \bullet\} :\mathbf{I}]\mathbf{I}$ + static const SymmetricTensor<4, dim> dev_P; }; template @@ -453,46 +453,46 @@ namespace Step44 // time step size. class Time { - public: - Time (const double time_end, - const double delta_t) - : - timestep(0), - time_current(0.0), - time_end(time_end), - delta_t(delta_t) - {} - - virtual ~Time() - {} - - double current() const - { - return time_current; - } - double end() const - { - return time_end; - } - double get_delta_t() const - { - return delta_t; - } - unsigned int get_timestep() const - { - return timestep; - } - void increment() - { - time_current += delta_t; - ++timestep; - } + public: + Time (const double time_end, + const double delta_t) + : + timestep(0), + time_current(0.0), + time_end(time_end), + delta_t(delta_t) + {} + + virtual ~Time() + {} + + double current() const + { + return time_current; + } + double end() const + { + return time_end; + } + double get_delta_t() const + { + return delta_t; + } + unsigned int get_timestep() const + { + return timestep; + } + void increment() + { + time_current += delta_t; + ++timestep; + } - private: - unsigned int timestep; - double time_current; - const double time_end; - const double delta_t; + private: + unsigned int timestep; + double time_current; + const double time_end; + const double delta_t; }; // @sect3{Compressible neo-Hookean material within a three-field formulation} @@ -528,193 +528,193 @@ namespace Step44 template class Material_Compressible_Neo_Hook_Three_Field { - public: - Material_Compressible_Neo_Hook_Three_Field(const double mu, - const double nu) - : - kappa((2.0 * mu * (1.0 + nu)) / (3.0 * (1.0 - 2.0 * nu))), - c_1(mu / 2.0), - det_F(1.0), - p_tilde(0.0), - J_tilde(1.0), - b_bar(StandardTensors::I) - { - Assert(kappa > 0, ExcInternalError()); - } + public: + Material_Compressible_Neo_Hook_Three_Field(const double mu, + const double nu) + : + kappa((2.0 * mu * (1.0 + nu)) / (3.0 * (1.0 - 2.0 * nu))), + c_1(mu / 2.0), + det_F(1.0), + p_tilde(0.0), + J_tilde(1.0), + b_bar(StandardTensors::I) + { + Assert(kappa > 0, ExcInternalError()); + } - ~Material_Compressible_Neo_Hook_Three_Field() - {} - - // We update the material model with - // various deformation dependent data - // based on $F$ and the pressure $\widetilde{p}$ - // and dilatation $\widetilde{J}$, - // and at the end of the - // function include a physical check for - // internal consistency: - void update_material_data(const Tensor<2, dim> & F, - const double p_tilde_in, - const double J_tilde_in) - { - det_F = determinant(F); - b_bar = std::pow(det_F, -2.0 / 3.0) * symmetrize(F * transpose(F)); - p_tilde = p_tilde_in; - J_tilde = J_tilde_in; + ~Material_Compressible_Neo_Hook_Three_Field() + {} + + // We update the material model with + // various deformation dependent data + // based on $F$ and the pressure $\widetilde{p}$ + // and dilatation $\widetilde{J}$, + // and at the end of the + // function include a physical check for + // internal consistency: + void update_material_data(const Tensor<2, dim> &F, + const double p_tilde_in, + const double J_tilde_in) + { + det_F = determinant(F); + b_bar = std::pow(det_F, -2.0 / 3.0) * symmetrize(F * transpose(F)); + p_tilde = p_tilde_in; + J_tilde = J_tilde_in; - Assert(det_F > 0, ExcInternalError()); - } + Assert(det_F > 0, ExcInternalError()); + } - // The second function determines the - // Kirchhoff stress $\boldsymbol{\tau} - // = \boldsymbol{\tau}_{\textrm{iso}} + - // \boldsymbol{\tau}_{\textrm{vol}}$ - SymmetricTensor<2, dim> get_tau() - { - return get_tau_iso() + get_tau_vol(); - } + // The second function determines the + // Kirchhoff stress $\boldsymbol{\tau} + // = \boldsymbol{\tau}_{\textrm{iso}} + + // \boldsymbol{\tau}_{\textrm{vol}}$ + SymmetricTensor<2, dim> get_tau() + { + return get_tau_iso() + get_tau_vol(); + } - // The fourth-order elasticity tensor - // in the spatial setting - // $\mathfrak{c}$ is calculated from - // the SEF $\Psi$ as $ J - // \mathfrak{c}_{ijkl} = F_{iA} F_{jB} - // \mathfrak{C}_{ABCD} F_{kC} F_{lD}$ - // where $ \mathfrak{C} = 4 - // \frac{\partial^2 - // \Psi(\mathbf{C})}{\partial - // \mathbf{C} \partial \mathbf{C}}$ - SymmetricTensor<4, dim> get_Jc() const - { - return get_Jc_vol() + get_Jc_iso(); - } + // The fourth-order elasticity tensor + // in the spatial setting + // $\mathfrak{c}$ is calculated from + // the SEF $\Psi$ as $ J + // \mathfrak{c}_{ijkl} = F_{iA} F_{jB} + // \mathfrak{C}_{ABCD} F_{kC} F_{lD}$ + // where $ \mathfrak{C} = 4 + // \frac{\partial^2 + // \Psi(\mathbf{C})}{\partial + // \mathbf{C} \partial \mathbf{C}}$ + SymmetricTensor<4, dim> get_Jc() const + { + return get_Jc_vol() + get_Jc_iso(); + } - // Derivative of the volumetric free - // energy with respect to $\widetilde{J}$ return - // $\frac{\partial - // \Psi_{\text{vol}}(\widetilde{J})}{\partial - // \widetilde{J}}$ - double get_dPsi_vol_dJ() const - { - return (kappa / 2.0) * (J_tilde - 1.0 / J_tilde); - } + // Derivative of the volumetric free + // energy with respect to $\widetilde{J}$ return + // $\frac{\partial + // \Psi_{\text{vol}}(\widetilde{J})}{\partial + // \widetilde{J}}$ + double get_dPsi_vol_dJ() const + { + return (kappa / 2.0) * (J_tilde - 1.0 / J_tilde); + } - // Second derivative of the volumetric - // free energy wrt $\widetilde{J}$. We - // need the following computation - // explicitly in the tangent so we make - // it public. We calculate - // $\frac{\partial^2 - // \Psi_{\textrm{vol}}(\widetilde{J})}{\partial - // \widetilde{J} \partial - // \widetilde{J}}$ - double get_d2Psi_vol_dJ2() const - { - return ( (kappa / 2.0) * (1.0 + 1.0 / (J_tilde * J_tilde))); - } + // Second derivative of the volumetric + // free energy wrt $\widetilde{J}$. We + // need the following computation + // explicitly in the tangent so we make + // it public. We calculate + // $\frac{\partial^2 + // \Psi_{\textrm{vol}}(\widetilde{J})}{\partial + // \widetilde{J} \partial + // \widetilde{J}}$ + double get_d2Psi_vol_dJ2() const + { + return ( (kappa / 2.0) * (1.0 + 1.0 / (J_tilde * J_tilde))); + } - // The next few functions return - // various data that we choose to store - // with the material: - double get_det_F() const - { - return det_F; - } + // The next few functions return + // various data that we choose to store + // with the material: + double get_det_F() const + { + return det_F; + } - double get_p_tilde() const - { - return p_tilde; - } + double get_p_tilde() const + { + return p_tilde; + } - double get_J_tilde() const - { - return J_tilde; - } + double get_J_tilde() const + { + return J_tilde; + } - protected: - // Define constitutive model paramaters - // $\kappa$ (bulk modulus) - // and the neo-Hookean model - // parameter $c_1$: - const double kappa; - const double c_1; - - // Model specific data that is - // convenient to store with the - // material: - double det_F; - double p_tilde; - double J_tilde; - SymmetricTensor<2, dim> b_bar; - - // The following functions are used - // internally in determining the result - // of some of the public functions - // above. The first one determines the - // volumetric Kirchhoff stress - // $\boldsymbol{\tau}_{\textrm{vol}}$: - SymmetricTensor<2, dim> get_tau_vol() const - { - return p_tilde * det_F * StandardTensors::I; - } + protected: + // Define constitutive model paramaters + // $\kappa$ (bulk modulus) + // and the neo-Hookean model + // parameter $c_1$: + const double kappa; + const double c_1; + + // Model specific data that is + // convenient to store with the + // material: + double det_F; + double p_tilde; + double J_tilde; + SymmetricTensor<2, dim> b_bar; + + // The following functions are used + // internally in determining the result + // of some of the public functions + // above. The first one determines the + // volumetric Kirchhoff stress + // $\boldsymbol{\tau}_{\textrm{vol}}$: + SymmetricTensor<2, dim> get_tau_vol() const + { + return p_tilde * det_F * StandardTensors::I; + } - // Next, determine the isochoric - // Kirchhoff stress - // $\boldsymbol{\tau}_{\textrm{iso}} = - // \mathcal{P}:\overline{\boldsymbol{\tau}}$: - SymmetricTensor<2, dim> get_tau_iso() const - { - return StandardTensors::dev_P * get_tau_bar(); - } + // Next, determine the isochoric + // Kirchhoff stress + // $\boldsymbol{\tau}_{\textrm{iso}} = + // \mathcal{P}:\overline{\boldsymbol{\tau}}$: + SymmetricTensor<2, dim> get_tau_iso() const + { + return StandardTensors::dev_P * get_tau_bar(); + } - // Then, determine the fictitious - // Kirchhoff stress - // $\overline{\boldsymbol{\tau}}$: - SymmetricTensor<2, dim> get_tau_bar() const - { - return 2.0 * c_1 * b_bar; - } + // Then, determine the fictitious + // Kirchhoff stress + // $\overline{\boldsymbol{\tau}}$: + SymmetricTensor<2, dim> get_tau_bar() const + { + return 2.0 * c_1 * b_bar; + } - // Calculate the volumetric part of the - // tangent $J - // \mathfrak{c}_\textrm{vol}$: - SymmetricTensor<4, dim> get_Jc_vol() const - { + // Calculate the volumetric part of the + // tangent $J + // \mathfrak{c}_\textrm{vol}$: + SymmetricTensor<4, dim> get_Jc_vol() const + { - return p_tilde * det_F - * ( StandardTensors::IxI - - (2.0 * StandardTensors::II) ); - } + return p_tilde * det_F + * ( StandardTensors::IxI + - (2.0 * StandardTensors::II) ); + } - // Calculate the isochoric part of the - // tangent $J - // \mathfrak{c}_\textrm{iso}$: - SymmetricTensor<4, dim> get_Jc_iso() const - { - const SymmetricTensor<2, dim> tau_bar = get_tau_bar(); - const SymmetricTensor<2, dim> tau_iso = get_tau_iso(); - const SymmetricTensor<4, dim> tau_iso_x_I - = outer_product(tau_iso, - StandardTensors::I); - const SymmetricTensor<4, dim> I_x_tau_iso - = outer_product(StandardTensors::I, - tau_iso); - const SymmetricTensor<4, dim> c_bar = get_c_bar(); - - return (2.0 / 3.0) * trace(tau_bar) - * StandardTensors::dev_P - - (2.0 / 3.0) * (tau_iso_x_I + I_x_tau_iso) - + StandardTensors::dev_P * c_bar - * StandardTensors::dev_P; - } + // Calculate the isochoric part of the + // tangent $J + // \mathfrak{c}_\textrm{iso}$: + SymmetricTensor<4, dim> get_Jc_iso() const + { + const SymmetricTensor<2, dim> tau_bar = get_tau_bar(); + const SymmetricTensor<2, dim> tau_iso = get_tau_iso(); + const SymmetricTensor<4, dim> tau_iso_x_I + = outer_product(tau_iso, + StandardTensors::I); + const SymmetricTensor<4, dim> I_x_tau_iso + = outer_product(StandardTensors::I, + tau_iso); + const SymmetricTensor<4, dim> c_bar = get_c_bar(); + + return (2.0 / 3.0) * trace(tau_bar) + * StandardTensors::dev_P + - (2.0 / 3.0) * (tau_iso_x_I + I_x_tau_iso) + + StandardTensors::dev_P * c_bar + * StandardTensors::dev_P; + } - // Calculate the fictitious elasticity - // tensor $\overline{\mathfrak{c}}$. - // For the material model chosen this - // is simply zero: - SymmetricTensor<4, dim> get_c_bar() const - { - return SymmetricTensor<4, dim>(); - } + // Calculate the fictitious elasticity + // tensor $\overline{\mathfrak{c}}$. + // For the material model chosen this + // is simply zero: + SymmetricTensor<4, dim> get_c_bar() const + { + return SymmetricTensor<4, dim>(); + } }; // @sect3{Quadrature point history} @@ -729,164 +729,164 @@ namespace Step44 template class PointHistory { - public: - PointHistory() - : - material(NULL), - F_inv(StandardTensors::I), - tau(SymmetricTensor<2, dim>()), - d2Psi_vol_dJ2(0.0), - dPsi_vol_dJ(0.0), - Jc(SymmetricTensor<4, dim>()) - {} - - virtual ~PointHistory() - { - delete material; - material = NULL; - } + public: + PointHistory() + : + material(NULL), + F_inv(StandardTensors::I), + tau(SymmetricTensor<2, dim>()), + d2Psi_vol_dJ2(0.0), + dPsi_vol_dJ(0.0), + Jc(SymmetricTensor<4, dim>()) + {} + + virtual ~PointHistory() + { + delete material; + material = NULL; + } - // The first function is used to create - // a material object and to initialize - // all tensors correctly: - // The second one updates the stored - // values and stresses based on the - // current deformation measure - // $\textrm{Grad}\mathbf{u}_{\textrm{n}}$, - // pressure $\widetilde{p}$ and - // dilation $\widetilde{J}$ field - // values. - void setup_lqp (const Parameters::AllParameters & parameters) - { - material = new Material_Compressible_Neo_Hook_Three_Field(parameters.mu, - parameters.nu); - update_values(Tensor<2, dim>(), 0.0, 1.0); - } + // The first function is used to create + // a material object and to initialize + // all tensors correctly: + // The second one updates the stored + // values and stresses based on the + // current deformation measure + // $\textrm{Grad}\mathbf{u}_{\textrm{n}}$, + // pressure $\widetilde{p}$ and + // dilation $\widetilde{J}$ field + // values. + void setup_lqp (const Parameters::AllParameters ¶meters) + { + material = new Material_Compressible_Neo_Hook_Three_Field(parameters.mu, + parameters.nu); + update_values(Tensor<2, dim>(), 0.0, 1.0); + } - // To this end, we calculate the - // deformation gradient $\mathbf{F}$ - // from the displacement gradient - // $\textrm{Grad}\ \mathbf{u}$, i.e. - // $\mathbf{F}(\mathbf{u}) = \mathbf{I} - // + \textrm{Grad}\ \mathbf{u}$ and - // then let the material model - // associated with this quadrature - // point update itself. When computing - // the deformation gradient, we have to - // take care with which data types we - // compare the sum $\mathbf{I} + - // \textrm{Grad}\ \mathbf{u}$: Since - // $I$ has data type SymmetricTensor, - // just writing I + - // Grad_u_n would convert the - // second argument to a symmetric - // tensor, perform the sum, and then - // cast the result to a Tensor (i.e., - // the type of a possibly non-symmetric - // tensor). However, since - // Grad_u_n is - // nonsymmetric in general, the - // conversion to SymmetricTensor will - // fail. We can avoid this back and - // forth by converting $I$ to Tensor - // first, and then performing the - // addition as between non-symmetric - // tensors: - void update_values (const Tensor<2, dim> & Grad_u_n, - const double p_tilde, - const double J_tilde) - { - const Tensor<2, dim> F - = (Tensor<2, dim>(StandardTensors::I) + - Grad_u_n); - material->update_material_data(F, p_tilde, J_tilde); - - // The material has been updated so - // we now calculate the Kirchhoff - // stress $\mathbf{\tau}$, the - // tangent $J\mathfrak{c}$ - // and the first and second derivatives - // of the volumetric free energy. - // - // We also store the inverse of - // the deformation gradient since - // we frequently use it: - F_inv = invert(F); - tau = material->get_tau(); - Jc = material->get_Jc(); - dPsi_vol_dJ = material->get_dPsi_vol_dJ(); - d2Psi_vol_dJ2 = material->get_d2Psi_vol_dJ2(); + // To this end, we calculate the + // deformation gradient $\mathbf{F}$ + // from the displacement gradient + // $\textrm{Grad}\ \mathbf{u}$, i.e. + // $\mathbf{F}(\mathbf{u}) = \mathbf{I} + // + \textrm{Grad}\ \mathbf{u}$ and + // then let the material model + // associated with this quadrature + // point update itself. When computing + // the deformation gradient, we have to + // take care with which data types we + // compare the sum $\mathbf{I} + + // \textrm{Grad}\ \mathbf{u}$: Since + // $I$ has data type SymmetricTensor, + // just writing I + + // Grad_u_n would convert the + // second argument to a symmetric + // tensor, perform the sum, and then + // cast the result to a Tensor (i.e., + // the type of a possibly non-symmetric + // tensor). However, since + // Grad_u_n is + // nonsymmetric in general, the + // conversion to SymmetricTensor will + // fail. We can avoid this back and + // forth by converting $I$ to Tensor + // first, and then performing the + // addition as between non-symmetric + // tensors: + void update_values (const Tensor<2, dim> &Grad_u_n, + const double p_tilde, + const double J_tilde) + { + const Tensor<2, dim> F + = (Tensor<2, dim>(StandardTensors::I) + + Grad_u_n); + material->update_material_data(F, p_tilde, J_tilde); + + // The material has been updated so + // we now calculate the Kirchhoff + // stress $\mathbf{\tau}$, the + // tangent $J\mathfrak{c}$ + // and the first and second derivatives + // of the volumetric free energy. + // + // We also store the inverse of + // the deformation gradient since + // we frequently use it: + F_inv = invert(F); + tau = material->get_tau(); + Jc = material->get_Jc(); + dPsi_vol_dJ = material->get_dPsi_vol_dJ(); + d2Psi_vol_dJ2 = material->get_d2Psi_vol_dJ2(); - } + } - // We offer an interface to retrieve - // certain data. Here are the - // kinematic variables: - double get_J_tilde() const - { - return material->get_J_tilde(); - } + // We offer an interface to retrieve + // certain data. Here are the + // kinematic variables: + double get_J_tilde() const + { + return material->get_J_tilde(); + } - double get_det_F() const - { - return material->get_det_F(); - } + double get_det_F() const + { + return material->get_det_F(); + } - const Tensor<2, dim>& get_F_inv() const - { - return F_inv; - } + const Tensor<2, dim> &get_F_inv() const + { + return F_inv; + } - // ...and the kinetic variables. These - // are used in the material and global - // tangent matrix and residual assembly - // operations: - double get_p_tilde() const - { - return material->get_p_tilde(); - } + // ...and the kinetic variables. These + // are used in the material and global + // tangent matrix and residual assembly + // operations: + double get_p_tilde() const + { + return material->get_p_tilde(); + } - const SymmetricTensor<2, dim>& get_tau() const - { - return tau; - } + const SymmetricTensor<2, dim> &get_tau() const + { + return tau; + } - double get_dPsi_vol_dJ() const - { - return dPsi_vol_dJ; - } + double get_dPsi_vol_dJ() const + { + return dPsi_vol_dJ; + } - double get_d2Psi_vol_dJ2() const - { - return d2Psi_vol_dJ2; - } + double get_d2Psi_vol_dJ2() const + { + return d2Psi_vol_dJ2; + } - // and finally the tangent - const SymmetricTensor<4, dim>& get_Jc() const - { - return Jc; - } + // and finally the tangent + const SymmetricTensor<4, dim> &get_Jc() const + { + return Jc; + } - // In terms of member functions, this - // class stores for the quadrature - // point it represents a copy of a - // material type in case different - // materials are used in different - // regions of the domain, as well as - // the inverse of the deformation - // gradient... - private: - Material_Compressible_Neo_Hook_Three_Field* material; - - Tensor<2, dim> F_inv; - - // ... and stress-type variables along - // with the tangent $J\mathfrak{c}$: - SymmetricTensor<2, dim> tau; - double d2Psi_vol_dJ2; - double dPsi_vol_dJ; - - SymmetricTensor<4, dim> Jc; + // In terms of member functions, this + // class stores for the quadrature + // point it represents a copy of a + // material type in case different + // materials are used in different + // regions of the domain, as well as + // the inverse of the deformation + // gradient... + private: + Material_Compressible_Neo_Hook_Three_Field *material; + + Tensor<2, dim> F_inv; + + // ... and stress-type variables along + // with the tangent $J\mathfrak{c}$: + SymmetricTensor<2, dim> tau; + double d2Psi_vol_dJ2; + double dPsi_vol_dJ; + + SymmetricTensor<4, dim> Jc; }; @@ -899,284 +899,284 @@ namespace Step44 template class Solid { - public: - Solid(const std::string & input_file); - - virtual - ~Solid(); - - void - run(); - - private: - - // In the private section of this - // class, we first forward declare a - // number of objects that are used in - // parallelizing work using the - // WorkStream object (see the @ref - // threads module for more information - // on this). - // - // We declare such structures for the - // computation of tangent (stiffness) - // matrix, right hand side, static - // condensation, and for updating - // quadrature points: - struct PerTaskData_K; - struct ScratchData_K; - - struct PerTaskData_RHS; - struct ScratchData_RHS; - - struct PerTaskData_SC; - struct ScratchData_SC; - - struct PerTaskData_UQPH; - struct ScratchData_UQPH; - - // We start the collection of member - // functions with one that builds the - // grid: - void - make_grid(); - - // Set up the finite element system to - // be solved: - void - system_setup(); - - void - determine_component_extractors(); - - // Several functions to assemble the - // system and right hand side matrices - // using multi-threading. Each of them - // comes as a wrapper function, one - // that is executed to do the work in - // the WorkStream model on one cell, - // and one that copies the work done on - // this one cell into the global object - // that represents it: - void - assemble_system_tangent(); - - void - assemble_system_tangent_one_cell(const typename DoFHandler::active_cell_iterator & cell, - ScratchData_K & scratch, - PerTaskData_K & data); - - void - copy_local_to_global_K(const PerTaskData_K & data); - - void - assemble_system_rhs(); - - void - assemble_system_rhs_one_cell(const typename DoFHandler::active_cell_iterator & cell, - ScratchData_RHS & scratch, - PerTaskData_RHS & data); - - void - copy_local_to_global_rhs(const PerTaskData_RHS & data); - - void - assemble_sc(); - - void - assemble_sc_one_cell(const typename DoFHandler::active_cell_iterator & cell, - ScratchData_SC & scratch, - PerTaskData_SC & data); - - void - copy_local_to_global_sc(const PerTaskData_SC & data); - - // Apply Dirichlet boundary conditions on - // the displacement field - void - make_constraints(const int & it_nr); - - // Create and update the quadrature - // points. Here, no data needs to be - // copied into a global object, so the - // copy_local_to_global function is - // empty: - void - setup_qph(); + public: + Solid(const std::string &input_file); + + virtual + ~Solid(); + + void + run(); + + private: + + // In the private section of this + // class, we first forward declare a + // number of objects that are used in + // parallelizing work using the + // WorkStream object (see the @ref + // threads module for more information + // on this). + // + // We declare such structures for the + // computation of tangent (stiffness) + // matrix, right hand side, static + // condensation, and for updating + // quadrature points: + struct PerTaskData_K; + struct ScratchData_K; + + struct PerTaskData_RHS; + struct ScratchData_RHS; + + struct PerTaskData_SC; + struct ScratchData_SC; + + struct PerTaskData_UQPH; + struct ScratchData_UQPH; + + // We start the collection of member + // functions with one that builds the + // grid: + void + make_grid(); - void - update_qph_incremental(const BlockVector & solution_delta); + // Set up the finite element system to + // be solved: + void + system_setup(); - void - update_qph_incremental_one_cell(const typename DoFHandler::active_cell_iterator & cell, - ScratchData_UQPH & scratch, - PerTaskData_UQPH & data); + void + determine_component_extractors(); - void - copy_local_to_global_UQPH(const PerTaskData_UQPH & data) - {} - - // Solve for the displacement using a - // Newton-Raphson method. We break this - // function into the nonlinear loop and - // the function that solves the - // linearized Newton-Raphson step: - void - solve_nonlinear_timestep(BlockVector & solution_delta); + // Several functions to assemble the + // system and right hand side matrices + // using multi-threading. Each of them + // comes as a wrapper function, one + // that is executed to do the work in + // the WorkStream model on one cell, + // and one that copies the work done on + // this one cell into the global object + // that represents it: + void + assemble_system_tangent(); + + void + assemble_system_tangent_one_cell(const typename DoFHandler::active_cell_iterator &cell, + ScratchData_K &scratch, + PerTaskData_K &data); + + void + copy_local_to_global_K(const PerTaskData_K &data); + + void + assemble_system_rhs(); + + void + assemble_system_rhs_one_cell(const typename DoFHandler::active_cell_iterator &cell, + ScratchData_RHS &scratch, + PerTaskData_RHS &data); + + void + copy_local_to_global_rhs(const PerTaskData_RHS &data); + + void + assemble_sc(); + + void + assemble_sc_one_cell(const typename DoFHandler::active_cell_iterator &cell, + ScratchData_SC &scratch, + PerTaskData_SC &data); + + void + copy_local_to_global_sc(const PerTaskData_SC &data); + + // Apply Dirichlet boundary conditions on + // the displacement field + void + make_constraints(const int &it_nr); + + // Create and update the quadrature + // points. Here, no data needs to be + // copied into a global object, so the + // copy_local_to_global function is + // empty: + void + setup_qph(); - std::pair - solve_linear_system(BlockVector & newton_update); + void + update_qph_incremental(const BlockVector &solution_delta); + + void + update_qph_incremental_one_cell(const typename DoFHandler::active_cell_iterator &cell, + ScratchData_UQPH &scratch, + PerTaskData_UQPH &data); + + void + copy_local_to_global_UQPH(const PerTaskData_UQPH &data) + {} + + // Solve for the displacement using a + // Newton-Raphson method. We break this + // function into the nonlinear loop and + // the function that solves the + // linearized Newton-Raphson step: + void + solve_nonlinear_timestep(BlockVector &solution_delta); + + std::pair + solve_linear_system(BlockVector &newton_update); + + // Solution retrieval as well as + // post-processing and writing data to + // file: + BlockVector + get_total_solution(const BlockVector &solution_delta) const; + + void + output_results() const; + + // Finally, some member variables that + // describe the current state: A + // collection of the parameters used to + // describe the problem setup... + Parameters::AllParameters parameters; + + // ...the volume of the reference and + // current configurations... + double vol_reference; + double vol_current; + + // ...and description of the geometry on which + // the problem is solved: + Triangulation triangulation; + + // Also, keep track of the current time and the + // time spent evaluating certain + // functions + Time time; + TimerOutput timer; + + // A storage object for quadrature point + // information. See step-18 for more on + // this: + std::vector > quadrature_point_history; + + // A description of the finite-element + // system including the displacement + // polynomial degree, the + // degree-of-freedom handler, number of + // dof's per cell and the extractor + // objects used to retrieve information + // from the solution vectors: + const unsigned int degree; + const FESystem fe; + DoFHandler dof_handler_ref; + const unsigned int dofs_per_cell; + const FEValuesExtractors::Vector u_fe; + const FEValuesExtractors::Scalar p_fe; + const FEValuesExtractors::Scalar J_fe; + + // Description of how the block-system is + // arranged. There are 3 blocks, the first + // contains a vector DOF $\mathbf{u}$ + // while the other two describe scalar + // DOFs, $\widetilde{p}$ and + // $\widetilde{J}$. + static const unsigned int n_blocks = 3; + static const unsigned int n_components = dim + 2; + static const unsigned int first_u_component = 0; + static const unsigned int p_component = dim; + static const unsigned int J_component = dim + 1; + + enum + { + u_dof = 0, + p_dof = 1, + J_dof = 2 + }; - // Solution retrieval as well as - // post-processing and writing data to - // file: - BlockVector - get_total_solution(const BlockVector & solution_delta) const; + std::vector dofs_per_block; + std::vector element_indices_u; + std::vector element_indices_p; + std::vector element_indices_J; + + // Rules for Gauss-quadrature on both the + // cell and faces. The number of + // quadrature points on both cells and + // faces is recorded. + const QGauss qf_cell; + const QGauss qf_face; + const unsigned int n_q_points; + const unsigned int n_q_points_f; + + // Objects that store the converged + // solution and right-hand side vectors, + // as well as the tangent matrix. There + // is a ConstraintMatrix object used to + // keep track of constraints. We make + // use of a sparsity pattern designed for + // a block system. + ConstraintMatrix constraints; + BlockSparsityPattern sparsity_pattern; + BlockSparseMatrix tangent_matrix; + BlockVector system_rhs; + BlockVector solution_n; + + // Then define a number of variables to + // store norms and update norms and + // normalisation factors. + struct Errors + { + Errors() + : + norm(1.0), u(1.0), p(1.0), J(1.0) + {} - void - output_results() const; - - // Finally, some member variables that - // describe the current state: A - // collection of the parameters used to - // describe the problem setup... - Parameters::AllParameters parameters; - - // ...the volume of the reference and - // current configurations... - double vol_reference; - double vol_current; - - // ...and description of the geometry on which - // the problem is solved: - Triangulation triangulation; - - // Also, keep track of the current time and the - // time spent evaluating certain - // functions - Time time; - TimerOutput timer; - - // A storage object for quadrature point - // information. See step-18 for more on - // this: - std::vector > quadrature_point_history; - - // A description of the finite-element - // system including the displacement - // polynomial degree, the - // degree-of-freedom handler, number of - // dof's per cell and the extractor - // objects used to retrieve information - // from the solution vectors: - const unsigned int degree; - const FESystem fe; - DoFHandler dof_handler_ref; - const unsigned int dofs_per_cell; - const FEValuesExtractors::Vector u_fe; - const FEValuesExtractors::Scalar p_fe; - const FEValuesExtractors::Scalar J_fe; - - // Description of how the block-system is - // arranged. There are 3 blocks, the first - // contains a vector DOF $\mathbf{u}$ - // while the other two describe scalar - // DOFs, $\widetilde{p}$ and - // $\widetilde{J}$. - static const unsigned int n_blocks = 3; - static const unsigned int n_components = dim + 2; - static const unsigned int first_u_component = 0; - static const unsigned int p_component = dim; - static const unsigned int J_component = dim + 1; - - enum + void reset() { - u_dof = 0, - p_dof = 1, - J_dof = 2 - }; - - std::vector dofs_per_block; - std::vector element_indices_u; - std::vector element_indices_p; - std::vector element_indices_J; - - // Rules for Gauss-quadrature on both the - // cell and faces. The number of - // quadrature points on both cells and - // faces is recorded. - const QGauss qf_cell; - const QGauss qf_face; - const unsigned int n_q_points; - const unsigned int n_q_points_f; - - // Objects that store the converged - // solution and right-hand side vectors, - // as well as the tangent matrix. There - // is a ConstraintMatrix object used to - // keep track of constraints. We make - // use of a sparsity pattern designed for - // a block system. - ConstraintMatrix constraints; - BlockSparsityPattern sparsity_pattern; - BlockSparseMatrix tangent_matrix; - BlockVector system_rhs; - BlockVector solution_n; - - // Then define a number of variables to - // store norms and update norms and - // normalisation factors. - struct Errors + norm = 1.0; + u = 1.0; + p = 1.0; + J = 1.0; + } + void normalise(const Errors &rhs) { - Errors() - : - norm(1.0), u(1.0), p(1.0), J(1.0) - {} - - void reset() - { - norm = 1.0; - u = 1.0; - p = 1.0; - J = 1.0; - } - void normalise(const Errors & rhs) - { - if (rhs.norm != 0.0) - norm /= rhs.norm; - if (rhs.u != 0.0) - u /= rhs.u; - if (rhs.p != 0.0) - p /= rhs.p; - if (rhs.J != 0.0) - J /= rhs.J; - } + if (rhs.norm != 0.0) + norm /= rhs.norm; + if (rhs.u != 0.0) + u /= rhs.u; + if (rhs.p != 0.0) + p /= rhs.p; + if (rhs.J != 0.0) + J /= rhs.J; + } - double norm, u, p, J; - }; + double norm, u, p, J; + }; - Errors error_residual, error_residual_0, error_residual_norm, error_update, - error_update_0, error_update_norm; + Errors error_residual, error_residual_0, error_residual_norm, error_update, + error_update_0, error_update_norm; - // Methods to calculate error measures - void - get_error_residual(Errors & error_residual); + // Methods to calculate error measures + void + get_error_residual(Errors &error_residual); - void - get_error_update(const BlockVector & newton_update, - Errors & error_update); + void + get_error_update(const BlockVector &newton_update, + Errors &error_update); - std::pair - get_error_dilation(); + std::pair + get_error_dilation(); - // Print information to screen - // in a pleasing way... - static - void - print_conv_header(); + // Print information to screen + // in a pleasing way... + static + void + print_conv_header(); - void - print_conv_footer(); + void + print_conv_footer(); }; // @sect3{Implementation of the Solid class} @@ -1185,50 +1185,50 @@ namespace Step44 // We initialise the Solid class using data extracted // from the parameter file. template - Solid::Solid(const std::string & input_file) - : - parameters(input_file), - triangulation(Triangulation::maximum_smoothing), - time(parameters.end_time, parameters.delta_t), - timer(std::cout, - TimerOutput::summary, - TimerOutput::wall_times), - degree(parameters.poly_degree), - // The Finite Element - // System is composed of - // dim continuous - // displacement DOFs, and - // discontinuous pressure - // and dilatation DOFs. In - // an attempt to satisfy - // the Babuska-Brezzi or LBB stability - // conditions (see Hughes (2000)), we - // setup a $Q_n \times - // DGPM_{n-1} \times DGPM_{n-1}$ - // system. $Q_2 \times DGPM_1 - // \times DGPM_1$ elements - // satisfy this condition, - // while $Q_1 \times DGPM_0 - // \times DGPM_0$ elements do - // not. However, it has - // been shown that the - // latter demonstrate good - // convergence - // characteristics - // nonetheless. - fe(FE_Q(parameters.poly_degree), dim, // displacement - FE_DGPMonomial(parameters.poly_degree - 1), 1, // pressure - FE_DGPMonomial(parameters.poly_degree - 1), 1), // dilatation - dof_handler_ref(triangulation), - dofs_per_cell (fe.dofs_per_cell), - u_fe(first_u_component), - p_fe(p_component), - J_fe(J_component), - dofs_per_block(n_blocks), - qf_cell(parameters.quad_order), - qf_face(parameters.quad_order), - n_q_points (qf_cell.size()), - n_q_points_f (qf_face.size()) + Solid::Solid(const std::string &input_file) + : + parameters(input_file), + triangulation(Triangulation::maximum_smoothing), + time(parameters.end_time, parameters.delta_t), + timer(std::cout, + TimerOutput::summary, + TimerOutput::wall_times), + degree(parameters.poly_degree), + // The Finite Element + // System is composed of + // dim continuous + // displacement DOFs, and + // discontinuous pressure + // and dilatation DOFs. In + // an attempt to satisfy + // the Babuska-Brezzi or LBB stability + // conditions (see Hughes (2000)), we + // setup a $Q_n \times + // DGPM_{n-1} \times DGPM_{n-1}$ + // system. $Q_2 \times DGPM_1 + // \times DGPM_1$ elements + // satisfy this condition, + // while $Q_1 \times DGPM_0 + // \times DGPM_0$ elements do + // not. However, it has + // been shown that the + // latter demonstrate good + // convergence + // characteristics + // nonetheless. + fe(FE_Q(parameters.poly_degree), dim, // displacement + FE_DGPMonomial(parameters.poly_degree - 1), 1, // pressure + FE_DGPMonomial(parameters.poly_degree - 1), 1), // dilatation + dof_handler_ref(triangulation), + dofs_per_cell (fe.dofs_per_cell), + u_fe(first_u_component), + p_fe(p_component), + J_fe(J_component), + dofs_per_block(n_blocks), + qf_cell(parameters.quad_order), + qf_face(parameters.quad_order), + n_q_points (qf_cell.size()), + n_q_points_f (qf_face.size()) { determine_component_extractors(); } @@ -1277,7 +1277,7 @@ namespace Step44 constraints.close(); const ComponentSelectFunction - J_mask (J_component, n_components); + J_mask (J_component, n_components); VectorTools::project (dof_handler_ref, constraints, @@ -1288,31 +1288,31 @@ namespace Step44 output_results(); time.increment(); - // We then declare the incremental - // solution update $\varDelta - // \mathbf{\Xi}:= \{\varDelta - // \mathbf{u},\varDelta \widetilde{p}, - // \varDelta \widetilde{J} \}$ and start - // the loop over the time domain. - // - // At the beginning, we reset the solution update - // for this time step... + // We then declare the incremental + // solution update $\varDelta + // \mathbf{\Xi}:= \{\varDelta + // \mathbf{u},\varDelta \widetilde{p}, + // \varDelta \widetilde{J} \}$ and start + // the loop over the time domain. + // + // At the beginning, we reset the solution update + // for this time step... BlockVector solution_delta(dofs_per_block); while (time.current() < time.end()) { solution_delta = 0.0; - // ...solve the current time step and - // update total solution vector - // $\mathbf{\Xi}_{\textrm{n}} = - // \mathbf{\Xi}_{\textrm{n-1}} + - // \varDelta \mathbf{\Xi}$... + // ...solve the current time step and + // update total solution vector + // $\mathbf{\Xi}_{\textrm{n}} = + // \mathbf{\Xi}_{\textrm{n-1}} + + // \varDelta \mathbf{\Xi}$... solve_nonlinear_timestep(solution_delta); solution_n += solution_delta; - // ...and plot the results before - // moving on happily to the next time - // step: + // ...and plot the results before + // moving on happily to the next time + // step: output_results(); time.increment(); } @@ -1336,19 +1336,19 @@ namespace Step44 template struct Solid::PerTaskData_K { - FullMatrix cell_matrix; - std::vector local_dof_indices; + FullMatrix cell_matrix; + std::vector local_dof_indices; - PerTaskData_K(const unsigned int dofs_per_cell) - : - cell_matrix(dofs_per_cell, dofs_per_cell), - local_dof_indices(dofs_per_cell) - {} + PerTaskData_K(const unsigned int dofs_per_cell) + : + cell_matrix(dofs_per_cell, dofs_per_cell), + local_dof_indices(dofs_per_cell) + {} - void reset() - { - cell_matrix = 0.0; - } + void reset() + { + cell_matrix = 0.0; + } }; @@ -1359,55 +1359,55 @@ namespace Step44 template struct Solid::ScratchData_K { - FEValues fe_values_ref; - - std::vector > Nx; - std::vector > > grad_Nx; - std::vector > > symm_grad_Nx; - - ScratchData_K(const FiniteElement & fe_cell, - const QGauss & qf_cell, - const UpdateFlags uf_cell) - : - fe_values_ref(fe_cell, qf_cell, uf_cell), - Nx(qf_cell.size(), - std::vector(fe_cell.dofs_per_cell)), - grad_Nx(qf_cell.size(), - std::vector >(fe_cell.dofs_per_cell)), - symm_grad_Nx(qf_cell.size(), - std::vector > - (fe_cell.dofs_per_cell)) - {} - - ScratchData_K(const ScratchData_K & rhs) - : - fe_values_ref(rhs.fe_values_ref.get_fe(), - rhs.fe_values_ref.get_quadrature(), - rhs.fe_values_ref.get_update_flags()), - Nx(rhs.Nx), - grad_Nx(rhs.grad_Nx), - symm_grad_Nx(rhs.symm_grad_Nx) - {} - - void reset() + FEValues fe_values_ref; + + std::vector > Nx; + std::vector > > grad_Nx; + std::vector > > symm_grad_Nx; + + ScratchData_K(const FiniteElement &fe_cell, + const QGauss &qf_cell, + const UpdateFlags uf_cell) + : + fe_values_ref(fe_cell, qf_cell, uf_cell), + Nx(qf_cell.size(), + std::vector(fe_cell.dofs_per_cell)), + grad_Nx(qf_cell.size(), + std::vector >(fe_cell.dofs_per_cell)), + symm_grad_Nx(qf_cell.size(), + std::vector > + (fe_cell.dofs_per_cell)) + {} + + ScratchData_K(const ScratchData_K &rhs) + : + fe_values_ref(rhs.fe_values_ref.get_fe(), + rhs.fe_values_ref.get_quadrature(), + rhs.fe_values_ref.get_update_flags()), + Nx(rhs.Nx), + grad_Nx(rhs.grad_Nx), + symm_grad_Nx(rhs.symm_grad_Nx) + {} + + void reset() + { + const unsigned int n_q_points = Nx.size(); + const unsigned int n_dofs_per_cell = Nx[0].size(); + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) { - const unsigned int n_q_points = Nx.size(); - const unsigned int n_dofs_per_cell = Nx[0].size(); - for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + Assert( Nx[q_point].size() == n_dofs_per_cell, ExcInternalError()); + Assert( grad_Nx[q_point].size() == n_dofs_per_cell, + ExcInternalError()); + Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell, + ExcInternalError()); + for (unsigned int k = 0; k < n_dofs_per_cell; ++k) { - Assert( Nx[q_point].size() == n_dofs_per_cell, ExcInternalError()); - Assert( grad_Nx[q_point].size() == n_dofs_per_cell, - ExcInternalError()); - Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell, - ExcInternalError()); - for (unsigned int k = 0; k < n_dofs_per_cell; ++k) - { - Nx[q_point][k] = 0.0; - grad_Nx[q_point][k] = 0.0; - symm_grad_Nx[q_point][k] = 0.0; - } + Nx[q_point][k] = 0.0; + grad_Nx[q_point][k] = 0.0; + symm_grad_Nx[q_point][k] = 0.0; } } + } }; @@ -1419,72 +1419,72 @@ namespace Step44 template struct Solid::PerTaskData_RHS { - Vector cell_rhs; - std::vector local_dof_indices; + Vector cell_rhs; + std::vector local_dof_indices; - PerTaskData_RHS(const unsigned int dofs_per_cell) - : - cell_rhs(dofs_per_cell), - local_dof_indices(dofs_per_cell) - {} + PerTaskData_RHS(const unsigned int dofs_per_cell) + : + cell_rhs(dofs_per_cell), + local_dof_indices(dofs_per_cell) + {} - void reset() - { - cell_rhs = 0.0; - } + void reset() + { + cell_rhs = 0.0; + } }; template struct Solid::ScratchData_RHS { - FEValues fe_values_ref; - FEFaceValues fe_face_values_ref; - - std::vector > Nx; - std::vector > > symm_grad_Nx; - - ScratchData_RHS(const FiniteElement & fe_cell, - const QGauss & qf_cell, const UpdateFlags uf_cell, - const QGauss & qf_face, const UpdateFlags uf_face) - : - fe_values_ref(fe_cell, qf_cell, uf_cell), - fe_face_values_ref(fe_cell, qf_face, uf_face), - Nx(qf_cell.size(), - std::vector(fe_cell.dofs_per_cell)), - symm_grad_Nx(qf_cell.size(), - std::vector > - (fe_cell.dofs_per_cell)) - {} - - ScratchData_RHS(const ScratchData_RHS & rhs) - : - fe_values_ref(rhs.fe_values_ref.get_fe(), - rhs.fe_values_ref.get_quadrature(), - rhs.fe_values_ref.get_update_flags()), - fe_face_values_ref(rhs.fe_face_values_ref.get_fe(), - rhs.fe_face_values_ref.get_quadrature(), - rhs.fe_face_values_ref.get_update_flags()), - Nx(rhs.Nx), - symm_grad_Nx(rhs.symm_grad_Nx) - {} - - void reset() + FEValues fe_values_ref; + FEFaceValues fe_face_values_ref; + + std::vector > Nx; + std::vector > > symm_grad_Nx; + + ScratchData_RHS(const FiniteElement &fe_cell, + const QGauss &qf_cell, const UpdateFlags uf_cell, + const QGauss & qf_face, const UpdateFlags uf_face) + : + fe_values_ref(fe_cell, qf_cell, uf_cell), + fe_face_values_ref(fe_cell, qf_face, uf_face), + Nx(qf_cell.size(), + std::vector(fe_cell.dofs_per_cell)), + symm_grad_Nx(qf_cell.size(), + std::vector > + (fe_cell.dofs_per_cell)) + {} + + ScratchData_RHS(const ScratchData_RHS &rhs) + : + fe_values_ref(rhs.fe_values_ref.get_fe(), + rhs.fe_values_ref.get_quadrature(), + rhs.fe_values_ref.get_update_flags()), + fe_face_values_ref(rhs.fe_face_values_ref.get_fe(), + rhs.fe_face_values_ref.get_quadrature(), + rhs.fe_face_values_ref.get_update_flags()), + Nx(rhs.Nx), + symm_grad_Nx(rhs.symm_grad_Nx) + {} + + void reset() + { + const unsigned int n_q_points = Nx.size(); + const unsigned int n_dofs_per_cell = Nx[0].size(); + for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) { - const unsigned int n_q_points = Nx.size(); - const unsigned int n_dofs_per_cell = Nx[0].size(); - for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) + Assert( Nx[q_point].size() == n_dofs_per_cell, ExcInternalError()); + Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell, + ExcInternalError()); + for (unsigned int k = 0; k < n_dofs_per_cell; ++k) { - Assert( Nx[q_point].size() == n_dofs_per_cell, ExcInternalError()); - Assert( symm_grad_Nx[q_point].size() == n_dofs_per_cell, - ExcInternalError()); - for (unsigned int k = 0; k < n_dofs_per_cell; ++k) - { - Nx[q_point][k] = 0.0; - symm_grad_Nx[q_point][k] = 0.0; - } + Nx[q_point][k] = 0.0; + symm_grad_Nx[q_point][k] = 0.0; } } + } }; @@ -1501,39 +1501,39 @@ namespace Step44 template struct Solid::PerTaskData_SC { - FullMatrix cell_matrix; - std::vector local_dof_indices; - - FullMatrix k_orig; - FullMatrix k_pu; - FullMatrix k_pJ; - FullMatrix k_JJ; - FullMatrix k_pJ_inv; - FullMatrix k_bbar; - FullMatrix A; - FullMatrix B; - FullMatrix C; - - PerTaskData_SC(const unsigned int dofs_per_cell, - const unsigned int n_u, - const unsigned int n_p, - const unsigned int n_J) - : - cell_matrix(dofs_per_cell, dofs_per_cell), - local_dof_indices(dofs_per_cell), - k_orig(dofs_per_cell, dofs_per_cell), - k_pu(n_p, n_u), - k_pJ(n_p, n_J), - k_JJ(n_J, n_J), - k_pJ_inv(n_p, n_J), - k_bbar(n_u, n_u), - A(n_J,n_u), - B(n_J, n_u), - C(n_p, n_u) - {} - - void reset() - {} + FullMatrix cell_matrix; + std::vector local_dof_indices; + + FullMatrix k_orig; + FullMatrix k_pu; + FullMatrix k_pJ; + FullMatrix k_JJ; + FullMatrix k_pJ_inv; + FullMatrix k_bbar; + FullMatrix A; + FullMatrix B; + FullMatrix C; + + PerTaskData_SC(const unsigned int dofs_per_cell, + const unsigned int n_u, + const unsigned int n_p, + const unsigned int n_J) + : + cell_matrix(dofs_per_cell, dofs_per_cell), + local_dof_indices(dofs_per_cell), + k_orig(dofs_per_cell, dofs_per_cell), + k_pu(n_p, n_u), + k_pJ(n_p, n_J), + k_JJ(n_J, n_J), + k_pJ_inv(n_p, n_J), + k_bbar(n_u, n_u), + A(n_J,n_u), + B(n_J, n_u), + C(n_p, n_u) + {} + + void reset() + {} }; @@ -1544,8 +1544,8 @@ namespace Step44 template struct Solid::ScratchData_SC { - void reset() - {} + void reset() + {} }; @@ -1567,8 +1567,8 @@ namespace Step44 template struct Solid::PerTaskData_UQPH { - void reset() - {} + void reset() + {} }; @@ -1579,47 +1579,47 @@ namespace Step44 template struct Solid::ScratchData_UQPH { - const BlockVector &solution_total; - - std::vector > solution_grads_u_total; - std::vector solution_values_p_total; - std::vector solution_values_J_total; - - FEValues fe_values_ref; - - ScratchData_UQPH(const FiniteElement & fe_cell, - const QGauss & qf_cell, - const UpdateFlags uf_cell, - const BlockVector & solution_total) - : - solution_total(solution_total), - solution_grads_u_total(qf_cell.size()), - solution_values_p_total(qf_cell.size()), - solution_values_J_total(qf_cell.size()), - fe_values_ref(fe_cell, qf_cell, uf_cell) - {} - - ScratchData_UQPH(const ScratchData_UQPH & rhs) - : - solution_total(rhs.solution_total), - solution_grads_u_total(rhs.solution_grads_u_total), - solution_values_p_total(rhs.solution_values_p_total), - solution_values_J_total(rhs.solution_values_J_total), - fe_values_ref(rhs.fe_values_ref.get_fe(), - rhs.fe_values_ref.get_quadrature(), - rhs.fe_values_ref.get_update_flags()) - {} - - void reset() + const BlockVector &solution_total; + + std::vector > solution_grads_u_total; + std::vector solution_values_p_total; + std::vector solution_values_J_total; + + FEValues fe_values_ref; + + ScratchData_UQPH(const FiniteElement &fe_cell, + const QGauss &qf_cell, + const UpdateFlags uf_cell, + const BlockVector &solution_total) + : + solution_total(solution_total), + solution_grads_u_total(qf_cell.size()), + solution_values_p_total(qf_cell.size()), + solution_values_J_total(qf_cell.size()), + fe_values_ref(fe_cell, qf_cell, uf_cell) + {} + + ScratchData_UQPH(const ScratchData_UQPH &rhs) + : + solution_total(rhs.solution_total), + solution_grads_u_total(rhs.solution_grads_u_total), + solution_values_p_total(rhs.solution_values_p_total), + solution_values_J_total(rhs.solution_values_J_total), + fe_values_ref(rhs.fe_values_ref.get_fe(), + rhs.fe_values_ref.get_quadrature(), + rhs.fe_values_ref.get_update_flags()) + {} + + void reset() + { + const unsigned int n_q_points = solution_grads_u_total.size(); + for (unsigned int q = 0; q < n_q_points; ++q) { - const unsigned int n_q_points = solution_grads_u_total.size(); - for (unsigned int q = 0; q < n_q_points; ++q) - { - solution_grads_u_total[q] = 0.0; - solution_values_p_total[q] = 0.0; - solution_values_J_total[q] = 0.0; - } + solution_grads_u_total[q] = 0.0; + solution_values_p_total[q] = 0.0; + solution_values_J_total[q] = 0.0; } + } }; @@ -1646,16 +1646,16 @@ namespace Step44 vol_current = vol_reference; std::cout << "Grid:\n\t Reference volume: " << vol_reference << std::endl; - // Since we wish to apply a Neumann BC to - // a patch on the top surface, we must - // find the cell faces in this part of - // the domain and mark them with a - // distinct boundary ID number. The - // faces we are looking for are on the +y - // surface and will get boundary ID 6 - // (zero through five are already used - // when creating the six faces of the - // cube domain): + // Since we wish to apply a Neumann BC to + // a patch on the top surface, we must + // find the cell faces in this part of + // the domain and mark them with a + // distinct boundary ID number. The + // faces we are looking for are on the +y + // surface and will get boundary ID 6 + // (zero through five are already used + // when creating the six faces of the + // cube domain): typename Triangulation::active_cell_iterator cell = triangulation.begin_active(), endc = triangulation.end(); for (; cell != endc; ++cell) @@ -1686,10 +1686,10 @@ namespace Step44 block_component[p_component] = p_dof; // Pressure block_component[J_component] = J_dof; // Dilatation - // The DOF handler is then initialised and we - // renumber the grid in an efficient - // manner. We also record the number of - // DOF's per block. + // The DOF handler is then initialised and we + // renumber the grid in an efficient + // manner. We also record the number of + // DOF's per block. dof_handler_ref.distribute_dofs(fe); DoFRenumbering::Cuthill_McKee(dof_handler_ref); DoFRenumbering::component_wise(dof_handler_ref, block_component); @@ -1701,7 +1701,7 @@ namespace Step44 << "\n\t Number of degrees of freedom: " << dof_handler_ref.n_dofs() << std::endl; - // Setup the sparsity pattern and tangent matrix + // Setup the sparsity pattern and tangent matrix tangent_matrix.clear(); { const unsigned int n_dofs_u = dofs_per_block[u_dof]; @@ -1723,28 +1723,28 @@ namespace Step44 csp.block(J_dof, J_dof).reinit(n_dofs_J, n_dofs_J); csp.collect_sizes(); - // The global system matrix initially has the following structure - // @f{align*} - // \underbrace{\begin{bmatrix} - // \mathbf{\mathsf{K}}_{uu} & \mathbf{\mathsf{K}}_{u\widetilde{p}} & \mathbf{0} - // \\ \mathbf{\mathsf{K}}_{\widetilde{p}u} & \mathbf{0} & \mathbf{\mathsf{K}}_{\widetilde{p}\widetilde{J}} - // \\ \mathbf{0} & \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{p}} & \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{J}} - // \end{bmatrix}}_{\mathbf{\mathsf{K}}(\mathbf{\Xi}_{\textrm{i}})} - // \underbrace{\begin{bmatrix} - // d \mathbf{\mathsf{u}} - // \\ d \widetilde{\mathbf{\mathsf{p}}} - // \\ d \widetilde{\mathbf{\mathsf{J}}} - // \end{bmatrix}}_{d \mathbf{\Xi}} - // = - // \underbrace{\begin{bmatrix} - // \mathbf{\mathsf{F}}_{u}(\mathbf{u}_{\textrm{i}}) - // \\ \mathbf{\mathsf{F}}_{\widetilde{p}}(\widetilde{p}_{\textrm{i}}) - // \\ \mathbf{\mathsf{F}}_{\widetilde{J}}(\widetilde{J}_{\textrm{i}}) - //\end{bmatrix}}_{ \mathbf{\mathsf{F}}(\mathbf{\Xi}_{\textrm{i}}) } \, . - // @f} - // We optimise the sparsity pattern to reflect this structure - // and prevent unnecessary data creation for the right-diagonal - // block components. + // The global system matrix initially has the following structure + // @f{align*} + // \underbrace{\begin{bmatrix} + // \mathbf{\mathsf{K}}_{uu} & \mathbf{\mathsf{K}}_{u\widetilde{p}} & \mathbf{0} + // \\ \mathbf{\mathsf{K}}_{\widetilde{p}u} & \mathbf{0} & \mathbf{\mathsf{K}}_{\widetilde{p}\widetilde{J}} + // \\ \mathbf{0} & \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{p}} & \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{J}} + // \end{bmatrix}}_{\mathbf{\mathsf{K}}(\mathbf{\Xi}_{\textrm{i}})} + // \underbrace{\begin{bmatrix} + // d \mathbf{\mathsf{u}} + // \\ d \widetilde{\mathbf{\mathsf{p}}} + // \\ d \widetilde{\mathbf{\mathsf{J}}} + // \end{bmatrix}}_{d \mathbf{\Xi}} + // = + // \underbrace{\begin{bmatrix} + // \mathbf{\mathsf{F}}_{u}(\mathbf{u}_{\textrm{i}}) + // \\ \mathbf{\mathsf{F}}_{\widetilde{p}}(\widetilde{p}_{\textrm{i}}) + // \\ \mathbf{\mathsf{F}}_{\widetilde{J}}(\widetilde{J}_{\textrm{i}}) + //\end{bmatrix}}_{ \mathbf{\mathsf{F}}(\mathbf{\Xi}_{\textrm{i}}) } \, . + // @f} + // We optimise the sparsity pattern to reflect this structure + // and prevent unnecessary data creation for the right-diagonal + // block components. Table<2, DoFTools::Coupling> coupling(n_components, n_components); for (unsigned int ii = 0; ii < n_components; ++ii) for (unsigned int jj = 0; jj < n_components; ++jj) @@ -1764,15 +1764,15 @@ namespace Step44 tangent_matrix.reinit(sparsity_pattern); - // We then set up storage vectors + // We then set up storage vectors system_rhs.reinit(dofs_per_block); system_rhs.collect_sizes(); solution_n.reinit(dofs_per_block); solution_n.collect_sizes(); - // ...and finally set up the quadrature - // point history: + // ...and finally set up the quadrature + // point history: setup_qph(); timer.leave_subsection(); @@ -1831,7 +1831,7 @@ namespace Step44 } quadrature_point_history - .resize(triangulation.n_active_cells() * n_q_points); + .resize(triangulation.n_active_cells() * n_q_points); unsigned int history_index = 0; for (typename Triangulation::active_cell_iterator cell = @@ -1846,12 +1846,12 @@ namespace Step44 ExcInternalError()); } - // Next we setup the initial quadrature - // point data: + // Next we setup the initial quadrature + // point data: for (typename Triangulation::active_cell_iterator cell = triangulation.begin_active(); cell != triangulation.end(); ++cell) { - PointHistory* lqph = + PointHistory *lqph = reinterpret_cast*>(cell->user_pointer()); Assert(lqph >= &quadrature_point_history.front(), ExcInternalError()); @@ -1871,7 +1871,7 @@ namespace Step44 // at this Newton increment and then create the initial copy of the scratch and // copy data objects: template - void Solid::update_qph_incremental(const BlockVector & solution_delta) + void Solid::update_qph_incremental(const BlockVector &solution_delta) { timer.enter_subsection("Update QPH data"); std::cout << " UQPH " << std::flush; @@ -1882,9 +1882,9 @@ namespace Step44 PerTaskData_UQPH per_task_data_UQPH; ScratchData_UQPH scratch_data_UQPH(fe, qf_cell, uf_UQPH, solution_total); - // We then pass them and the one-cell update - // function to the WorkStream to be - // processed: + // We then pass them and the one-cell update + // function to the WorkStream to be + // processed: WorkStream::run(dof_handler_ref.begin_active(), dof_handler_ref.end(), *this, @@ -1901,11 +1901,11 @@ namespace Step44 // along to each QP storage object for processing. template void - Solid::update_qph_incremental_one_cell(const typename DoFHandler::active_cell_iterator & cell, - ScratchData_UQPH & scratch, - PerTaskData_UQPH & data) + Solid::update_qph_incremental_one_cell(const typename DoFHandler::active_cell_iterator &cell, + ScratchData_UQPH &scratch, + PerTaskData_UQPH &data) { - PointHistory* lqph = + PointHistory *lqph = reinterpret_cast*>(cell->user_pointer()); Assert(lqph >= &quadrature_point_history.front(), ExcInternalError()); @@ -1920,12 +1920,12 @@ namespace Step44 scratch.reset(); - // We first need to find the values and - // gradients at quadrature points inside - // the current cell and then we update - // each local QP using the displacement - // gradient and total pressure and - // dilatation solution values: + // We first need to find the values and + // gradients at quadrature points inside + // the current cell and then we update + // each local QP using the displacement + // gradient and total pressure and + // dilatation solution values: scratch.fe_values_ref.reinit(cell); scratch.fe_values_ref[u_fe].get_function_gradients(scratch.solution_total, scratch.solution_grads_u_total); @@ -1948,7 +1948,7 @@ namespace Step44 // reset the error storage objects and print solver header. template void - Solid::solve_nonlinear_timestep(BlockVector & solution_delta) + Solid::solve_nonlinear_timestep(BlockVector &solution_delta) { std::cout << std::endl << "Timestep " << time.get_timestep() << " @ " << time.current() << "s" << std::endl; @@ -1964,26 +1964,26 @@ namespace Step44 print_conv_header(); - // We now perform a number of Newton - // iterations to iteratively solve the - // nonlinear problem. Since the problem - // is fully nonlinear and we are using a - // full Newton method, the data stored in - // the tangent matrix and right-hand side - // vector is not reusable and must be - // cleared at each Newton step. We then - // initially build the right-hand side - // vector to check for convergence (and - // store this value in the first - // iteration). The unconstrained DOFs - // of the rhs vector hold the - // out-of-balance forces. The building is - // done before assembling the system - // matrix as the latter is an expensive - // operation and we can potentially avoid - // an extra assembly process by not - // assembling the tangent matrix when - // convergence is attained. + // We now perform a number of Newton + // iterations to iteratively solve the + // nonlinear problem. Since the problem + // is fully nonlinear and we are using a + // full Newton method, the data stored in + // the tangent matrix and right-hand side + // vector is not reusable and must be + // cleared at each Newton step. We then + // initially build the right-hand side + // vector to check for convergence (and + // store this value in the first + // iteration). The unconstrained DOFs + // of the rhs vector hold the + // out-of-balance forces. The building is + // done before assembling the system + // matrix as the latter is an expensive + // operation and we can potentially avoid + // an extra assembly process by not + // assembling the tangent matrix when + // convergence is attained. unsigned int newton_iteration = 0; for (; newton_iteration < parameters.max_iterations_NR; ++newton_iteration) @@ -1999,9 +1999,9 @@ namespace Step44 if (newton_iteration == 0) error_residual_0 = error_residual; - // We can now determine the - // normalised residual error and - // check for solution convergence: + // We can now determine the + // normalised residual error and + // check for solution convergence: error_residual_norm = error_residual; error_residual_norm.normalise(error_residual_0); @@ -2014,32 +2014,32 @@ namespace Step44 break; } - // If we have decided that we want to - // continue with the iteration, we - // assemble the tangent, make and - // impose the Dirichlet constraints, - // and do the solve of the linearised - // system: + // If we have decided that we want to + // continue with the iteration, we + // assemble the tangent, make and + // impose the Dirichlet constraints, + // and do the solve of the linearised + // system: assemble_system_tangent(); make_constraints(newton_iteration); constraints.condense(tangent_matrix, system_rhs); const std::pair - lin_solver_output = solve_linear_system(newton_update); + lin_solver_output = solve_linear_system(newton_update); get_error_update(newton_update, error_update); if (newton_iteration == 0) error_update_0 = error_update; - // We can now determine the - // normalised Newton update error, - // and perform the actual update of - // the solution increment for the - // current time step, update all - // quadrature point information - // pertaining to this new - // displacement and stress state and - // continue iterating: + // We can now determine the + // normalised Newton update error, + // and perform the actual update of + // the solution increment for the + // current time step, update all + // quadrature point information + // pertaining to this new + // displacement and stress state and + // continue iterating: error_update_norm = error_update; error_update_norm.normalise(error_update_0); @@ -2056,21 +2056,21 @@ namespace Step44 << " " << std::endl; } - // At the end, if it turns out that we - // have in fact done more iterations than - // the parameter file allowed, we raise - // an exception that can be caught in the - // main() function. The call - // AssertThrow(condition, - // exc_object) is in essence - // equivalent to if (!cond) throw - // exc_object; but the former form - // fills certain fields in the exception - // object that identify the location - // (filename and line number) where the - // exception was raised to make it - // simpler to identify where the problem - // happened. + // At the end, if it turns out that we + // have in fact done more iterations than + // the parameter file allowed, we raise + // an exception that can be caught in the + // main() function. The call + // AssertThrow(condition, + // exc_object) is in essence + // equivalent to if (!cond) throw + // exc_object; but the former form + // fills certain fields in the exception + // object that identify the location + // (filename and line number) where the + // exception was raised to make it + // simpler to identify where the problem + // happened. AssertThrow (newton_iteration <= parameters.max_iterations_NR, ExcMessage("No convergence in nonlinear solver!")); } @@ -2141,12 +2141,12 @@ namespace Step44 FEValues fe_values_ref(fe, qf_cell, update_JxW_values); for (typename Triangulation::active_cell_iterator - cell = triangulation.begin_active(); + cell = triangulation.begin_active(); cell != triangulation.end(); ++cell) { fe_values_ref.reinit(cell); - PointHistory* lqph = + PointHistory *lqph = reinterpret_cast*>(cell->user_pointer()); Assert(lqph >= &quadrature_point_history.front(), ExcInternalError()); @@ -2181,7 +2181,7 @@ namespace Step44 // do so, we need to ignore constrained DOFs by setting the residual in these // vector components to zero. template - void Solid::get_error_residual(Errors & error_residual) + void Solid::get_error_residual(Errors &error_residual) { BlockVector error_res(dofs_per_block); @@ -2200,8 +2200,8 @@ namespace Step44 // Determine the true Newton update error for the problem template - void Solid::get_error_update(const BlockVector & newton_update, - Errors & error_update) + void Solid::get_error_update(const BlockVector &newton_update, + Errors &error_update) { BlockVector error_ud(dofs_per_block); for (unsigned int i = 0; i < dof_handler_ref.n_dofs(); ++i) @@ -2223,7 +2223,7 @@ namespace Step44 // only updated at the end of the timestep. template BlockVector - Solid::get_total_solution(const BlockVector & solution_delta) const + Solid::get_total_solution(const BlockVector &solution_delta) const { BlockVector solution_total(solution_n); solution_total += solution_delta; @@ -2269,7 +2269,7 @@ namespace Step44 // job for us because the tangent matrix and residual processes have // been split up into two separate functions. template - void Solid::copy_local_to_global_K(const PerTaskData_K & data) + void Solid::copy_local_to_global_K(const PerTaskData_K &data) { for (unsigned int i = 0; i < dofs_per_cell; ++i) for (unsigned int j = 0; j < dofs_per_cell; ++j) @@ -2288,9 +2288,9 @@ namespace Step44 // \ \mathbf{F}^{-1}$. template void - Solid::assemble_system_tangent_one_cell(const typename DoFHandler::active_cell_iterator & cell, - ScratchData_K & scratch, - PerTaskData_K & data) + Solid::assemble_system_tangent_one_cell(const typename DoFHandler::active_cell_iterator &cell, + ScratchData_K &scratch, + PerTaskData_K &data) { data.reset(); scratch.reset(); @@ -2324,25 +2324,25 @@ namespace Step44 } } - // Now we build the local cell stiffness - // matrix. Since the global and local - // system matrices are symmetric, we can - // exploit this property by building only - // the lower half of the local matrix and - // copying the values to the upper half. - // So we only assemble half of the - // $\mathsf{\mathbf{k}}_{uu}$, - // $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{p}} = \mathbf{0}$, - // $\mathsf{\mathbf{k}}_{\widetilde{J} \widetilde{J}}$ - // blocks, while the whole $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{J}}$, - // $\mathsf{\mathbf{k}}_{\mathbf{u} \widetilde{J}} = \mathbf{0}$, - // $\mathsf{\mathbf{k}}_{\mathbf{u} \widetilde{p}}$ - // blocks are built. - // - // In doing so, we first extract some - // configuration dependent variables from - // our QPH history objects for the - // current quadrature point. + // Now we build the local cell stiffness + // matrix. Since the global and local + // system matrices are symmetric, we can + // exploit this property by building only + // the lower half of the local matrix and + // copying the values to the upper half. + // So we only assemble half of the + // $\mathsf{\mathbf{k}}_{uu}$, + // $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{p}} = \mathbf{0}$, + // $\mathsf{\mathbf{k}}_{\widetilde{J} \widetilde{J}}$ + // blocks, while the whole $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{J}}$, + // $\mathsf{\mathbf{k}}_{\mathbf{u} \widetilde{J}} = \mathbf{0}$, + // $\mathsf{\mathbf{k}}_{\mathbf{u} \widetilde{p}}$ + // blocks are built. + // + // In doing so, we first extract some + // configuration dependent variables from + // our QPH history objects for the + // current quadrature point. for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) { const Tensor<2, dim> tau = lqph[q_point].get_tau(); @@ -2350,14 +2350,14 @@ namespace Step44 const double d2Psi_vol_dJ2 = lqph[q_point].get_d2Psi_vol_dJ2(); const double det_F = lqph[q_point].get_det_F(); - // Next we define some aliases to make - // the assembly process easier to follow + // Next we define some aliases to make + // the assembly process easier to follow const std::vector - & N = scratch.Nx[q_point]; + &N = scratch.Nx[q_point]; const std::vector > - & symm_grad_Nx = scratch.symm_grad_Nx[q_point]; + &symm_grad_Nx = scratch.symm_grad_Nx[q_point]; const std::vector > - & grad_Nx = scratch.grad_Nx[q_point]; + &grad_Nx = scratch.grad_Nx[q_point]; const double JxW = scratch.fe_values_ref.JxW(q_point); for (unsigned int i = 0; i < dofs_per_cell; ++i) @@ -2370,12 +2370,12 @@ namespace Step44 const unsigned int component_j = fe.system_to_component_index(j).first; const unsigned int j_group = fe.system_to_base_index(j).first.first; - // This is the $\mathsf{\mathbf{k}}_{\mathbf{u} \mathbf{u}}$ - // contribution. It comprises a - // material contribution, and a - // geometrical stress contribution - // which is only added along the - // local matrix diagonals: + // This is the $\mathsf{\mathbf{k}}_{\mathbf{u} \mathbf{u}}$ + // contribution. It comprises a + // material contribution, and a + // geometrical stress contribution + // which is only added along the + // local matrix diagonals: if ((i_group == j_group) && (i_group == u_dof)) { data.cell_matrix(i, j) += symm_grad_Nx[i] * Jc // The material contribution: @@ -2384,7 +2384,7 @@ namespace Step44 data.cell_matrix(i, j) += grad_Nx[i][component_i] * tau * grad_Nx[j][component_j] * JxW; } - // Next is the $\mathsf{\mathbf{k}}_{ \widetilde{p} \mathbf{u}}$ contribution + // Next is the $\mathsf{\mathbf{k}}_{ \widetilde{p} \mathbf{u}}$ contribution else if ((i_group == p_dof) && (j_group == u_dof)) { data.cell_matrix(i, j) += N[i] * det_F @@ -2392,9 +2392,9 @@ namespace Step44 * StandardTensors::I) * JxW; } - // and lastly the $\mathsf{\mathbf{k}}_{ \widetilde{J} \widetilde{p}}$ - // and $\mathsf{\mathbf{k}}_{ \widetilde{J} \widetilde{J}}$ - // contributions: + // and lastly the $\mathsf{\mathbf{k}}_{ \widetilde{J} \widetilde{p}}$ + // and $\mathsf{\mathbf{k}}_{ \widetilde{J} \widetilde{J}}$ + // contributions: else if ((i_group == J_dof) && (j_group == p_dof)) data.cell_matrix(i, j) -= N[i] * N[j] * JxW; else if ((i_group == j_group) && (i_group == J_dof)) @@ -2406,9 +2406,9 @@ namespace Step44 } } - // Finally, we need to copy the lower - // half of the local matrix into the - // upper half: + // Finally, we need to copy the lower + // half of the local matrix into the + // upper half: for (unsigned int i = 0; i < dofs_per_cell; ++i) for (unsigned int j = i + 1; j < dofs_per_cell; ++j) data.cell_matrix(i, j) = data.cell_matrix(j, i); @@ -2452,7 +2452,7 @@ namespace Step44 template - void Solid::copy_local_to_global_rhs(const PerTaskData_RHS & data) + void Solid::copy_local_to_global_rhs(const PerTaskData_RHS &data) { for (unsigned int i = 0; i < dofs_per_cell; ++i) system_rhs(data.local_dof_indices[i]) += data.cell_rhs(i); @@ -2462,9 +2462,9 @@ namespace Step44 template void - Solid::assemble_system_rhs_one_cell(const typename DoFHandler::active_cell_iterator & cell, - ScratchData_RHS & scratch, - PerTaskData_RHS & data) + Solid::assemble_system_rhs_one_cell(const typename DoFHandler::active_cell_iterator &cell, + ScratchData_RHS &scratch, + PerTaskData_RHS &data) { data.reset(); scratch.reset(); @@ -2477,22 +2477,23 @@ namespace Step44 { const Tensor<2, dim> F_inv = lqph[q_point].get_F_inv(); - for (unsigned int k = 0; k < dofs_per_cell; ++k) { - const unsigned int k_group = fe.system_to_base_index(k).first.first; - - if (k_group == u_dof) - scratch.symm_grad_Nx[q_point][k] - = symmetrize(scratch.fe_values_ref[u_fe].gradient(k, q_point) - * F_inv); - else if (k_group == p_dof) - scratch.Nx[q_point][k] = scratch.fe_values_ref[p_fe].value(k, - q_point); - else if (k_group == J_dof) - scratch.Nx[q_point][k] = scratch.fe_values_ref[J_fe].value(k, - q_point); - else - Assert(k_group <= J_dof, ExcInternalError()); - } + for (unsigned int k = 0; k < dofs_per_cell; ++k) + { + const unsigned int k_group = fe.system_to_base_index(k).first.first; + + if (k_group == u_dof) + scratch.symm_grad_Nx[q_point][k] + = symmetrize(scratch.fe_values_ref[u_fe].gradient(k, q_point) + * F_inv); + else if (k_group == p_dof) + scratch.Nx[q_point][k] = scratch.fe_values_ref[p_fe].value(k, + q_point); + else if (k_group == J_dof) + scratch.Nx[q_point][k] = scratch.fe_values_ref[J_fe].value(k, + q_point); + else + Assert(k_group <= J_dof, ExcInternalError()); + } } for (unsigned int q_point = 0; q_point < n_q_points; ++q_point) @@ -2504,16 +2505,16 @@ namespace Step44 const double dPsi_vol_dJ = lqph[q_point].get_dPsi_vol_dJ(); const std::vector - & N = scratch.Nx[q_point]; + &N = scratch.Nx[q_point]; const std::vector > - & symm_grad_Nx = scratch.symm_grad_Nx[q_point]; + &symm_grad_Nx = scratch.symm_grad_Nx[q_point]; const double JxW = scratch.fe_values_ref.JxW(q_point); - // We first compute the contributions - // from the internal forces. Note, by - // definition of the rhs as the negative - // of the residual, these contributions - // are subtracted. + // We first compute the contributions + // from the internal forces. Note, by + // definition of the rhs as the negative + // of the residual, these contributions + // are subtracted. for (unsigned int i = 0; i < dofs_per_cell; ++i) { const unsigned int i_group = fe.system_to_base_index(i).first.first; @@ -2529,11 +2530,11 @@ namespace Step44 } } - // Next we assemble the Neumann - // contribution. We first check to see it - // the cell face exists on a boundary on - // which a traction is applied and add the - // contribution if this is the case. + // Next we assemble the Neumann + // contribution. We first check to see it + // the cell face exists on a boundary on + // which a traction is applied and add the + // contribution if this is the case. for (unsigned int face = 0; face < GeometryInfo::faces_per_cell; ++face) if (cell->face(face)->at_boundary() == true @@ -2544,35 +2545,35 @@ namespace Step44 for (unsigned int f_q_point = 0; f_q_point < n_q_points_f; ++f_q_point) { - const Tensor<1, dim> & N = + const Tensor<1, dim> &N = scratch.fe_face_values_ref.normal_vector(f_q_point); - // Using the face normal at - // this quadrature point - // we specify - // the traction in reference - // configuration. For this - // problem, a defined pressure - // is applied in the reference - // configuration. The - // direction of the applied - // traction is assumed not to - // evolve with the deformation - // of the domain. The traction - // is defined using the first - // Piola-Kirchhoff stress is - // simply - // $\mathbf{t} = \mathbf{P}\mathbf{N} - // = [p_0 \mathbf{I}] \mathbf{N} = p_0 \mathbf{N}$ - // We use the - // time variable to linearly - // ramp up the pressure load. - // - // Note that the contributions - // to the right hand side - // vector we compute here only - // exist in the displacement - // components of the vector. + // Using the face normal at + // this quadrature point + // we specify + // the traction in reference + // configuration. For this + // problem, a defined pressure + // is applied in the reference + // configuration. The + // direction of the applied + // traction is assumed not to + // evolve with the deformation + // of the domain. The traction + // is defined using the first + // Piola-Kirchhoff stress is + // simply + // $\mathbf{t} = \mathbf{P}\mathbf{N} + // = [p_0 \mathbf{I}] \mathbf{N} = p_0 \mathbf{N}$ + // We use the + // time variable to linearly + // ramp up the pressure load. + // + // Note that the contributions + // to the right hand side + // vector we compute here only + // exist in the displacement + // components of the vector. static const double p0 = -4.0 / (parameters.scale * parameters.scale); @@ -2593,7 +2594,7 @@ namespace Step44 scratch.fe_face_values_ref.shape_value(i, f_q_point); const double JxW = scratch.fe_face_values_ref.JxW( - f_q_point); + f_q_point); data.cell_rhs(i) += (Ni * traction[component_i]) * JxW; @@ -2611,53 +2612,53 @@ namespace Step44 // additional contributions are to be made since the constraints // are already exactly satisfied. template - void Solid::make_constraints(const int & it_nr) + void Solid::make_constraints(const int &it_nr) { std::cout << " CST " << std::flush; - // Since the constraints are different at - // different Newton iterations, we need - // to clear the constraints matrix and - // completely rebuild it. However, after - // the first iteration, the constraints - // remain the same and we can simply skip - // the rebuilding step if we do not clear - // it. + // Since the constraints are different at + // different Newton iterations, we need + // to clear the constraints matrix and + // completely rebuild it. However, after + // the first iteration, the constraints + // remain the same and we can simply skip + // the rebuilding step if we do not clear + // it. if (it_nr > 1) return; constraints.clear(); const bool apply_dirichlet_bc = (it_nr == 0); - // The boundary conditions for the - // indentation problem are as follows: On - // the -x, -y and -z faces (ID's 0,2,4) we - // set up a symmetry condition to allow - // only planar movement while the +x and +y - // faces (ID's 1,3) are traction free. In - // this contrived problem, part of the +z - // face (ID 5) is set to have no motion in - // the x- and y-component. Finally, as - // described earlier, the other part of the - // +z face has an the applied pressure but - // is also constrained in the x- and - // y-directions. - // - // In the following, we will have to tell - // the function interpolation boundary - // values which components of the - // solution vector should be constrained - // (i.e., whether it's the x-, y-, - // z-displacements or combinations - // thereof). This is done using - // ComponentMask objects (see @ref - // GlossComponentMask) which we can get - // from the finite element if we provide - // it with an extractor object for the - // component we wish to select. To this - // end we first set up such extractor - // objects and later use it when - // generating the relevant component - // masks: + // The boundary conditions for the + // indentation problem are as follows: On + // the -x, -y and -z faces (ID's 0,2,4) we + // set up a symmetry condition to allow + // only planar movement while the +x and +y + // faces (ID's 1,3) are traction free. In + // this contrived problem, part of the +z + // face (ID 5) is set to have no motion in + // the x- and y-component. Finally, as + // described earlier, the other part of the + // +z face has an the applied pressure but + // is also constrained in the x- and + // y-directions. + // + // In the following, we will have to tell + // the function interpolation boundary + // values which components of the + // solution vector should be constrained + // (i.e., whether it's the x-, y-, + // z-displacements or combinations + // thereof). This is done using + // ComponentMask objects (see @ref + // GlossComponentMask) which we can get + // from the finite element if we provide + // it with an extractor object for the + // component we wish to select. To this + // end we first set up such extractor + // objects and later use it when + // generating the relevant component + // masks: const FEValuesExtractors::Scalar x_displacement(0); const FEValuesExtractors::Scalar y_displacement(1); const FEValuesExtractors::Scalar z_displacement(2); @@ -2719,16 +2720,16 @@ namespace Step44 ZeroFunction(n_components), constraints, (fe.component_mask(x_displacement) - | - fe.component_mask(y_displacement))); + | + fe.component_mask(y_displacement))); else VectorTools::interpolate_boundary_values(dof_handler_ref, boundary_id, ZeroFunction(n_components), constraints, (fe.component_mask(x_displacement) - | - fe.component_mask(y_displacement))); + | + fe.component_mask(y_displacement))); } { const int boundary_id = 6; @@ -2739,16 +2740,16 @@ namespace Step44 ZeroFunction(n_components), constraints, (fe.component_mask(x_displacement) - | - fe.component_mask(y_displacement))); + | + fe.component_mask(y_displacement))); else VectorTools::interpolate_boundary_values(dof_handler_ref, boundary_id, ZeroFunction(n_components), constraints, (fe.component_mask(x_displacement) - | - fe.component_mask(y_displacement))); + | + fe.component_mask(y_displacement))); } constraints.close(); @@ -2813,7 +2814,7 @@ namespace Step44 // @f] template std::pair - Solid::solve_linear_system(BlockVector & newton_update) + Solid::solve_linear_system(BlockVector &newton_update) { BlockVector A(dofs_per_block); BlockVector B(dofs_per_block); @@ -2821,88 +2822,88 @@ namespace Step44 unsigned int lin_it = 0; double lin_res = 0.0; - // In the first step of this function, we solve for the incremental displacement $d\mathbf{u}$. - // To this end, we perform static condensation to make - // $\mathbf{\mathsf{K}}_{\textrm{con}} - // = \bigl[ \mathbf{\mathsf{K}}_{uu} + \overline{\overline{\mathbf{\mathsf{K}}}}~ \bigr]$ - // and put - // $\mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}}$ - // in the original $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{J}}$ block. - // That is, we make $\mathbf{\mathsf{K}}_{\textrm{store}}$. + // In the first step of this function, we solve for the incremental displacement $d\mathbf{u}$. + // To this end, we perform static condensation to make + // $\mathbf{\mathsf{K}}_{\textrm{con}} + // = \bigl[ \mathbf{\mathsf{K}}_{uu} + \overline{\overline{\mathbf{\mathsf{K}}}}~ \bigr]$ + // and put + // $\mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}}$ + // in the original $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{J}}$ block. + // That is, we make $\mathbf{\mathsf{K}}_{\textrm{store}}$. { assemble_sc(); - // $ - // \mathsf{\mathbf{A}}_{\widetilde{J}} - // = - // \mathsf{\mathbf{K}}^{-1}_{\widetilde{p} \widetilde{J}} - // \mathsf{\mathbf{F}}_{\widetilde{p}} - // $ + // $ + // \mathsf{\mathbf{A}}_{\widetilde{J}} + // = + // \mathsf{\mathbf{K}}^{-1}_{\widetilde{p} \widetilde{J}} + // \mathsf{\mathbf{F}}_{\widetilde{p}} + // $ tangent_matrix.block(p_dof, J_dof).vmult(A.block(J_dof), system_rhs.block(p_dof)); - // $ - // \mathsf{\mathbf{B}}_{\widetilde{J}} - // = - // \mathsf{\mathbf{K}}_{\widetilde{J} \widetilde{J}} - // \mathsf{\mathbf{K}}^{-1}_{\widetilde{p} \widetilde{J}} - // \mathsf{\mathbf{F}}_{\widetilde{p}} - // $ + // $ + // \mathsf{\mathbf{B}}_{\widetilde{J}} + // = + // \mathsf{\mathbf{K}}_{\widetilde{J} \widetilde{J}} + // \mathsf{\mathbf{K}}^{-1}_{\widetilde{p} \widetilde{J}} + // \mathsf{\mathbf{F}}_{\widetilde{p}} + // $ tangent_matrix.block(J_dof, J_dof).vmult(B.block(J_dof), A.block(J_dof)); - // $ - // \mathsf{\mathbf{A}}_{\widetilde{J}} - // = - // \mathsf{\mathbf{F}}_{\widetilde{J}} - // - - // \mathsf{\mathbf{K}}_{\widetilde{J} \widetilde{J}} - // \mathsf{\mathbf{K}}^{-1}_{\widetilde{p} \widetilde{J}} - // \mathsf{\mathbf{F}}_{\widetilde{p}} - // $ + // $ + // \mathsf{\mathbf{A}}_{\widetilde{J}} + // = + // \mathsf{\mathbf{F}}_{\widetilde{J}} + // - + // \mathsf{\mathbf{K}}_{\widetilde{J} \widetilde{J}} + // \mathsf{\mathbf{K}}^{-1}_{\widetilde{p} \widetilde{J}} + // \mathsf{\mathbf{F}}_{\widetilde{p}} + // $ A.block(J_dof).equ(1.0, system_rhs.block(J_dof), -1.0, B.block(J_dof)); - // $ - // \mathsf{\mathbf{A}}_{\widetilde{J}} - // = - // \mathsf{\mathbf{K}}^{-1}_{\widetilde{J} \widetilde{p}} - // [ - // \mathsf{\mathbf{F}}_{\widetilde{J}} - // - - // \mathsf{\mathbf{K}}_{\widetilde{J} \widetilde{J}} - // \mathsf{\mathbf{K}}^{-1}_{\widetilde{p} \widetilde{J}} - // \mathsf{\mathbf{F}}_{\widetilde{p}} - // ] - // $ + // $ + // \mathsf{\mathbf{A}}_{\widetilde{J}} + // = + // \mathsf{\mathbf{K}}^{-1}_{\widetilde{J} \widetilde{p}} + // [ + // \mathsf{\mathbf{F}}_{\widetilde{J}} + // - + // \mathsf{\mathbf{K}}_{\widetilde{J} \widetilde{J}} + // \mathsf{\mathbf{K}}^{-1}_{\widetilde{p} \widetilde{J}} + // \mathsf{\mathbf{F}}_{\widetilde{p}} + // ] + // $ tangent_matrix.block(p_dof, J_dof).Tvmult(A.block(p_dof), A.block(J_dof)); - // $ - // \mathsf{\mathbf{A}}_{\mathbf{u}} - // = - // \mathsf{\mathbf{K}}_{\mathbf{u} \widetilde{p}} - // \mathsf{\mathbf{K}}^{-1}_{\widetilde{J} \widetilde{p}} - // [ - // \mathsf{\mathbf{F}}_{\widetilde{J}} - // - - // \mathsf{\mathbf{K}}_{\widetilde{J} \widetilde{J}} - // \mathsf{\mathbf{K}}^{-1}_{\widetilde{p} \widetilde{J}} - // \mathsf{\mathbf{F}}_{\widetilde{p}} - // ] - // $ + // $ + // \mathsf{\mathbf{A}}_{\mathbf{u}} + // = + // \mathsf{\mathbf{K}}_{\mathbf{u} \widetilde{p}} + // \mathsf{\mathbf{K}}^{-1}_{\widetilde{J} \widetilde{p}} + // [ + // \mathsf{\mathbf{F}}_{\widetilde{J}} + // - + // \mathsf{\mathbf{K}}_{\widetilde{J} \widetilde{J}} + // \mathsf{\mathbf{K}}^{-1}_{\widetilde{p} \widetilde{J}} + // \mathsf{\mathbf{F}}_{\widetilde{p}} + // ] + // $ tangent_matrix.block(u_dof, p_dof).vmult(A.block(u_dof), A.block(p_dof)); - // $ - // \mathsf{\mathbf{F}}_{\text{con}} - // = - // \mathsf{\mathbf{F}}_{\mathbf{u}} - // - - // \mathsf{\mathbf{K}}_{\mathbf{u} \widetilde{p}} - // \mathsf{\mathbf{K}}^{-1}_{\widetilde{J} \widetilde{p}} - // [ - // \mathsf{\mathbf{F}}_{\widetilde{J}} - // - - // \mathsf{\mathbf{K}}_{\widetilde{J} \widetilde{J}} - // \mathsf{\mathbf{K}}^{-1}_{\widetilde{p} \widetilde{J}} - // \mathsf{\mathbf{K}}_{\widetilde{p}} - // ] - // $ + // $ + // \mathsf{\mathbf{F}}_{\text{con}} + // = + // \mathsf{\mathbf{F}}_{\mathbf{u}} + // - + // \mathsf{\mathbf{K}}_{\mathbf{u} \widetilde{p}} + // \mathsf{\mathbf{K}}^{-1}_{\widetilde{J} \widetilde{p}} + // [ + // \mathsf{\mathbf{F}}_{\widetilde{J}} + // - + // \mathsf{\mathbf{K}}_{\widetilde{J} \widetilde{J}} + // \mathsf{\mathbf{K}}^{-1}_{\widetilde{p} \widetilde{J}} + // \mathsf{\mathbf{K}}_{\widetilde{p}} + // ] + // $ system_rhs.block(u_dof) -= A.block(u_dof); timer.enter_subsection("Linear solver"); @@ -2919,18 +2920,18 @@ namespace Step44 GrowingVectorMemory > GVM; SolverCG > solver_CG(solver_control, GVM); - // We've chosen by default a SSOR - // preconditioner as it appears to - // provide the fastest solver - // convergence characteristics for this - // problem on a single-thread machine. - // However, for multicore - // computing, the Jacobi preconditioner - // which is multithreaded may converge - // quicker for larger linear systems. + // We've chosen by default a SSOR + // preconditioner as it appears to + // provide the fastest solver + // convergence characteristics for this + // problem on a single-thread machine. + // However, for multicore + // computing, the Jacobi preconditioner + // which is multithreaded may converge + // quicker for larger linear systems. PreconditionSelector, Vector > - preconditioner (parameters.preconditioner_type, - parameters.preconditioner_relaxation); + preconditioner (parameters.preconditioner_type, + parameters.preconditioner_relaxation); preconditioner.use_matrix(tangent_matrix.block(u_dof, u_dof)); solver_CG.solve(tangent_matrix.block(u_dof, u_dof), @@ -2943,9 +2944,9 @@ namespace Step44 } else if (parameters.type_lin == "Direct") { - // Otherwise if the problem is small - // enough, a direct solver can be - // utilised. + // Otherwise if the problem is small + // enough, a direct solver can be + // utilised. SparseDirectUMFPACK A_direct; A_direct.initialize(tangent_matrix.block(u_dof, u_dof)); A_direct.vmult(newton_update.block(u_dof), system_rhs.block(u_dof)); @@ -2959,118 +2960,118 @@ namespace Step44 timer.leave_subsection(); } - // Now that we have the displacement - // update, distribute the constraints - // back to the Newton update: + // Now that we have the displacement + // update, distribute the constraints + // back to the Newton update: constraints.distribute(newton_update); timer.enter_subsection("Linear solver postprocessing"); std::cout << " PP " << std::flush; - // The next step after solving the displacement - // problem is to post-process to get the - // dilatation solution from the - // substitution: - // $ - // d \widetilde{\mathbf{\mathsf{J}}} - // = \mathbf{\mathsf{K}}_{\widetilde{p}\widetilde{J}}^{-1} \bigl[ - // \mathbf{\mathsf{F}}_{\widetilde{p}} - // - \mathbf{\mathsf{K}}_{\widetilde{p}u} d \mathbf{\mathsf{u}} - // \bigr] - // $ - { - // $ - // \mathbf{\mathsf{A}}_{\widetilde{p}} - // = - // \mathbf{\mathsf{K}}_{\widetilde{p}u} d \mathbf{\mathsf{u}} - // $ + // The next step after solving the displacement + // problem is to post-process to get the + // dilatation solution from the + // substitution: + // $ + // d \widetilde{\mathbf{\mathsf{J}}} + // = \mathbf{\mathsf{K}}_{\widetilde{p}\widetilde{J}}^{-1} \bigl[ + // \mathbf{\mathsf{F}}_{\widetilde{p}} + // - \mathbf{\mathsf{K}}_{\widetilde{p}u} d \mathbf{\mathsf{u}} + // \bigr] + // $ + { + // $ + // \mathbf{\mathsf{A}}_{\widetilde{p}} + // = + // \mathbf{\mathsf{K}}_{\widetilde{p}u} d \mathbf{\mathsf{u}} + // $ tangent_matrix.block(p_dof, u_dof).vmult(A.block(p_dof), newton_update.block(u_dof)); - // $ - // \mathbf{\mathsf{A}}_{\widetilde{p}} - // = - // -\mathbf{\mathsf{K}}_{\widetilde{p}u} d \mathbf{\mathsf{u}} - // $ + // $ + // \mathbf{\mathsf{A}}_{\widetilde{p}} + // = + // -\mathbf{\mathsf{K}}_{\widetilde{p}u} d \mathbf{\mathsf{u}} + // $ A.block(p_dof) *= -1.0; - // $ - // \mathbf{\mathsf{A}}_{\widetilde{p}} - // = - // \mathbf{\mathsf{F}}_{\widetilde{p}} - // -\mathbf{\mathsf{K}}_{\widetilde{p}u} d \mathbf{\mathsf{u}} - // $ + // $ + // \mathbf{\mathsf{A}}_{\widetilde{p}} + // = + // \mathbf{\mathsf{F}}_{\widetilde{p}} + // -\mathbf{\mathsf{K}}_{\widetilde{p}u} d \mathbf{\mathsf{u}} + // $ A.block(p_dof) += system_rhs.block(p_dof); - // $ - // d\mathbf{\mathsf{\widetilde{J}}} - // = - // \mathbf{\mathsf{K}}^{-1}_{\widetilde{p}\widetilde{J}} - // [ - // \mathbf{\mathsf{F}}_{\widetilde{p}} - // -\mathbf{\mathsf{K}}_{\widetilde{p}u} d \mathbf{\mathsf{u}} - // ] - // $ + // $ + // d\mathbf{\mathsf{\widetilde{J}}} + // = + // \mathbf{\mathsf{K}}^{-1}_{\widetilde{p}\widetilde{J}} + // [ + // \mathbf{\mathsf{F}}_{\widetilde{p}} + // -\mathbf{\mathsf{K}}_{\widetilde{p}u} d \mathbf{\mathsf{u}} + // ] + // $ tangent_matrix.block(p_dof, J_dof).vmult(newton_update.block(J_dof), A.block(p_dof)); } - // we insure here that any Dirichlet constraints - // are distributed on the updated solution: + // we insure here that any Dirichlet constraints + // are distributed on the updated solution: constraints.distribute(newton_update); - // Finally we solve for the pressure - // update with the substitution: - // $ - // d \widetilde{\mathbf{\mathsf{p}}} - // = - // \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{p}}^{-1} - // \bigl[ - // \mathbf{\mathsf{F}}_{\widetilde{J}} - // - \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{J}} - // d \widetilde{\mathbf{\mathsf{J}}} - // \bigr] - // $ - { - // $ - // \mathsf{\mathbf{A}}_{\widetilde{J}} - // = - // \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{J}} - // d \widetilde{\mathbf{\mathsf{J}}} - // $ + // Finally we solve for the pressure + // update with the substitution: + // $ + // d \widetilde{\mathbf{\mathsf{p}}} + // = + // \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{p}}^{-1} + // \bigl[ + // \mathbf{\mathsf{F}}_{\widetilde{J}} + // - \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{J}} + // d \widetilde{\mathbf{\mathsf{J}}} + // \bigr] + // $ + { + // $ + // \mathsf{\mathbf{A}}_{\widetilde{J}} + // = + // \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{J}} + // d \widetilde{\mathbf{\mathsf{J}}} + // $ tangent_matrix.block(J_dof, J_dof).vmult(A.block(J_dof), newton_update.block(J_dof)); - // $ - // \mathsf{\mathbf{A}}_{\widetilde{J}} - // = - // -\mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{J}} - // d \widetilde{\mathbf{\mathsf{J}}} - // $ + // $ + // \mathsf{\mathbf{A}}_{\widetilde{J}} + // = + // -\mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{J}} + // d \widetilde{\mathbf{\mathsf{J}}} + // $ A.block(J_dof) *= -1.0; - // $ - // \mathsf{\mathbf{A}}_{\widetilde{J}} - // = - // \mathsf{\mathbf{F}}_{\widetilde{J}} - // - - // \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{J}} - // d \widetilde{\mathbf{\mathsf{J}}} - // $ + // $ + // \mathsf{\mathbf{A}}_{\widetilde{J}} + // = + // \mathsf{\mathbf{F}}_{\widetilde{J}} + // - + // \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{J}} + // d \widetilde{\mathbf{\mathsf{J}}} + // $ A.block(J_dof) += system_rhs.block(J_dof); - // and finally.... - // $ - // d \widetilde{\mathbf{\mathsf{p}}} - // = - // \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{p}}^{-1} - // \bigl[ - // \mathbf{\mathsf{F}}_{\widetilde{J}} - // - \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{J}} - // d \widetilde{\mathbf{\mathsf{J}}} - // \bigr] - // $ + // and finally.... + // $ + // d \widetilde{\mathbf{\mathsf{p}}} + // = + // \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{p}}^{-1} + // \bigl[ + // \mathbf{\mathsf{F}}_{\widetilde{J}} + // - \mathbf{\mathsf{K}}_{\widetilde{J}\widetilde{J}} + // d \widetilde{\mathbf{\mathsf{J}}} + // \bigr] + // $ tangent_matrix.block(p_dof, J_dof).Tvmult(newton_update.block(p_dof), A.block(J_dof)); } - // We are now at the end, so we distribute all - // constrained dofs back to the Newton - // update: + // We are now at the end, so we distribute all + // constrained dofs back to the Newton + // update: constraints.distribute(newton_update); timer.leave_subsection(); @@ -3085,7 +3086,7 @@ namespace Step44 // dilatation variables are discontinuous, the static condensation (SC) // operation can be done on a per-cell basis and we can produce the inverse of // the block-diagonal $ \mathbf{\mathsf{K}}_{\widetilde{p}\widetilde{J}}$ - // block by inverting the local blocks. We can again + // block by inverting the local blocks. We can again // use TBB to do this since each operation will be independent of one another. // // Using the TBB via the WorkStream class, we assemble the contributions to form @@ -3120,7 +3121,7 @@ namespace Step44 template - void Solid::copy_local_to_global_sc(const PerTaskData_SC & data) + void Solid::copy_local_to_global_sc(const PerTaskData_SC &data) { for (unsigned int i = 0; i < dofs_per_cell; ++i) for (unsigned int j = 0; j < dofs_per_cell; ++j) @@ -3135,132 +3136,132 @@ namespace Step44 // have and reset some data structures: template void - Solid::assemble_sc_one_cell(const typename DoFHandler::active_cell_iterator & cell, - ScratchData_SC & scratch, - PerTaskData_SC & data) + Solid::assemble_sc_one_cell(const typename DoFHandler::active_cell_iterator &cell, + ScratchData_SC &scratch, + PerTaskData_SC &data) { data.reset(); scratch.reset(); cell->get_dof_indices(data.local_dof_indices); - // We now extract the contribution of - // the dofs associated with the current cell - // to the global stiffness matrix. - // The discontinuous nature of the $\widetilde{p}$ - // and $\widetilde{J}$ - // interpolations mean that their is no - // coupling of the local contributions at the - // global level. This is not the case with the u dof. - // In other words, - // $\mathsf{\mathbf{k}}_{\widetilde{J} \widetilde{p}}$, - // $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{p}}$ - // and - // $\mathsf{\mathbf{k}}_{\widetilde{J} \widetilde{p}}$, - // when extracted - // from the global stiffness matrix are the element - // contributions. - // This is not the case for - // $\mathsf{\mathbf{k}}_{\mathbf{u} \mathbf{u}}$ - // - // Note: a lower-case symbol is used to denote - // element stiffness matrices. - - // Currently the matrix corresponding to - // the dof associated with the current element - // (denoted somewhat loosely as $\mathsf{\mathbf{k}}$) - // is of the form: - // @f{align*} - // \begin{bmatrix} - // \mathbf{\mathsf{k}}_{uu} & \mathbf{\mathsf{k}}_{u\widetilde{p}} & \mathbf{0} - // \\ \mathbf{\mathsf{k}}_{\widetilde{p}u} & \mathbf{0} & \mathbf{\mathsf{k}}_{\widetilde{p}\widetilde{J}} - // \\ \mathbf{0} & \mathbf{\mathsf{k}}_{\widetilde{J}\widetilde{p}} & \mathbf{\mathsf{k}}_{\widetilde{J}\widetilde{J}} - // \end{bmatrix} - // @f} - // - // We now need to modify it such that it appear as - // @f{align*} - // \begin{bmatrix} - // \mathbf{\mathsf{k}}_{\textrm{con}} & \mathbf{\mathsf{k}}_{u\widetilde{p}} & \mathbf{0} - // \\ \mathbf{\mathsf{k}}_{\widetilde{p}u} & \mathbf{0} & \mathbf{\mathsf{k}}_{\widetilde{p}\widetilde{J}}^{-1} - // \\ \mathbf{0} & \mathbf{\mathsf{k}}_{\widetilde{J}\widetilde{p}} & \mathbf{\mathsf{k}}_{\widetilde{J}\widetilde{J}} - // \end{bmatrix} - // @f} - // with $\mathbf{\mathsf{k}}_{\textrm{con}} = \bigl[ \mathbf{\mathsf{k}}_{uu} +\overline{\overline{\mathbf{\mathsf{k}}}}~ \bigr]$ - // where - // $ \overline{\overline{\mathbf{\mathsf{k}}}} := - // \mathbf{\mathsf{k}}_{u\widetilde{p}} \overline{\mathbf{\mathsf{k}}} \mathbf{\mathsf{k}}_{\widetilde{p}u} - // $ - // and - // $ - // \overline{\mathbf{\mathsf{k}}} = - // \mathbf{\mathsf{k}}_{\widetilde{J}\widetilde{p}}^{-1} \mathbf{\mathsf{k}}_{\widetilde{J}\widetilde{J}} - // \mathbf{\mathsf{k}}_{\widetilde{p}\widetilde{J}}^{-1} - // $. - // - // At this point, we need to take note of - // the fact that global data already exists - // in the $\mathsf{\mathbf{K}}_{uu}$, - // $\mathsf{\mathbf{K}}_{\widetilde{p} \widetilde{J}}$ - // and - // $\mathsf{\mathbf{K}}_{\widetilde{J} \widetilde{p}}$ - // sub-blocks. So - // if we are to modify them, we must - // account for the data that is already - // there (i.e. simply add to it or remove - // it if necessary). Since the - // copy_local_to_global operation is a "+=" - // operation, we need to take this into - // account - // - // For the $\mathsf{\mathbf{K}}_{uu}$ block in particular, this - // means that contributions have been added - // from the surrounding cells, so we need - // to be careful when we manipulate this - // block. We can't just erase the - // sub-blocks. - // - // This is the strategy we will employ to - // get the sub-blocks we want: - // - // - $ {\mathbf{\mathsf{k}}}_{\textrm{store}}$: - // Since we don't have access to $\mathsf{\mathbf{k}}_{uu}$, - // but we know its contribution is added to - // the global $\mathsf{\mathbf{K}}_{uu}$ matrix, we just want - // to add the element wise - // static-condensation $\overline{\overline{\mathbf{\mathsf{k}}}}$. - // - // - $\mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}}$: - // Similarly, $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{J}}$ exists in - // the subblock. Since the copy - // operation is a += operation, we - // need to subtract the existing - // $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{J}}$ - // submatrix in addition to - // "adding" that which we wish to - // replace it with. - // - // - $\mathsf{\mathbf{k}}^{-1}_{\widetilde{J} \widetilde{p}}$: - // Since the global matrix - // is symmetric, this block is the - // same as the one above and we - // can simply use - // $\mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}}$ - // as a substitute for this one. - // - // We first extract element data from the - // system matrix. So first we get the - // entire subblock for the cell, then - // extract $\mathsf{\mathbf{k}}$ - // for the dofs associated with - // the current element + // We now extract the contribution of + // the dofs associated with the current cell + // to the global stiffness matrix. + // The discontinuous nature of the $\widetilde{p}$ + // and $\widetilde{J}$ + // interpolations mean that their is no + // coupling of the local contributions at the + // global level. This is not the case with the u dof. + // In other words, + // $\mathsf{\mathbf{k}}_{\widetilde{J} \widetilde{p}}$, + // $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{p}}$ + // and + // $\mathsf{\mathbf{k}}_{\widetilde{J} \widetilde{p}}$, + // when extracted + // from the global stiffness matrix are the element + // contributions. + // This is not the case for + // $\mathsf{\mathbf{k}}_{\mathbf{u} \mathbf{u}}$ + // + // Note: a lower-case symbol is used to denote + // element stiffness matrices. + + // Currently the matrix corresponding to + // the dof associated with the current element + // (denoted somewhat loosely as $\mathsf{\mathbf{k}}$) + // is of the form: + // @f{align*} + // \begin{bmatrix} + // \mathbf{\mathsf{k}}_{uu} & \mathbf{\mathsf{k}}_{u\widetilde{p}} & \mathbf{0} + // \\ \mathbf{\mathsf{k}}_{\widetilde{p}u} & \mathbf{0} & \mathbf{\mathsf{k}}_{\widetilde{p}\widetilde{J}} + // \\ \mathbf{0} & \mathbf{\mathsf{k}}_{\widetilde{J}\widetilde{p}} & \mathbf{\mathsf{k}}_{\widetilde{J}\widetilde{J}} + // \end{bmatrix} + // @f} + // + // We now need to modify it such that it appear as + // @f{align*} + // \begin{bmatrix} + // \mathbf{\mathsf{k}}_{\textrm{con}} & \mathbf{\mathsf{k}}_{u\widetilde{p}} & \mathbf{0} + // \\ \mathbf{\mathsf{k}}_{\widetilde{p}u} & \mathbf{0} & \mathbf{\mathsf{k}}_{\widetilde{p}\widetilde{J}}^{-1} + // \\ \mathbf{0} & \mathbf{\mathsf{k}}_{\widetilde{J}\widetilde{p}} & \mathbf{\mathsf{k}}_{\widetilde{J}\widetilde{J}} + // \end{bmatrix} + // @f} + // with $\mathbf{\mathsf{k}}_{\textrm{con}} = \bigl[ \mathbf{\mathsf{k}}_{uu} +\overline{\overline{\mathbf{\mathsf{k}}}}~ \bigr]$ + // where + // $ \overline{\overline{\mathbf{\mathsf{k}}}} := + // \mathbf{\mathsf{k}}_{u\widetilde{p}} \overline{\mathbf{\mathsf{k}}} \mathbf{\mathsf{k}}_{\widetilde{p}u} + // $ + // and + // $ + // \overline{\mathbf{\mathsf{k}}} = + // \mathbf{\mathsf{k}}_{\widetilde{J}\widetilde{p}}^{-1} \mathbf{\mathsf{k}}_{\widetilde{J}\widetilde{J}} + // \mathbf{\mathsf{k}}_{\widetilde{p}\widetilde{J}}^{-1} + // $. + // + // At this point, we need to take note of + // the fact that global data already exists + // in the $\mathsf{\mathbf{K}}_{uu}$, + // $\mathsf{\mathbf{K}}_{\widetilde{p} \widetilde{J}}$ + // and + // $\mathsf{\mathbf{K}}_{\widetilde{J} \widetilde{p}}$ + // sub-blocks. So + // if we are to modify them, we must + // account for the data that is already + // there (i.e. simply add to it or remove + // it if necessary). Since the + // copy_local_to_global operation is a "+=" + // operation, we need to take this into + // account + // + // For the $\mathsf{\mathbf{K}}_{uu}$ block in particular, this + // means that contributions have been added + // from the surrounding cells, so we need + // to be careful when we manipulate this + // block. We can't just erase the + // sub-blocks. + // + // This is the strategy we will employ to + // get the sub-blocks we want: + // + // - $ {\mathbf{\mathsf{k}}}_{\textrm{store}}$: + // Since we don't have access to $\mathsf{\mathbf{k}}_{uu}$, + // but we know its contribution is added to + // the global $\mathsf{\mathbf{K}}_{uu}$ matrix, we just want + // to add the element wise + // static-condensation $\overline{\overline{\mathbf{\mathsf{k}}}}$. + // + // - $\mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}}$: + // Similarly, $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{J}}$ exists in + // the subblock. Since the copy + // operation is a += operation, we + // need to subtract the existing + // $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{J}}$ + // submatrix in addition to + // "adding" that which we wish to + // replace it with. + // + // - $\mathsf{\mathbf{k}}^{-1}_{\widetilde{J} \widetilde{p}}$: + // Since the global matrix + // is symmetric, this block is the + // same as the one above and we + // can simply use + // $\mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}}$ + // as a substitute for this one. + // + // We first extract element data from the + // system matrix. So first we get the + // entire subblock for the cell, then + // extract $\mathsf{\mathbf{k}}$ + // for the dofs associated with + // the current element data.k_orig.extract_submatrix_from(tangent_matrix, data.local_dof_indices, data.local_dof_indices); - // and next the local matrices for - // $\mathsf{\mathbf{k}}_{ \widetilde{p} \mathbf{u}}$ - // $\mathsf{\mathbf{k}}_{ \widetilde{p} \widetilde{J}}$ - // and - // $\mathsf{\mathbf{k}}_{ \widetilde{J} \widetilde{J}}$: + // and next the local matrices for + // $\mathsf{\mathbf{k}}_{ \widetilde{p} \mathbf{u}}$ + // $\mathsf{\mathbf{k}}_{ \widetilde{p} \widetilde{J}}$ + // and + // $\mathsf{\mathbf{k}}_{ \widetilde{J} \widetilde{J}}$: data.k_pu.extract_submatrix_from(data.k_orig, element_indices_p, element_indices_u); @@ -3271,63 +3272,63 @@ namespace Step44 element_indices_J, element_indices_J); - // To get the inverse of - // $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{J}}$, - // we invert it - // directly. This operation is relatively - // inexpensive since $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{J}}$ - // since block-diagonal. + // To get the inverse of + // $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{J}}$, + // we invert it + // directly. This operation is relatively + // inexpensive since $\mathsf{\mathbf{k}}_{\widetilde{p} \widetilde{J}}$ + // since block-diagonal. data.k_pJ_inv.invert(data.k_pJ); - // Now we can make condensation terms to - // add to the $\mathsf{\mathbf{k}}_{\mathbf{u} \mathbf{u}}$ - // block and put them in - // the cell local matrix - // $ - // \mathsf{\mathbf{A}} - // = - // \mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}} - // \mathsf{\mathbf{k}}_{\widetilde{p} \mathbf{u}} - // $: + // Now we can make condensation terms to + // add to the $\mathsf{\mathbf{k}}_{\mathbf{u} \mathbf{u}}$ + // block and put them in + // the cell local matrix + // $ + // \mathsf{\mathbf{A}} + // = + // \mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}} + // \mathsf{\mathbf{k}}_{\widetilde{p} \mathbf{u}} + // $: data.k_pJ_inv.mmult(data.A, data.k_pu); - // $ - // \mathsf{\mathbf{B}} - // = - // \mathsf{\mathbf{k}}^{-1}_{\widetilde{J} \widetilde{J}} - // \mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}} - // \mathsf{\mathbf{k}}_{\widetilde{p} \mathbf{u}} - // $ + // $ + // \mathsf{\mathbf{B}} + // = + // \mathsf{\mathbf{k}}^{-1}_{\widetilde{J} \widetilde{J}} + // \mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}} + // \mathsf{\mathbf{k}}_{\widetilde{p} \mathbf{u}} + // $ data.k_JJ.mmult(data.B, data.A); - // $ - // \mathsf{\mathbf{C}} - // = - // \mathsf{\mathbf{k}}^{-1}_{\widetilde{J} \widetilde{p}} - // \mathsf{\mathbf{k}}^{-1}_{\widetilde{J} \widetilde{J}} - // \mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}} - // \mathsf{\mathbf{k}}_{\widetilde{p} \mathbf{u}} - // $ + // $ + // \mathsf{\mathbf{C}} + // = + // \mathsf{\mathbf{k}}^{-1}_{\widetilde{J} \widetilde{p}} + // \mathsf{\mathbf{k}}^{-1}_{\widetilde{J} \widetilde{J}} + // \mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}} + // \mathsf{\mathbf{k}}_{\widetilde{p} \mathbf{u}} + // $ data.k_pJ_inv.Tmmult(data.C, data.B); - // $ - // \overline{\overline{\mathsf{\mathbf{k}}}} - // = - // \mathsf{\mathbf{k}}_{\mathbf{u} \widetilde{p}} - // \mathsf{\mathbf{k}}^{-1}_{\widetilde{J} \widetilde{p}} - // \mathsf{\mathbf{k}}^{-1}_{\widetilde{J} \widetilde{J}} - // \mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}} - // \mathsf{\mathbf{k}}_{\widetilde{p} \mathbf{u}} - // $ + // $ + // \overline{\overline{\mathsf{\mathbf{k}}}} + // = + // \mathsf{\mathbf{k}}_{\mathbf{u} \widetilde{p}} + // \mathsf{\mathbf{k}}^{-1}_{\widetilde{J} \widetilde{p}} + // \mathsf{\mathbf{k}}^{-1}_{\widetilde{J} \widetilde{J}} + // \mathsf{\mathbf{k}}^{-1}_{\widetilde{p} \widetilde{J}} + // \mathsf{\mathbf{k}}_{\widetilde{p} \mathbf{u}} + // $ data.k_pu.Tmmult(data.k_bbar, data.C); data.k_bbar.scatter_matrix_to(element_indices_u, element_indices_u, data.cell_matrix); - // Next we place - // $\mathsf{\mathbf{k}}^{-1}_{ \widetilde{p} \widetilde{J}}$ - // in the - // $\mathsf{\mathbf{k}}_{ \widetilde{p} \widetilde{J}}$ - // block for post-processing. Note again - // that we need to remove the - // contribution that already exists there. + // Next we place + // $\mathsf{\mathbf{k}}^{-1}_{ \widetilde{p} \widetilde{J}}$ + // in the + // $\mathsf{\mathbf{k}}_{ \widetilde{p} \widetilde{J}}$ + // block for post-processing. Note again + // that we need to remove the + // contribution that already exists there. data.k_pJ_inv.add(-1.0, data.k_pJ); data.k_pJ_inv.scatter_matrix_to(element_indices_p, element_indices_J, @@ -3343,8 +3344,8 @@ namespace Step44 { DataOut data_out; std::vector - data_component_interpretation(dim, - DataComponentInterpretation::component_is_part_of_vector); + data_component_interpretation(dim, + DataComponentInterpretation::component_is_part_of_vector); data_component_interpretation.push_back(DataComponentInterpretation::component_is_scalar); data_component_interpretation.push_back(DataComponentInterpretation::component_is_scalar); @@ -3358,22 +3359,22 @@ namespace Step44 DataOut::type_dof_data, data_component_interpretation); - // Since we are dealing with a large - // deformation problem, it would be nice - // to display the result on a displaced - // grid! The MappingQEulerian class - // linked with the DataOut class provides - // an interface through which this can be - // achieved without physically moving the - // grid points in the Triangulation - // object ourselves. We first need to - // copy the solution to a temporary - // vector and then create the Eulerian - // mapping. We also specify the - // polynomial degree to the DataOut - // object in order to produce a more - // refined output data set when higher - // order polynomials are used. + // Since we are dealing with a large + // deformation problem, it would be nice + // to display the result on a displaced + // grid! The MappingQEulerian class + // linked with the DataOut class provides + // an interface through which this can be + // achieved without physically moving the + // grid points in the Triangulation + // object ourselves. We first need to + // copy the solution to a temporary + // vector and then create the Eulerian + // mapping. We also specify the + // polynomial degree to the DataOut + // object in order to produce a more + // refined output data set when higher + // order polynomials are used. Vector soln(solution_n.size()); for (unsigned int i = 0; i < soln.size(); ++i) soln(i) = solution_n(i); diff --git a/deal.II/examples/step-45/step-45.cc b/deal.II/examples/step-45/step-45.cc index 1374b9708a..44c679d977 100644 --- a/deal.II/examples/step-45/step-45.cc +++ b/deal.II/examples/step-45/step-45.cc @@ -10,12 +10,12 @@ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // The include files are already known. The - // one critical for the current program is - // the one that contains the ConstraintMatrix - // in the lac/ directory: +// The include files are already known. The +// one critical for the current program is +// the one that contains the ConstraintMatrix +// in the lac/ directory: #include #include @@ -47,74 +47,74 @@ namespace Step45 { using namespace dealii; - // @sect3{The LaplaceProblem class} - - // The class LaplaceProblem is - // the main class of this problem. As - // mentioned in the introduction, it is - // fashioned after the corresponding class in - // step-3. Correspondingly, the documentation - // from that tutorial program applies here as - // well. The only new member variable is the - // constraints variables that - // will hold the constraints from the - // periodic boundary condition. We will - // initialize it in the - // make_periodicity_constraints() - // function which we call from - // make_grid_and_dofs(). + // @sect3{The LaplaceProblem class} + + // The class LaplaceProblem is + // the main class of this problem. As + // mentioned in the introduction, it is + // fashioned after the corresponding class in + // step-3. Correspondingly, the documentation + // from that tutorial program applies here as + // well. The only new member variable is the + // constraints variables that + // will hold the constraints from the + // periodic boundary condition. We will + // initialize it in the + // make_periodicity_constraints() + // function which we call from + // make_grid_and_dofs(). class LaplaceProblem { - public: - LaplaceProblem (); - void run (); + public: + LaplaceProblem (); + void run (); - private: - Triangulation<2> triangulation; + private: + Triangulation<2> triangulation; - FE_Q<2> fe; - DoFHandler<2> dof_handler; + FE_Q<2> fe; + DoFHandler<2> dof_handler; - ConstraintMatrix constraints; + ConstraintMatrix constraints; - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - Vector system_rhs; - Vector solution; + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + Vector system_rhs; + Vector solution; - void assemble_system (); - void output_results (); - void make_grid_and_dofs (); - void make_periodicity_constraints (); - void solve (); + void assemble_system (); + void output_results (); + void make_grid_and_dofs (); + void make_periodicity_constraints (); + void solve (); }; - // @sect3{The RightHandSide class} + // @sect3{The RightHandSide class} - // The following implements the right hand - // side function discussed in the - // introduction. Its implementation is - // obvious given what has been shown in - // step-4 before: + // The following implements the right hand + // side function discussed in the + // introduction. Its implementation is + // obvious given what has been shown in + // step-4 before: class RightHandSide: public Function<2> { - public: - RightHandSide (); + public: + RightHandSide (); - virtual double value (const Point<2>& p, - const unsigned int component = 0) const; + virtual double value (const Point<2> &p, + const unsigned int component = 0) const; }; RightHandSide::RightHandSide () - : - Function<2> () + : + Function<2> () {} double - RightHandSide::value (const Point<2>&p, + RightHandSide::value (const Point<2> &p, const unsigned int) const { return (std::cos (2 * numbers::PI * p(0)) * @@ -123,38 +123,38 @@ namespace Step45 std::exp (- 2 * p(1))); } - // @sect3{Implementation of the LaplaceProblem class} + // @sect3{Implementation of the LaplaceProblem class} - // The first part of implementing the main - // class is the constructor. It is unchanged - // from step-3 and step-4: + // The first part of implementing the main + // class is the constructor. It is unchanged + // from step-3 and step-4: LaplaceProblem::LaplaceProblem () - : - fe (1), - dof_handler (triangulation) + : + fe (1), + dof_handler (triangulation) {} - // @sect4{LaplaceProblem::make_grid_and_dofs} - - // The following is the first function to be - // called in run(). It sets up - // the mesh and degrees of freedom. - // - // We start by creating the usual square mesh - // and changing the boundary indicator on the - // parts of the boundary where we have - // Dirichlet boundary conditions (top and - // bottom, i.e. faces two and three of the - // reference cell as defined by - // GeometryInfo), so that we can distinguish - // between the parts of the boundary where - // periodic and where Dirichlet boundary - // conditions hold. We then refine the mesh a - // fixed number of times, with child faces - // inheriting the boundary indicators - // previously set on the coarse mesh from - // their parents. + // @sect4{LaplaceProblem::make_grid_and_dofs} + + // The following is the first function to be + // called in run(). It sets up + // the mesh and degrees of freedom. + // + // We start by creating the usual square mesh + // and changing the boundary indicator on the + // parts of the boundary where we have + // Dirichlet boundary conditions (top and + // bottom, i.e. faces two and three of the + // reference cell as defined by + // GeometryInfo), so that we can distinguish + // between the parts of the boundary where + // periodic and where Dirichlet boundary + // conditions hold. We then refine the mesh a + // fixed number of times, with child faces + // inheriting the boundary indicators + // previously set on the coarse mesh from + // their parents. void LaplaceProblem::make_grid_and_dofs () { GridGenerator::hyper_cube (triangulation); @@ -162,9 +162,9 @@ namespace Step45 triangulation.begin_active ()->face (3)->set_boundary_indicator (1); triangulation.refine_global (5); - // The next step is to distribute the - // degrees of freedom and produce a little - // bit of graphical output: + // The next step is to distribute the + // degrees of freedom and produce a little + // bit of graphical output: dof_handler.distribute_dofs (fe); std::cout << "Number of active cells: " << triangulation.n_active_cells () @@ -172,31 +172,31 @@ namespace Step45 << "Degrees of freedom: " << dof_handler.n_dofs () << std::endl; - // Now it is the time for the constraints - // that come from the periodicity - // constraints. We do this in the - // following, separate function, after - // clearing any possible prior content from - // the constraints object: + // Now it is the time for the constraints + // that come from the periodicity + // constraints. We do this in the + // following, separate function, after + // clearing any possible prior content from + // the constraints object: constraints.clear (); make_periodicity_constraints (); - // We also incorporate the homogeneous - // Dirichlet boundary conditions on the - // upper and lower parts of the boundary - // (i.e. the ones with boundary indicator - // 1) and close the - // ConstraintMatrix object: + // We also incorporate the homogeneous + // Dirichlet boundary conditions on the + // upper and lower parts of the boundary + // (i.e. the ones with boundary indicator + // 1) and close the + // ConstraintMatrix object: VectorTools::interpolate_boundary_values (dof_handler, 1, ZeroFunction<2> (), constraints); constraints.close (); - // Then we create the sparsity pattern and - // the system matrix and initialize the - // solution and right-hand side - // vectors. This is again as in step-3 or - // step-6, for example: + // Then we create the sparsity pattern and + // the system matrix and initialize the + // solution and right-hand side + // vectors. This is again as in step-3 or + // step-6, for example: CompressedSparsityPattern c_sparsity_pattern (dof_handler.n_dofs(), dof_handler.n_dofs()); DoFTools::make_sparsity_pattern (dof_handler, @@ -213,51 +213,51 @@ namespace Step45 - // @sect4{LaplaceProblem::make_periodicity_constraints} - - // This is the function that provides the new - // material of this tutorial program. The - // general outline of the algorithm is as - // follows: we first loop over all the - // degrees of freedom on the right boundary - // and record their $y$-locations in a map - // together with their global indices. Then - // we go along the left boundary, find - // matching $y$-locations for each degree of - // freedom, and then add constraints that - // identify these matched degrees of freedom. - // - // In this function, we make use of the fact - // that we have a scalar element (i.e. the - // only valid vector component that can be - // passed to DoFAccessor::vertex_dof_index is - // zero) and that we have a $Q_1$ element for - // which all degrees of freedom live in the - // vertices of the cell. Furthermore, we have - // assumed that we are in 2d and that meshes - // were not refined adaptively — the - // latter assumption would imply that there - // may be vertices that aren't matched - // one-to-one and for which we won't be able - // to compute constraints this easily. We - // will discuss in the "outlook" part of the - // results section below other strategies to - // write the current function that can work - // in cases like this as well. + // @sect4{LaplaceProblem::make_periodicity_constraints} + + // This is the function that provides the new + // material of this tutorial program. The + // general outline of the algorithm is as + // follows: we first loop over all the + // degrees of freedom on the right boundary + // and record their $y$-locations in a map + // together with their global indices. Then + // we go along the left boundary, find + // matching $y$-locations for each degree of + // freedom, and then add constraints that + // identify these matched degrees of freedom. + // + // In this function, we make use of the fact + // that we have a scalar element (i.e. the + // only valid vector component that can be + // passed to DoFAccessor::vertex_dof_index is + // zero) and that we have a $Q_1$ element for + // which all degrees of freedom live in the + // vertices of the cell. Furthermore, we have + // assumed that we are in 2d and that meshes + // were not refined adaptively — the + // latter assumption would imply that there + // may be vertices that aren't matched + // one-to-one and for which we won't be able + // to compute constraints this easily. We + // will discuss in the "outlook" part of the + // results section below other strategies to + // write the current function that can work + // in cases like this as well. void LaplaceProblem::make_periodicity_constraints () { - // To start with the actual implementation, - // we loop over all active cells and check - // whether the cell is located at the right - // boundary (i.e. face 1 — the one at - // the right end of the cell — is at - // the boundary). If that is so, then we - // use that for the currently used finite - // element, each degree of freedom of the - // face is located on one vertex, and store - // their $y$-coordinate along with the - // global number of this degree of freedom - // in the following map: + // To start with the actual implementation, + // we loop over all active cells and check + // whether the cell is located at the right + // boundary (i.e. face 1 — the one at + // the right end of the cell — is at + // the boundary). If that is so, then we + // use that for the currently used finite + // element, each degree of freedom of the + // face is located on one vertex, and store + // their $y$-coordinate along with the + // global number of this degree of freedom + // in the following map: std::map dof_locations; for (DoFHandler<2>::active_cell_iterator cell = dof_handler.begin_active (); @@ -271,79 +271,79 @@ namespace Step45 dof_locations[cell->face(1)->vertex_dof_index(1, 0)] = cell->face(1)->vertex(1)[1]; } - // Note that in the above block, we add - // vertices zero and one of the affected - // face to the map. This means that we will - // add each vertex twice, once from each of - // the two adjacent cells (unless the - // vertex is a corner of the domain). Since - // the coordinates of the vertex are the - // same both times of course, there is no - // harm: we replace one value in the map - // with itself the second time we visit an - // entry. - // - // The same will be true below where we add - // the same constraint twice to the - // ConstraintMatrix — again, we will - // overwrite the constraint with itself, - // and no harm is done. - - // Now we have to find the corresponding - // degrees of freedom on the left part of - // the boundary. Therefore we loop over all - // cells again and choose the ones where - // face 0 is at the boundary: + // Note that in the above block, we add + // vertices zero and one of the affected + // face to the map. This means that we will + // add each vertex twice, once from each of + // the two adjacent cells (unless the + // vertex is a corner of the domain). Since + // the coordinates of the vertex are the + // same both times of course, there is no + // harm: we replace one value in the map + // with itself the second time we visit an + // entry. + // + // The same will be true below where we add + // the same constraint twice to the + // ConstraintMatrix — again, we will + // overwrite the constraint with itself, + // and no harm is done. + + // Now we have to find the corresponding + // degrees of freedom on the left part of + // the boundary. Therefore we loop over all + // cells again and choose the ones where + // face 0 is at the boundary: for (DoFHandler<2>::active_cell_iterator cell = dof_handler.begin_active (); cell != dof_handler.end (); ++cell) if (cell->at_boundary () && cell->face (0)->at_boundary ()) { - // Every degree of freedom on this - // face needs to have a corresponding - // one on the right side of the face, - // and our goal is to add a - // constraint for the one on the left - // in terms of the one on the - // right. To this end we first add a - // new line to the constraint matrix - // for this one degree of - // freedom. Then we identify it with - // the corresponding degree of - // freedom on the right part of the - // boundary by constraining the - // degree of freedom on the left with - // the one on the right times a - // weight of 1.0. - // - // Consequently, we loop over the two - // vertices of each face we find and - // then loop over all the - // $y$-locations we've previously - // recorded to find which degree of - // freedom on the right boundary - // corresponds to the one we - // currently look at. Note that we - // have entered these into a map, and - // when looping over the iterators - // p of this map, - // p-@>first corresponds - // to the "key" of an entry (the - // global number of the degree of - // freedom), whereas - // p-@>second is the - // "value" (the $y$-location we have - // entered above). - // - // We are quite sure here that we - // should be finding such a - // corresponding degree of - // freedom. However, sometimes stuff - // happens and so the bottom of the - // block contains an assertion that - // our assumption was indeed correct - // and that a vertex was found. + // Every degree of freedom on this + // face needs to have a corresponding + // one on the right side of the face, + // and our goal is to add a + // constraint for the one on the left + // in terms of the one on the + // right. To this end we first add a + // new line to the constraint matrix + // for this one degree of + // freedom. Then we identify it with + // the corresponding degree of + // freedom on the right part of the + // boundary by constraining the + // degree of freedom on the left with + // the one on the right times a + // weight of 1.0. + // + // Consequently, we loop over the two + // vertices of each face we find and + // then loop over all the + // $y$-locations we've previously + // recorded to find which degree of + // freedom on the right boundary + // corresponds to the one we + // currently look at. Note that we + // have entered these into a map, and + // when looping over the iterators + // p of this map, + // p-@>first corresponds + // to the "key" of an entry (the + // global number of the degree of + // freedom), whereas + // p-@>second is the + // "value" (the $y$-location we have + // entered above). + // + // We are quite sure here that we + // should be finding such a + // corresponding degree of + // freedom. However, sometimes stuff + // happens and so the bottom of the + // block contains an assertion that + // our assumption was indeed correct + // and that a vertex was found. for (unsigned int face_vertex = 0; face_vertex<2; ++face_vertex) { constraints.add_line (cell->face(0)->vertex_dof_index (face_vertex, 0)); @@ -364,23 +364,23 @@ namespace Step45 - // @sect4{LaplaceProblem::assemble_system} - - // Assembling the system matrix and the - // right-hand side vector is done as in other - // tutorials before. - // - // The only difference here is that we don't - // copy elements from local contributions - // into the global matrix and later fix up - // constrained degrees of freedom, but that - // we let the ConstraintMatrix do this job in - // one swoop for us using the - // ConstraintMatrix::distribute_local_to_global - // function(). This was previously already - // demonstrated in step-16, step-22, for - // example, along with a discussion in the - // introduction of step-27. + // @sect4{LaplaceProblem::assemble_system} + + // Assembling the system matrix and the + // right-hand side vector is done as in other + // tutorials before. + // + // The only difference here is that we don't + // copy elements from local contributions + // into the global matrix and later fix up + // constrained degrees of freedom, but that + // we let the ConstraintMatrix do this job in + // one swoop for us using the + // ConstraintMatrix::distribute_local_to_global + // function(). This was previously already + // demonstrated in step-16, step-22, for + // example, along with a discussion in the + // introduction of step-27. void LaplaceProblem::assemble_system () { QGauss<2> quadrature_formula(2); @@ -427,16 +427,16 @@ namespace Step45 } - // @sect4{LaplaceProblem::solve} + // @sect4{LaplaceProblem::solve} - // To solve the linear system of equations - // $Au=b$ we use the CG solver with an - // SSOR-preconditioner. This is, again, - // copied almost verbatim from step-6. As in - // step-6, we need to make sure that - // constrained degrees of freedom get their - // correct values after solving by calling - // the ConstraintMatrix::distribute function: + // To solve the linear system of equations + // $Au=b$ we use the CG solver with an + // SSOR-preconditioner. This is, again, + // copied almost verbatim from step-6. As in + // step-6, we need to make sure that + // constrained degrees of freedom get their + // correct values after solving by calling + // the ConstraintMatrix::distribute function: void LaplaceProblem::solve () { SolverControl solver_control (dof_handler.n_dofs (), 1e-12); @@ -451,11 +451,11 @@ namespace Step45 } - // @sect4{LaplaceProblem::output_results} + // @sect4{LaplaceProblem::output_results} - // This is another function copied from - // previous tutorial programs. It generates - // graphical output in VTK format: + // This is another function copied from + // previous tutorial programs. It generates + // graphical output in VTK format: void LaplaceProblem::output_results () { DataOut<2> data_out; @@ -471,10 +471,10 @@ namespace Step45 - // @sect4{LaplaceProblem::run} + // @sect4{LaplaceProblem::run} - // And another function copied from previous - // programs: + // And another function copied from previous + // programs: void LaplaceProblem::run () { make_grid_and_dofs(); @@ -484,10 +484,10 @@ namespace Step45 } } - // @sect3{The main function} +// @sect3{The main function} - // And at the end we have the main function - // as usual, this time copied from step-6: +// And at the end we have the main function +// as usual, this time copied from step-6: int main () { try diff --git a/deal.II/examples/step-46/step-46.cc b/deal.II/examples/step-46/step-46.cc index 7ecc7c5e84..26dfa0132f 100644 --- a/deal.II/examples/step-46/step-46.cc +++ b/deal.II/examples/step-46/step-46.cc @@ -10,14 +10,14 @@ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // The include files for this program are the - // same as for many others before. The only - // new one is the one that declares - // FE_Nothing as discussed in the - // introduction. The ones in the hp directory - // have already been discussed in step-27. +// The include files for this program are the +// same as for many others before. The only +// new one is the one that declares +// FE_Nothing as discussed in the +// introduction. The ones in the hp directory +// have already been discussed in step-27. #include #include @@ -60,123 +60,123 @@ namespace Step46 { using namespace dealii; - // @sect3{The FluidStructureProblem class template} - - // This is the main class. It is, if you - // want, a combination of step-8 and step-22 - // in that it has member variables that - // either address the global problem (the - // Triangulation and hp::DoFHandler objects, - // as well as the hp::FECollection and - // various linear algebra objects) or that - // pertain to either the elasticity or Stokes - // sub-problems. The general structure of the - // class, however, is like that of most of - // the other programs implementing stationary - // problems. - // - // There are a few helper functions - // (cell_is_in_fluid_domain, - // cell_is_in_solid_domain) of - // self-explanatory nature (operating on the - // symbolic names for the two subdomains that - // will be used as material_ids for cells - // belonging to the subdomains, as explained - // in the introduction) and a few functions - // (make_grid, set_active_fe_indices, - // assemble_interface_terms) that have - // been broken out of other functions that - // can be found in many of the other tutorial - // programs and that will be discussed as we - // get to their implementation. - // - // The final set of variables - // (viscosity, lambda, eta) - // describes the material properties used for - // the two physics models. + // @sect3{The FluidStructureProblem class template} + + // This is the main class. It is, if you + // want, a combination of step-8 and step-22 + // in that it has member variables that + // either address the global problem (the + // Triangulation and hp::DoFHandler objects, + // as well as the hp::FECollection and + // various linear algebra objects) or that + // pertain to either the elasticity or Stokes + // sub-problems. The general structure of the + // class, however, is like that of most of + // the other programs implementing stationary + // problems. + // + // There are a few helper functions + // (cell_is_in_fluid_domain, + // cell_is_in_solid_domain) of + // self-explanatory nature (operating on the + // symbolic names for the two subdomains that + // will be used as material_ids for cells + // belonging to the subdomains, as explained + // in the introduction) and a few functions + // (make_grid, set_active_fe_indices, + // assemble_interface_terms) that have + // been broken out of other functions that + // can be found in many of the other tutorial + // programs and that will be discussed as we + // get to their implementation. + // + // The final set of variables + // (viscosity, lambda, eta) + // describes the material properties used for + // the two physics models. template class FluidStructureProblem { - public: - FluidStructureProblem (const unsigned int stokes_degree, - const unsigned int elasticity_degree); - void run (); + public: + FluidStructureProblem (const unsigned int stokes_degree, + const unsigned int elasticity_degree); + void run (); - private: - enum - { - fluid_domain_id, - solid_domain_id - }; - - static bool - cell_is_in_fluid_domain (const typename hp::DoFHandler::cell_iterator &cell); - - static bool - cell_is_in_solid_domain (const typename hp::DoFHandler::cell_iterator &cell); - - - void make_grid (); - void set_active_fe_indices (); - void setup_dofs (); - void assemble_system (); - void assemble_interface_term (const FEFaceValuesBase &elasticity_fe_face_values, - const FEFaceValuesBase &stokes_fe_face_values, - std::vector > &elasticity_phi, - std::vector > &stokes_symgrad_phi_u, - std::vector &stokes_phi_p, - FullMatrix &local_interface_matrix) const; - void solve (); - void output_results (const unsigned int refinement_cycle) const; - void refine_mesh (); - - const unsigned int stokes_degree; - const unsigned int elasticity_degree; - - Triangulation triangulation; - FESystem stokes_fe; - FESystem elasticity_fe; - hp::FECollection fe_collection; - hp::DoFHandler dof_handler; - - ConstraintMatrix constraints; - - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - - Vector solution; - Vector system_rhs; - - const double viscosity; - const double lambda; - const double mu; + private: + enum + { + fluid_domain_id, + solid_domain_id + }; + + static bool + cell_is_in_fluid_domain (const typename hp::DoFHandler::cell_iterator &cell); + + static bool + cell_is_in_solid_domain (const typename hp::DoFHandler::cell_iterator &cell); + + + void make_grid (); + void set_active_fe_indices (); + void setup_dofs (); + void assemble_system (); + void assemble_interface_term (const FEFaceValuesBase &elasticity_fe_face_values, + const FEFaceValuesBase &stokes_fe_face_values, + std::vector > &elasticity_phi, + std::vector > &stokes_symgrad_phi_u, + std::vector &stokes_phi_p, + FullMatrix &local_interface_matrix) const; + void solve (); + void output_results (const unsigned int refinement_cycle) const; + void refine_mesh (); + + const unsigned int stokes_degree; + const unsigned int elasticity_degree; + + Triangulation triangulation; + FESystem stokes_fe; + FESystem elasticity_fe; + hp::FECollection fe_collection; + hp::DoFHandler dof_handler; + + ConstraintMatrix constraints; + + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + + Vector solution; + Vector system_rhs; + + const double viscosity; + const double lambda; + const double mu; }; - // @sect3{Boundary values and right hand side} + // @sect3{Boundary values and right hand side} - // The following classes do as their names - // suggest. The boundary values for the - // velocity are $\mathbf u=(0, \sin(\pi - // x))^T$ in 2d and $\mathbf u=(0, 0, - // \sin(\pi x)\sin(\pi y))^T$ in 3d, - // respectively. The remaining boundary - // conditions for this problem are all - // homogenous and have been discussed in the - // introduction. The right hand side forcing - // term is zero for both the fluid and the - // solid. + // The following classes do as their names + // suggest. The boundary values for the + // velocity are $\mathbf u=(0, \sin(\pi + // x))^T$ in 2d and $\mathbf u=(0, 0, + // \sin(\pi x)\sin(\pi y))^T$ in 3d, + // respectively. The remaining boundary + // conditions for this problem are all + // homogenous and have been discussed in the + // introduction. The right hand side forcing + // term is zero for both the fluid and the + // solid. template class StokesBoundaryValues : public Function { - public: - StokesBoundaryValues () : Function(dim+1+dim) {} + public: + StokesBoundaryValues () : Function(dim+1+dim) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void vector_value (const Point &p, - Vector &value) const; + virtual void vector_value (const Point &p, + Vector &value) const; }; @@ -191,12 +191,12 @@ namespace Step46 if (component == dim-1) switch (dim) { - case 2: - return std::sin(numbers::PI*p[0]); - case 3: - return std::sin(numbers::PI*p[0]) * std::sin(numbers::PI*p[1]); - default: - Assert (false, ExcNotImplemented()); + case 2: + return std::sin(numbers::PI*p[0]); + case 3: + return std::sin(numbers::PI*p[0]) * std::sin(numbers::PI*p[1]); + default: + Assert (false, ExcNotImplemented()); } return 0; @@ -217,14 +217,14 @@ namespace Step46 template class RightHandSide : public Function { - public: - RightHandSide () : Function(dim+1) {} + public: + RightHandSide () : Function(dim+1) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void vector_value (const Point &p, - Vector &value) const; + virtual void vector_value (const Point &p, + Vector &value) const; }; @@ -249,45 +249,45 @@ namespace Step46 - // @sect3{The FluidStructureProblem implementation} - - // @sect4{Constructors and helper functions} - - // Let's now get to the implementation of the - // primary class of this program. The first - // few functions are the constructor and the - // helper functions that can be used to - // determine which part of the domain a cell - // is in. Given the discussion of these - // topics in the introduction, their - // implementation is rather obvious. In the - // constructor, note that we have to - // construct the hp::FECollection object from - // the base elements for Stokes and - // elasticity; using the - // hp::FECollection::push_back function - // assigns them spots zero and one in this - // collection, an order that we have to - // remember and use consistently in the rest - // of the program. + // @sect3{The FluidStructureProblem implementation} + + // @sect4{Constructors and helper functions} + + // Let's now get to the implementation of the + // primary class of this program. The first + // few functions are the constructor and the + // helper functions that can be used to + // determine which part of the domain a cell + // is in. Given the discussion of these + // topics in the introduction, their + // implementation is rather obvious. In the + // constructor, note that we have to + // construct the hp::FECollection object from + // the base elements for Stokes and + // elasticity; using the + // hp::FECollection::push_back function + // assigns them spots zero and one in this + // collection, an order that we have to + // remember and use consistently in the rest + // of the program. template FluidStructureProblem:: FluidStructureProblem (const unsigned int stokes_degree, const unsigned int elasticity_degree) - : - stokes_degree (stokes_degree), - elasticity_degree (elasticity_degree), - triangulation (Triangulation::maximum_smoothing), - stokes_fe (FE_Q(stokes_degree+1), dim, - FE_Q(stokes_degree), 1, - FE_Nothing(), dim), - elasticity_fe (FE_Nothing(), dim, - FE_Nothing(), 1, - FE_Q(elasticity_degree), dim), - dof_handler (triangulation), - viscosity (2), - lambda (1), - mu (1) + : + stokes_degree (stokes_degree), + elasticity_degree (elasticity_degree), + triangulation (Triangulation::maximum_smoothing), + stokes_fe (FE_Q(stokes_degree+1), dim, + FE_Q(stokes_degree), 1, + FE_Nothing(), dim), + elasticity_fe (FE_Nothing(), dim, + FE_Nothing(), 1, + FE_Q(elasticity_degree), dim), + dof_handler (triangulation), + viscosity (2), + lambda (1), + mu (1) { fe_collection.push_back (stokes_fe); fe_collection.push_back (elasticity_fe); @@ -314,26 +314,26 @@ namespace Step46 } - // @sect4{Meshes and assigning subdomains} - - // The next pair of functions deals with - // generating a mesh and making sure all - // flags that denote subdomains are - // correct. make_grid, as - // discussed in the introduction, generates - // an $8\times 8$ mesh (or an $8\times - // 8\times 8$ mesh in 3d) to make sure that - // each coarse mesh cell is completely within - // one of the subdomains. After generating - // this mesh, we loop over its boundary and - // set the boundary indicator to one at the - // top boundary, the only place where we set - // nonzero Dirichlet boundary - // conditions. After this, we loop again over - // all cells to set the material indicator - // — used to denote which part of the - // domain we are in, to either the fluid or - // solid indicator. + // @sect4{Meshes and assigning subdomains} + + // The next pair of functions deals with + // generating a mesh and making sure all + // flags that denote subdomains are + // correct. make_grid, as + // discussed in the introduction, generates + // an $8\times 8$ mesh (or an $8\times + // 8\times 8$ mesh in 3d) to make sure that + // each coarse mesh cell is completely within + // one of the subdomains. After generating + // this mesh, we loop over its boundary and + // set the boundary indicator to one at the + // top boundary, the only place where we set + // nonzero Dirichlet boundary + // conditions. After this, we loop again over + // all cells to set the material indicator + // — used to denote which part of the + // domain we are in, to either the fluid or + // solid indicator. template void FluidStructureProblem::make_grid () @@ -341,7 +341,7 @@ namespace Step46 GridGenerator::subdivided_hyper_cube (triangulation, 8, -1, 1); for (typename Triangulation::active_cell_iterator - cell = triangulation.begin_active(); + cell = triangulation.begin_active(); cell != triangulation.end(); ++cell) for (unsigned int f=0; f::faces_per_cell; ++f) if (cell->face(f)->at_boundary() @@ -351,7 +351,7 @@ namespace Step46 for (typename Triangulation::active_cell_iterator - cell = dof_handler.begin_active(); + cell = dof_handler.begin_active(); cell != dof_handler.end(); ++cell) if (((std::fabs(cell->center()[0]) < 0.25) && @@ -366,30 +366,30 @@ namespace Step46 } - // The second part of this pair of functions - // determines which finite element to use on - // each cell. Above we have set the material - // indicator for each coarse mesh cell, and - // as mentioned in the introduction, this - // information is inherited from mother to - // child cell upon mesh refinement. - // - // In other words, whenever we have refined - // (or created) the mesh, we can rely on the - // material indicators to be a correct - // description of which part of the domain a - // cell is in. We then use this to set the - // active FE index of the cell to the - // corresponding element of the - // hp::FECollection member variable of this - // class: zero for fluid cells, one for solid - // cells. + // The second part of this pair of functions + // determines which finite element to use on + // each cell. Above we have set the material + // indicator for each coarse mesh cell, and + // as mentioned in the introduction, this + // information is inherited from mother to + // child cell upon mesh refinement. + // + // In other words, whenever we have refined + // (or created) the mesh, we can rely on the + // material indicators to be a correct + // description of which part of the domain a + // cell is in. We then use this to set the + // active FE index of the cell to the + // corresponding element of the + // hp::FECollection member variable of this + // class: zero for fluid cells, one for solid + // cells. template void FluidStructureProblem::set_active_fe_indices () { for (typename hp::DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(); + cell = dof_handler.begin_active(); cell != dof_handler.end(); ++cell) { if (cell_is_in_fluid_domain(cell)) @@ -402,20 +402,20 @@ namespace Step46 } - // @sect4{FluidStructureProblem::setup_dofs} - - // The next step is to setup the data - // structures for the linear system. To this - // end, we first have to set the active FE - // indices with the function immediately - // above, then distribute degrees of freedom, - // and then determine constraints on the - // linear system. The latter includes hanging - // node constraints as usual, but also the - // inhomogenous boundary values at the top - // fluid boundary, and zero boundary values - // along the perimeter of the solid - // subdomain. + // @sect4{FluidStructureProblem::setup_dofs} + + // The next step is to setup the data + // structures for the linear system. To this + // end, we first have to set the active FE + // indices with the function immediately + // above, then distribute degrees of freedom, + // and then determine constraints on the + // linear system. The latter includes hanging + // node constraints as usual, but also the + // inhomogenous boundary values at the top + // fluid boundary, and zero boundary values + // along the perimeter of the solid + // subdomain. template void FluidStructureProblem::setup_dofs () @@ -443,16 +443,16 @@ namespace Step46 fe_collection.component_mask(displacements)); } - // There are more constraints we have to - // handle, though: we have to make sure - // that the velocity is zero at the - // interface between fluid and solid. The - // following piece of code was already - // presented in the introduction: + // There are more constraints we have to + // handle, though: we have to make sure + // that the velocity is zero at the + // interface between fluid and solid. The + // following piece of code was already + // presented in the introduction: { std::vector local_face_dof_indices (stokes_fe.dofs_per_face); for (typename hp::DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(); + cell = dof_handler.begin_active(); cell != dof_handler.end(); ++cell) if (cell_is_in_fluid_domain (cell)) for (unsigned int f=0; f::faces_per_cell; ++f) @@ -485,11 +485,11 @@ namespace Step46 } } - // At the end of all this, we can declare - // to the constraints object that we now - // have all constraints ready to go and - // that the object can rebuild its internal - // data structures for better efficiency: + // At the end of all this, we can declare + // to the constraints object that we now + // have all constraints ready to go and + // that the object can rebuild its internal + // data structures for better efficiency: constraints.close (); std::cout << " Number of active cells: " @@ -499,11 +499,11 @@ namespace Step46 << dof_handler.n_dofs() << std::endl; - // In the rest of this function we create a - // sparsity pattern as discussed - // extensively in the introduction, and use - // it to initialize the matrix; then also - // set vectors to their correct sizes: + // In the rest of this function we create a + // sparsity pattern as discussed + // extensively in the introduction, and use + // it to initialize the matrix; then also + // set vectors to their correct sizes: { CompressedSimpleSparsityPattern csp (dof_handler.n_dofs(), dof_handler.n_dofs()); @@ -540,20 +540,20 @@ namespace Step46 - // @sect4{FluidStructureProblem::assemble_system} + // @sect4{FluidStructureProblem::assemble_system} - // Following is the central function of this - // program: the one that assembles the linear - // system. It has a long section of setting - // up auxiliary functions at the beginning: - // from creating the quadrature formulas and - // setting up the FEValues, FEFaceValues and - // FESubfaceValues objects necessary to - // integrate the cell terms as well as the - // interface terms for the case where cells - // along the interface come together at same - // size or with differing levels of - // refinement... + // Following is the central function of this + // program: the one that assembles the linear + // system. It has a long section of setting + // up auxiliary functions at the beginning: + // from creating the quadrature formulas and + // setting up the FEValues, FEFaceValues and + // FESubfaceValues objects necessary to + // integrate the cell terms as well as the + // interface terms for the case where cells + // along the interface come together at same + // size or with differing levels of + // refinement... template void FluidStructureProblem::assemble_system () { @@ -593,9 +593,9 @@ namespace Step46 common_face_quadrature, update_values); - // ...to objects that are needed to - // describe the local contributions to the - // global linear system... + // ...to objects that are needed to + // describe the local contributions to the + // global linear system... const unsigned int stokes_dofs_per_cell = stokes_fe.dofs_per_cell; const unsigned int elasticity_dofs_per_cell = elasticity_fe.dofs_per_cell; @@ -609,11 +609,11 @@ namespace Step46 const RightHandSide right_hand_side; - // ...to variables that allow us to extract - // certain components of the shape - // functions and cache their values rather - // than having to recompute them at every - // quadrature point: + // ...to variables that allow us to extract + // certain components of the shape + // functions and cache their values rather + // than having to recompute them at every + // quadrature point: const FEValuesExtractors::Vector velocities (0); const FEValuesExtractors::Scalar pressure (dim); const FEValuesExtractors::Vector displacements (dim+1); @@ -626,15 +626,15 @@ namespace Step46 std::vector elasticity_div_phi (elasticity_dofs_per_cell); std::vector > elasticity_phi (elasticity_dofs_per_cell); - // Then comes the main loop over all cells - // and, as in step-27, the initialization - // of the hp::FEValues object for the - // current cell and the extraction of a - // FEValues object that is appropriate for - // the current cell: + // Then comes the main loop over all cells + // and, as in step-27, the initialization + // of the hp::FEValues object for the + // current cell and the extraction of a + // FEValues object that is appropriate for + // the current cell: typename hp::DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { hp_fe_values.reinit (cell); @@ -645,29 +645,29 @@ namespace Step46 cell->get_fe().dofs_per_cell); local_rhs.reinit (cell->get_fe().dofs_per_cell); - // With all of this done, we continue - // to assemble the cell terms for cells - // that are part of the Stokes and - // elastic regions. While we could in - // principle do this in one formula, in - // effect implementing the one bilinear - // form stated in the introduction, we - // realize that our finite element - // spaces are chosen in such a way that - // on each cell, one set of variables - // (either velocities and pressure, or - // displacements) are always zero, and - // consequently a more efficient way of - // computing local integrals is to do - // only what's necessary based on an - // if clause that tests - // which part of the domain we are in. - // - // The actual computation of the local - // matrix is the same as in step-22 as - // well as that given in the @ref - // vector_valued documentation module - // for the elasticity equations: + // With all of this done, we continue + // to assemble the cell terms for cells + // that are part of the Stokes and + // elastic regions. While we could in + // principle do this in one formula, in + // effect implementing the one bilinear + // form stated in the introduction, we + // realize that our finite element + // spaces are chosen in such a way that + // on each cell, one set of variables + // (either velocities and pressure, or + // displacements) are always zero, and + // consequently a more efficient way of + // computing local integrals is to do + // only what's necessary based on an + // if clause that tests + // which part of the domain we are in. + // + // The actual computation of the local + // matrix is the same as in step-22 as + // well as that given in the @ref + // vector_valued documentation module + // for the elasticity equations: if (cell_is_in_fluid_domain (cell)) { const unsigned int dofs_per_cell = cell->get_fe().dofs_per_cell; @@ -709,109 +709,109 @@ namespace Step46 for (unsigned int j=0; jlocal_rhs variable, - // though we still need to pass it - // along since the elimination of - // nonzero boundary values requires the - // modification of local and - // consequently also global right hand - // side values: + // Once we have the contributions from + // cell integrals, we copy them into + // the global matrix (taking care of + // constraints right away, through the + // ConstraintMatrix::distribute_local_to_global + // function). Note that we have not + // written anything into the + // local_rhs variable, + // though we still need to pass it + // along since the elimination of + // nonzero boundary values requires the + // modification of local and + // consequently also global right hand + // side values: local_dof_indices.resize (cell->get_fe().dofs_per_cell); cell->get_dof_indices (local_dof_indices); constraints.distribute_local_to_global (local_matrix, local_rhs, local_dof_indices, system_matrix, system_rhs); - // The more interesting part of this - // function is where we see about face - // terms along the interface between - // the two subdomains. To this end, we - // first have to make sure that we only - // assemble them once even though a - // loop over all faces of all cells - // would encounter each part of the - // interface twice. We arbitrarily make - // the decision that we will only - // evaluate interface terms if the - // current cell is part of the solid - // subdomain and if, consequently, a - // face is not at the boundary and the - // potential neighbor behind it is part - // of the fluid domain. Let's start - // with these conditions: + // The more interesting part of this + // function is where we see about face + // terms along the interface between + // the two subdomains. To this end, we + // first have to make sure that we only + // assemble them once even though a + // loop over all faces of all cells + // would encounter each part of the + // interface twice. We arbitrarily make + // the decision that we will only + // evaluate interface terms if the + // current cell is part of the solid + // subdomain and if, consequently, a + // face is not at the boundary and the + // potential neighbor behind it is part + // of the fluid domain. Let's start + // with these conditions: if (cell_is_in_solid_domain (cell)) for (unsigned int f=0; f::faces_per_cell; ++f) if (cell->at_boundary(f) == false) { - // At this point we know that - // the current cell is a - // candidate for integration - // and that a neighbor behind - // face f - // exists. There are now three - // possibilities: - // - // - The neighbor is at the - // same refinement level and - // has no children. - // - The neighbor has children. - // - The neighbor is coarser. - // - // In all three cases, we are - // only interested in it if it - // is part of the fluid - // subdomain. So let us start - // with the first and simplest - // case: if the neighbor is at - // the same level, has no - // children, and is a fluid - // cell, then the two cells - // share a boundary that is - // part of the interface along - // which we want to integrate - // interface terms. All we have - // to do is initialize two - // FEFaceValues object with the - // current face and the face of - // the neighboring cell (note - // how we find out which face - // of the neighboring cell - // borders on the current cell) - // and pass things off to the - // function that evaluates the - // interface terms (the third - // through fifth arguments to - // this function provide it - // with scratch arrays). The - // result is then again copied - // into the global matrix, - // using a function that knows - // that the DoF indices of rows - // and columns of the local - // matrix result from different - // cells: + // At this point we know that + // the current cell is a + // candidate for integration + // and that a neighbor behind + // face f + // exists. There are now three + // possibilities: + // + // - The neighbor is at the + // same refinement level and + // has no children. + // - The neighbor has children. + // - The neighbor is coarser. + // + // In all three cases, we are + // only interested in it if it + // is part of the fluid + // subdomain. So let us start + // with the first and simplest + // case: if the neighbor is at + // the same level, has no + // children, and is a fluid + // cell, then the two cells + // share a boundary that is + // part of the interface along + // which we want to integrate + // interface terms. All we have + // to do is initialize two + // FEFaceValues object with the + // current face and the face of + // the neighboring cell (note + // how we find out which face + // of the neighboring cell + // borders on the current cell) + // and pass things off to the + // function that evaluates the + // interface terms (the third + // through fifth arguments to + // this function provide it + // with scratch arrays). The + // result is then again copied + // into the global matrix, + // using a function that knows + // that the DoF indices of rows + // and columns of the local + // matrix result from different + // cells: if ((cell->neighbor(f)->level() == cell->level()) && (cell->neighbor(f)->has_children() == false) @@ -833,22 +833,22 @@ namespace Step46 system_matrix); } - // The second case is if the - // neighbor has further - // children. In that case, we - // have to loop over all the - // children of the neighbor to - // see if they are part of the - // fluid subdomain. If they - // are, then we integrate over - // the common interface, which - // is a face for the neighbor - // and a subface of the current - // cell, requiring us to use an - // FEFaceValues for the - // neighbor and an - // FESubfaceValues for the - // current cell: + // The second case is if the + // neighbor has further + // children. In that case, we + // have to loop over all the + // children of the neighbor to + // see if they are part of the + // fluid subdomain. If they + // are, then we integrate over + // the common interface, which + // is a face for the neighbor + // and a subface of the current + // cell, requiring us to use an + // FEFaceValues for the + // neighbor and an + // FESubfaceValues for the + // current cell: else if ((cell->neighbor(f)->level() == cell->level()) && (cell->neighbor(f)->has_children() == true)) @@ -872,7 +872,7 @@ namespace Step46 local_interface_matrix); cell->neighbor_child_on_subface (f, subface) - ->get_dof_indices (neighbor_dof_indices); + ->get_dof_indices (neighbor_dof_indices); constraints.distribute_local_to_global(local_interface_matrix, local_dof_indices, neighbor_dof_indices, @@ -880,14 +880,14 @@ namespace Step46 } } - // The last option is that the - // neighbor is coarser. In that - // case we have to use an - // FESubfaceValues object for - // the neighbor and a - // FEFaceValues for the current - // cell; the rest is the same - // as before: + // The last option is that the + // neighbor is coarser. In that + // case we have to use an + // FESubfaceValues object for + // the neighbor and a + // FEFaceValues for the current + // cell; the rest is the same + // as before: else if (cell->neighbor_is_coarser(f) && cell_is_in_fluid_domain(cell->neighbor(f))) @@ -916,25 +916,25 @@ namespace Step46 - // In the function that assembles the global - // system, we passed computing interface - // terms to a separate function we discuss - // here. The key is that even though we can't - // predict the combination of FEFaceValues - // and FESubfaceValues objects, they are both - // derived from the FEFaceValuesBase class - // and consequently we don't have to care: - // the function is simply called with two - // such objects denoting the values of the - // shape functions on the quadrature points - // of the two sides of the face. We then do - // what we always do: we fill the scratch - // arrays with the values of shape functions - // and their derivatives, and then loop over - // all entries of the matrix to compute the - // local integrals. The details of the - // bilinear form we evaluate here are given - // in the introduction. + // In the function that assembles the global + // system, we passed computing interface + // terms to a separate function we discuss + // here. The key is that even though we can't + // predict the combination of FEFaceValues + // and FESubfaceValues objects, they are both + // derived from the FEFaceValuesBase class + // and consequently we don't have to care: + // the function is simply called with two + // such objects denoting the values of the + // shape functions on the quadrature points + // of the two sides of the face. We then do + // what we always do: we fill the scratch + // arrays with the values of shape functions + // and their derivatives, and then loop over + // all entries of the matrix to compute the + // local integrals. The details of the + // bilinear form we evaluate here are given + // in the introduction. template void FluidStructureProblem:: @@ -979,16 +979,16 @@ namespace Step46 } - // @sect4{FluidStructureProblem::solve} + // @sect4{FluidStructureProblem::solve} - // As discussed in the introduction, we use a - // rather trivial solver here: we just pass - // the linear system off to the - // SparseDirectUMFPACK direct solver (see, - // for example, step-29). The only thing we - // have to do after solving is ensure that - // hanging node and boundary value - // constraints are correct. + // As discussed in the introduction, we use a + // rather trivial solver here: we just pass + // the linear system off to the + // SparseDirectUMFPACK direct solver (see, + // for example, step-29). The only thing we + // have to do after solving is ensure that + // hanging node and boundary value + // constraints are correct. template void FluidStructureProblem::solve () @@ -1002,17 +1002,17 @@ namespace Step46 - // @sect4{FluidStructureProblem::output_results} + // @sect4{FluidStructureProblem::output_results} - // Generating graphical output is rather - // trivial here: all we have to do is - // identify which components of the solution - // vector belong to scalars and/or vectors - // (see, for example, step-22 for a previous - // example), and then pass it all on to the - // DataOut class (with the second template - // argument equal to hp::DoFHandler instead - // of the usual default DoFHandler): + // Generating graphical output is rather + // trivial here: all we have to do is + // identify which components of the solution + // vector belong to scalars and/or vectors + // (see, for example, step-22 for a previous + // example), and then pass it all on to the + // DataOut class (with the second template + // argument equal to hp::DoFHandler instead + // of the usual default DoFHandler): template void FluidStructureProblem:: @@ -1024,13 +1024,13 @@ namespace Step46 solution_names.push_back ("displacement"); std::vector - data_component_interpretation - (dim, DataComponentInterpretation::component_is_part_of_vector); data_component_interpretation - .push_back (DataComponentInterpretation::component_is_scalar); + (dim, DataComponentInterpretation::component_is_part_of_vector); + data_component_interpretation + .push_back (DataComponentInterpretation::component_is_scalar); for (unsigned int d=0; d > data_out; data_out.attach_dof_handler (dof_handler); @@ -1050,30 +1050,30 @@ namespace Step46 } - // @sect4{FluidStructureProblem::refine_mesh} - - // The next step is to refine the mesh. As - // was discussed in the introduction, this is - // a bit tricky primarily because the fluid - // and the solid subdomains use variables - // that have different physical dimensions - // and for which the absolute magnitude of - // error estimates is consequently not - // directly comparable. We will therefore - // have to scale them. At the top of the - // function, we therefore first compute error - // estimates for the different variables - // separately (using the velocities but not - // the pressure for the fluid domain, and the - // displacements in the solid domain): + // @sect4{FluidStructureProblem::refine_mesh} + + // The next step is to refine the mesh. As + // was discussed in the introduction, this is + // a bit tricky primarily because the fluid + // and the solid subdomains use variables + // that have different physical dimensions + // and for which the absolute magnitude of + // error estimates is consequently not + // directly comparable. We will therefore + // have to scale them. At the top of the + // function, we therefore first compute error + // estimates for the different variables + // separately (using the velocities but not + // the pressure for the fluid domain, and the + // displacements in the solid domain): template void FluidStructureProblem::refine_mesh () { Vector - stokes_estimated_error_per_cell (triangulation.n_active_cells()); + stokes_estimated_error_per_cell (triangulation.n_active_cells()); Vector - elasticity_estimated_error_per_cell (triangulation.n_active_cells()); + elasticity_estimated_error_per_cell (triangulation.n_active_cells()); const QGauss stokes_face_quadrature(stokes_degree+2); const QGauss elasticity_face_quadrature(elasticity_degree+2); @@ -1098,56 +1098,56 @@ namespace Step46 elasticity_estimated_error_per_cell, fe_collection.component_mask(displacements)); - // We then normalize error estimates by - // dividing by their norm and scale the - // fluid error indicators by a factor of 4 - // as discussed in the introduction. The - // results are then added together into a - // vector that contains error indicators - // for all cells: + // We then normalize error estimates by + // dividing by their norm and scale the + // fluid error indicators by a factor of 4 + // as discussed in the introduction. The + // results are then added together into a + // vector that contains error indicators + // for all cells: stokes_estimated_error_per_cell - *= 4. / stokes_estimated_error_per_cell.l2_norm(); + *= 4. / stokes_estimated_error_per_cell.l2_norm(); elasticity_estimated_error_per_cell - *= 1. / elasticity_estimated_error_per_cell.l2_norm(); + *= 1. / elasticity_estimated_error_per_cell.l2_norm(); Vector - estimated_error_per_cell (triangulation.n_active_cells()); + estimated_error_per_cell (triangulation.n_active_cells()); estimated_error_per_cell += stokes_estimated_error_per_cell; estimated_error_per_cell += elasticity_estimated_error_per_cell; - // The second to last part of the function, - // before actually refining the mesh, - // involves a heuristic that we have - // already mentioned in the introduction: - // because the solution is discontinuous, - // the KellyErrorEstimator class gets all - // confused about cells that sit at the - // boundary between subdomains: it believes - // that the error is large there because - // the jump in the gradient is large, even - // though this is entirely expected and a - // feature that is in fact present in the - // exact solution as well and therefore not - // indicative of any numerical error. - // - // Consequently, we set the error - // indicators to zero for all cells at the - // interface; the conditions determining - // which cells this affects are slightly - // awkward because we have to account for - // the possibility of adaptively refined - // meshes, meaning that the neighboring - // cell can be coarser than the current - // one, or could in fact be refined some - // more. The structure of these nested - // conditions is much the same as we - // encountered when assembling interface - // terms in assemble_system. + // The second to last part of the function, + // before actually refining the mesh, + // involves a heuristic that we have + // already mentioned in the introduction: + // because the solution is discontinuous, + // the KellyErrorEstimator class gets all + // confused about cells that sit at the + // boundary between subdomains: it believes + // that the error is large there because + // the jump in the gradient is large, even + // though this is entirely expected and a + // feature that is in fact present in the + // exact solution as well and therefore not + // indicative of any numerical error. + // + // Consequently, we set the error + // indicators to zero for all cells at the + // interface; the conditions determining + // which cells this affects are slightly + // awkward because we have to account for + // the possibility of adaptively refined + // meshes, meaning that the neighboring + // cell can be coarser than the current + // one, or could in fact be refined some + // more. The structure of these nested + // conditions is much the same as we + // encountered when assembling interface + // terms in assemble_system. { unsigned int cell_index = 0; for (typename hp::DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(); + cell = dof_handler.begin_active(); cell != dof_handler.end(); ++cell, ++cell_index) for (unsigned int f=0; f::faces_per_cell; ++f) if (cell_is_in_solid_domain (cell)) @@ -1206,14 +1206,14 @@ namespace Step46 - // @sect4{FluidStructureProblem::run} + // @sect4{FluidStructureProblem::run} - // This is, as usual, the function that - // controls the overall flow of operation. If - // you've read through tutorial programs - // step-1 through step-6, for example, then - // you are already quite familiar with the - // following structure: + // This is, as usual, the function that + // controls the overall flow of operation. If + // you've read through tutorial programs + // step-1 through step-6, for example, then + // you are already quite familiar with the + // following structure: template void FluidStructureProblem::run () { @@ -1245,11 +1245,11 @@ namespace Step46 - // @sect4{The main() function} +// @sect4{The main() function} - // This, final, function contains pretty much - // exactly what most of the other tutorial - // programs have: +// This, final, function contains pretty much +// exactly what most of the other tutorial +// programs have: int main () { try diff --git a/deal.II/examples/step-47/step-47.cc b/deal.II/examples/step-47/step-47.cc index 4bcd524b5f..3babff4911 100644 --- a/deal.II/examples/step-47/step-47.cc +++ b/deal.II/examples/step-47/step-47.cc @@ -68,39 +68,39 @@ namespace Step47 template class LaplaceProblem { - public: - LaplaceProblem (); - ~LaplaceProblem (); + public: + LaplaceProblem (); + ~LaplaceProblem (); - void run (); + void run (); - private: - bool interface_intersects_cell (const typename Triangulation::cell_iterator &cell) const; - std::pair > compute_quadrature(const Quadrature &plain_quadrature, const typename hp::DoFHandler::active_cell_iterator &cell, const std::vector &level_set_values); - void append_quadrature(const Quadrature &plain_quadrature, - const std::vector > &v , - std::vector > &xfem_points, - std::vector &xfem_weights); + private: + bool interface_intersects_cell (const typename Triangulation::cell_iterator &cell) const; + std::pair > compute_quadrature(const Quadrature &plain_quadrature, const typename hp::DoFHandler::active_cell_iterator &cell, const std::vector &level_set_values); + void append_quadrature(const Quadrature &plain_quadrature, + const std::vector > &v , + std::vector > &xfem_points, + std::vector &xfem_weights); - void setup_system (); - void assemble_system (); - void solve (); - void refine_grid (); - void output_results (const unsigned int cycle) const; - void compute_error () const; + void setup_system (); + void assemble_system (); + void solve (); + void refine_grid (); + void output_results (const unsigned int cycle) const; + void compute_error () const; - Triangulation triangulation; + Triangulation triangulation; - hp::DoFHandler dof_handler; - hp::FECollection fe_collection; + hp::DoFHandler dof_handler; + hp::FECollection fe_collection; - ConstraintMatrix constraints; + ConstraintMatrix constraints; - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; - Vector solution; - Vector system_rhs; + Vector solution; + Vector system_rhs; }; @@ -109,15 +109,15 @@ namespace Step47 template class Coefficient : public Function { - public: - Coefficient () : Function() {} + public: + Coefficient () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; @@ -173,8 +173,8 @@ namespace Step47 template LaplaceProblem::LaplaceProblem () - : - dof_handler (triangulation) + : + dof_handler (triangulation) { fe_collection.push_back (FESystem (FE_Q(1), 1, FE_Nothing(), 1)); @@ -219,9 +219,9 @@ namespace Step47 if (level_set(cell->vertex(v)) * level_set(cell->vertex(v+1)) < 0) return true; - // we get here only if all vertices - // have the same sign, which means - // that the cell is not intersected + // we get here only if all vertices + // have the same sign, which means + // that the cell is not intersected return false; } @@ -231,7 +231,7 @@ namespace Step47 void LaplaceProblem::setup_system () { for (typename hp::DoFHandler::cell_iterator cell - = dof_handler.begin_active(); + = dof_handler.begin_active(); cell != dof_handler.end(); ++cell) if (interface_intersects_cell(cell) == false) cell->set_active_fe_index(0); @@ -246,8 +246,8 @@ namespace Step47 constraints.clear (); //TODO: fix this, it currently crashes - // DoFTools::make_hanging_node_constraints (dof_handler, - // constraints); + // DoFTools::make_hanging_node_constraints (dof_handler, + // constraints); //TODO: component 1 must satisfy zero boundary conditions constraints.close(); @@ -288,8 +288,8 @@ namespace Step47 std::vector coefficient_values (n_q_points); typename hp::DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { @@ -457,7 +457,7 @@ namespace Step47 unsigned int type = 0; - // find the type of cut + // find the type of cut int sign_ls[GeometryInfo::vertices_per_cell]; for (unsigned int v=0; v::vertices_per_cell; ++v) { @@ -466,11 +466,11 @@ namespace Step47 else sign_ls[v] = 0; } - // the sign of the level set function at the 4 nodes of the elements can be positive + or negative - - // depending on the sign of the level set function we have the folloing three classes of decomposition - // type 1: ++++, ---- - // type 2: -+++, +-++, ++-+, +++-, +---, -+--, --+-, ---+ - // type 3: +--+, ++--, +-+-, -++-, --++, -+-+ + // the sign of the level set function at the 4 nodes of the elements can be positive + or negative - + // depending on the sign of the level set function we have the folloing three classes of decomposition + // type 1: ++++, ---- + // type 2: -+++, +-++, ++-+, +++-, +---, -+--, --+-, ---+ + // type 3: +--+, ++--, +-+-, -++-, --++, -+-+ if ( sign_ls[0]==sign_ls[1] & sign_ls[0]==sign_ls[2] & sign_ls[0]==sign_ls[3] ) type =1; else if ( sign_ls[0]*sign_ls[1]*sign_ls[2]*sign_ls[3] < 0 ) type = 2; @@ -497,8 +497,8 @@ namespace Step47 { const unsigned int n_q_points = plain_quadrature.size(); - // loop over all subelements for integration - // in type 2 there are 5 subelements + // loop over all subelements for integration + // in type 2 there are 5 subelements Quadrature xfem_quadrature(5*n_q_points); @@ -510,15 +510,15 @@ namespace Step47 else if (sign_ls[3]!=sign_ls[0] && sign_ls[3]!=sign_ls[1] && sign_ls[3]!=sign_ls[2]) Pos = 3; else assert(0); // error message - // Find cut coordinates + // Find cut coordinates - // deal.ii local coordinates + // deal.ii local coordinates - // 2-------3 - // | | - // | | - // | | - // 0-------1 + // 2-------3 + // | | + // | | + // | | + // 0-------1 if (Pos == 0) { @@ -581,12 +581,12 @@ namespace Step47 F(1) = 0.5*( 1. + B(1) ); } - //std::cout << A << std::endl; - //std::cout << B << std::endl; - //std::cout << C << std::endl; - //std::cout << D << std::endl; - //std::cout << E << std::endl; - //std::cout << F << std::endl; + //std::cout << A << std::endl; + //std::cout << B << std::endl; + //std::cout << C << std::endl; + //std::cout << D << std::endl; + //std::cout << E << std::endl; + //std::cout << F << std::endl; std::string filename = "vertices.dat"; std::ofstream output (filename.c_str()); @@ -634,35 +634,36 @@ namespace Step47 std::vector > xfem_points; std::vector xfem_weights; - // lookup table for the decomposition + // lookup table for the decomposition if (dim==2) { - unsigned int subcell_v_indices[4][5][4] = { - {{0,8,9,7}, {9,7,5,6}, {8,4,7,6}, {5,6,2,3}, {6,4,3,1}}, - {{8,1,7,9}, {4,8,6,7}, {6,7,5,9}, {0,4,2,6}, {2,6,3,5}}, - {{9,7,2,8}, {5,6,9,7}, {6,4,7,8}, {0,1,5,6}, {6,1,4,3}}, - {{7,9,8,3}, {4,6,8,7}, {6,5,7,9}, {0,6,2,4}, {0,1,6,5}} + unsigned int subcell_v_indices[4][5][4] = + { + {{0,8,9,7}, {9,7,5,6}, {8,4,7,6}, {5,6,2,3}, {6,4,3,1}}, + {{8,1,7,9}, {4,8,6,7}, {6,7,5,9}, {0,4,2,6}, {2,6,3,5}}, + {{9,7,2,8}, {5,6,9,7}, {6,4,7,8}, {0,1,5,6}, {6,1,4,3}}, + {{7,9,8,3}, {4,6,8,7}, {6,5,7,9}, {0,6,2,4}, {0,1,6,5}} }; for (unsigned int subcell = 0; subcell<5; subcell++) { - //std::cout << "subcell : " << subcell << std::endl; + //std::cout << "subcell : " << subcell << std::endl; std::vector > vertices; for (unsigned int i=0; i<4; i++) { vertices.push_back( subcell_vertices[subcell_v_indices[Pos][subcell][i]] ); - //std::cout << "i : " << i << std::endl; - //std::cout << "subcell v : " << subcell_v_indices[Pos][subcell][i] << std::endl; - //std::cout << vertices[i](0) << " " << vertices[i](1) << std::endl; + //std::cout << "i : " << i << std::endl; + //std::cout << "subcell v : " << subcell_v_indices[Pos][subcell][i] << std::endl; + //std::cout << vertices[i](0) << " " << vertices[i](1) << std::endl; } - //std::cout << std::endl; - // create quadrature rule + //std::cout << std::endl; + // create quadrature rule append_quadrature( plain_quadrature, vertices, xfem_points, xfem_weights); - //initialize xfem_quadrature with quadrature points of all subelements + //initialize xfem_quadrature with quadrature points of all subelements xfem_quadrature.initialize(xfem_points, xfem_weights); } } @@ -671,15 +672,15 @@ namespace Step47 return std::pair >(2, xfem_quadrature); } - // Type three decomposition - // (+--+, ++--, +-+-, -++-, --++, -+-+) + // Type three decomposition + // (+--+, ++--, +-+-, -++-, --++, -+-+) if (type==3) { const unsigned int n_q_points = plain_quadrature.size(); - // loop over all subelements for integration - // in type 2 there are 5 subelements + // loop over all subelements for integration + // in type 2 there are 5 subelements Quadrature xfem_quadrature(5*n_q_points); @@ -712,16 +713,16 @@ namespace Step47 assert(0); } - //std::cout << "Pos " << Pos << std::endl; - //std::cout << A << std::endl; - //std::cout << B << std::endl; + //std::cout << "Pos " << Pos << std::endl; + //std::cout << A << std::endl; + //std::cout << B << std::endl; std::string filename = "vertices.dat"; std::ofstream output (filename.c_str()); output << "#vertices of xfem subcells" << std::endl; output << A(0) << " " << A(1) << std::endl; output << B(0) << " " << B(1) << std::endl; - //fill xfem_quadrature + //fill xfem_quadrature Point subcell_vertices[6]; subcell_vertices[0] = v0; subcell_vertices[1] = v1; @@ -735,30 +736,31 @@ namespace Step47 if (dim==2) { - unsigned int subcell_v_indices[2][2][4] = { - {{0,1,4,5}, {4,5,2,3}}, - {{0,4,2,5}, {4,1,5,3}} + unsigned int subcell_v_indices[2][2][4] = + { + {{0,1,4,5}, {4,5,2,3}}, + {{0,4,2,5}, {4,1,5,3}} }; - //std::cout << "Pos : " << Pos << std::endl; + //std::cout << "Pos : " << Pos << std::endl; for (unsigned int subcell = 0; subcell<2; subcell++) { - //std::cout << "subcell : " << subcell << std::endl; + //std::cout << "subcell : " << subcell << std::endl; std::vector > vertices; for (unsigned int i=0; i<4; i++) { vertices.push_back( subcell_vertices[subcell_v_indices[Pos][subcell][i]] ); - //std::cout << "i : " << i << std::endl; - //std::cout << "subcell v : " << subcell_v_indices[Pos][subcell][i] << std::endl; - //std::cout << vertices[i](0) << " " << vertices[i](1) << std::endl; + //std::cout << "i : " << i << std::endl; + //std::cout << "subcell v : " << subcell_v_indices[Pos][subcell][i] << std::endl; + //std::cout << vertices[i](0) << " " << vertices[i](1) << std::endl; } - //std::cout << std::endl; - // create quadrature rule + //std::cout << std::endl; + // create quadrature rule append_quadrature( plain_quadrature, vertices, xfem_points, xfem_weights); - //initialize xfem_quadrature with quadrature points of all subelements + //initialize xfem_quadrature with quadrature points of all subelements xfem_quadrature.initialize(xfem_points, xfem_weights); } } @@ -777,11 +779,11 @@ namespace Step47 std::vector &xfem_weights) { - // Project integration points into sub-elements. - // This maps quadrature points from a reference element to a subelement of a reference element. - // To implement the action of this map the coordinates of the subelements have been calculated (A(0)...F(0),A(1)...F(1)) - // the coordinates of the quadrature points are given by the bi-linear map defined by the form functions - // $x^\prime_i = \sum_j v^\prime \phi_j(x^hat_i)$, where the $\phi_j$ are the shape functions of the FEQ. + // Project integration points into sub-elements. + // This maps quadrature points from a reference element to a subelement of a reference element. + // To implement the action of this map the coordinates of the subelements have been calculated (A(0)...F(0),A(1)...F(1)) + // the coordinates of the quadrature points are given by the bi-linear map defined by the form functions + // $x^\prime_i = \sum_j v^\prime \phi_j(x^hat_i)$, where the $\phi_j$ are the shape functions of the FEQ. unsigned int n_v = GeometryInfo::vertices_per_cell; @@ -799,39 +801,39 @@ namespace Step47 { switch (dim) { - case 2: - { - double xi = q_points[i](0); - double eta = q_points[i](1); - - // Define shape functions on reference element - // we consider a bi-linear mapping - phi[0] = (1. - xi) * (1. - eta); - phi[1] = xi * (1. - eta); - phi[2] = (1. - xi) * eta; - phi[3] = xi * eta; - - grad_phi[0][0] = (-1. + eta); - grad_phi[1][0] = (1. - eta); - grad_phi[2][0] = -eta; - grad_phi[3][0] = eta; - - grad_phi[0][1] = (-1. + xi); - grad_phi[1][1] = -xi; - grad_phi[2][1] = 1-xi; - grad_phi[3][1] = xi; - - break; - } + case 2: + { + double xi = q_points[i](0); + double eta = q_points[i](1); + + // Define shape functions on reference element + // we consider a bi-linear mapping + phi[0] = (1. - xi) * (1. - eta); + phi[1] = xi * (1. - eta); + phi[2] = (1. - xi) * eta; + phi[3] = xi * eta; + + grad_phi[0][0] = (-1. + eta); + grad_phi[1][0] = (1. - eta); + grad_phi[2][0] = -eta; + grad_phi[3][0] = eta; + + grad_phi[0][1] = (-1. + xi); + grad_phi[1][1] = -xi; + grad_phi[2][1] = 1-xi; + grad_phi[3][1] = xi; + + break; + } - default: - Assert (false, ExcNotImplemented()); + default: + Assert (false, ExcNotImplemented()); } Tensor<2,dim> jacobian; - // Calculate Jacobian of transformation + // Calculate Jacobian of transformation for (unsigned int d=0; d q_prime; for (unsigned int d=0; d::vertices_per_cell; j++) @@ -895,23 +897,23 @@ namespace Step47 template class Postprocessor : public DataPostprocessor { - public: - virtual - void - compute_derived_quantities_vector (const std::vector > &uh, - const std::vector > > &duh, - const std::vector > > &dduh, - const std::vector > &normals, - const std::vector > &evaluation_points, - std::vector > &computed_quantities) const; - - virtual std::vector get_names () const; - - virtual - std::vector - get_data_component_interpretation () const; - - virtual UpdateFlags get_needed_update_flags () const; + public: + virtual + void + compute_derived_quantities_vector (const std::vector > &uh, + const std::vector > > &duh, + const std::vector > > &dduh, + const std::vector > &normals, + const std::vector > &evaluation_points, + std::vector > &computed_quantities) const; + + virtual std::vector get_names () const; + + virtual + std::vector + get_data_component_interpretation () const; + + virtual UpdateFlags get_needed_update_flags () const; }; @@ -931,8 +933,8 @@ namespace Step47 get_data_component_interpretation () const { std::vector - interpretation (2, - DataComponentInterpretation::component_is_scalar); + interpretation (2, + DataComponentInterpretation::component_is_scalar); return interpretation; } @@ -1014,8 +1016,8 @@ namespace Step47 std::vector > solution_values; typename hp::DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { @@ -1057,7 +1059,7 @@ namespace Step47 if (cycle == 0) { GridGenerator::hyper_ball (triangulation); - //GridGenerator::hyper_cube (triangulation, -1, 1); + //GridGenerator::hyper_cube (triangulation, -1, 1); static const HyperBallBoundary boundary; triangulation.set_boundary (0, boundary); diff --git a/deal.II/examples/step-48/step-48.cc b/deal.II/examples/step-48/step-48.cc index 7a4259e428..e801034fc2 100644 --- a/deal.II/examples/step-48/step-48.cc +++ b/deal.II/examples/step-48/step-48.cc @@ -11,8 +11,8 @@ /* further information on this license. */ - // The necessary files from the deal.II - // library. +// The necessary files from the deal.II +// library. #include #include #include @@ -34,9 +34,9 @@ #include #include - // This includes the data structures for the - // efficient implementation of matrix-free - // methods. +// This includes the data structures for the +// efficient implementation of matrix-free +// methods. #include #include #include @@ -50,99 +50,99 @@ namespace Step48 { using namespace dealii; - // We start by defining two global - // variables to collect all parameters - // subject to changes at one place: - // One for the dimension and one for - // the finite element degree. The - // dimension is used in the main - // function as a template argument for - // the actual classes (like in all - // other deal.II programs), whereas - // the degree of the finite element is - // more crucial, as it is passed as a - // template argument to the - // implementation of the Sine-Gordon - // operator. Therefore, it needs to be - // a compile-time constant. + // We start by defining two global + // variables to collect all parameters + // subject to changes at one place: + // One for the dimension and one for + // the finite element degree. The + // dimension is used in the main + // function as a template argument for + // the actual classes (like in all + // other deal.II programs), whereas + // the degree of the finite element is + // more crucial, as it is passed as a + // template argument to the + // implementation of the Sine-Gordon + // operator. Therefore, it needs to be + // a compile-time constant. const unsigned int dimension = 2; const unsigned int fe_degree = 4; - // @sect3{SineGordonOperation} - - // The SineGordonOperation class - // implements the cell-based operation that is - // needed in each time step. This nonlinear - // operation can be implemented - // straight-forwardly based on the - // MatrixFree class, in the - // same way as a linear operation would be - // treated by this implementation of the - // finite element operator application. We - // apply two template arguments to the class, - // one for the dimension and one for the - // degree of the finite element. This is a - // difference to other functions in deal.II - // where only the dimension is a template - // argument. This is necessary to provide the - // inner loops in @p FEEvaluation with - // information about loop lengths etc., which - // is essential for efficiency. On the other - // hand, it makes it more challenging to - // implement the degree as a run-time - // parameter. + // @sect3{SineGordonOperation} + + // The SineGordonOperation class + // implements the cell-based operation that is + // needed in each time step. This nonlinear + // operation can be implemented + // straight-forwardly based on the + // MatrixFree class, in the + // same way as a linear operation would be + // treated by this implementation of the + // finite element operator application. We + // apply two template arguments to the class, + // one for the dimension and one for the + // degree of the finite element. This is a + // difference to other functions in deal.II + // where only the dimension is a template + // argument. This is necessary to provide the + // inner loops in @p FEEvaluation with + // information about loop lengths etc., which + // is essential for efficiency. On the other + // hand, it makes it more challenging to + // implement the degree as a run-time + // parameter. template class SineGordonOperation { - public: - SineGordonOperation(const MatrixFree &data_in, - const double time_step); - - void apply (parallel::distributed::Vector &dst, - const std::vector*> &src) const; - - private: - const MatrixFree &data; - const VectorizedArray delta_t_sqr; - parallel::distributed::Vector inv_mass_matrix; - - void local_apply (const MatrixFree &data, - parallel::distributed::Vector &dst, - const std::vector*>&src, - const std::pair &cell_range) const; + public: + SineGordonOperation(const MatrixFree &data_in, + const double time_step); + + void apply (parallel::distributed::Vector &dst, + const std::vector*> &src) const; + + private: + const MatrixFree &data; + const VectorizedArray delta_t_sqr; + parallel::distributed::Vector inv_mass_matrix; + + void local_apply (const MatrixFree &data, + parallel::distributed::Vector &dst, + const std::vector*> &src, + const std::pair &cell_range) const; }; - // @sect4{SineGordonOperation::SineGordonOperation} - - // This is the constructor of the - // SineGordonOperation class. It receives a - // reference to the MatrixFree holding the - // problem information and the time step size - // as input parameters. The initialization - // routine sets up the mass matrix. Since we - // use Gauss-Lobatto elements, the mass matrix - // is a diagonal matrix and can be stored as a - // vector. The computation of the mass matrix - // diagonal is simple to achieve with the data - // structures provided by FEEvaluation: Just - // loop over all (macro-) cells and integrate - // over the function that is constant one on - // all quadrature points by using the - // integrate function with @p - // true argument at the slot for - // values. Finally, we invert the diagonal - // entries since we have to multiply by the - // inverse mass matrix in each time step. + // @sect4{SineGordonOperation::SineGordonOperation} + + // This is the constructor of the + // SineGordonOperation class. It receives a + // reference to the MatrixFree holding the + // problem information and the time step size + // as input parameters. The initialization + // routine sets up the mass matrix. Since we + // use Gauss-Lobatto elements, the mass matrix + // is a diagonal matrix and can be stored as a + // vector. The computation of the mass matrix + // diagonal is simple to achieve with the data + // structures provided by FEEvaluation: Just + // loop over all (macro-) cells and integrate + // over the function that is constant one on + // all quadrature points by using the + // integrate function with @p + // true argument at the slot for + // values. Finally, we invert the diagonal + // entries since we have to multiply by the + // inverse mass matrix in each time step. template SineGordonOperation:: SineGordonOperation(const MatrixFree &data_in, const double time_step) - : - data(data_in), - delta_t_sqr(make_vectorized_array(time_step*time_step)) + : + data(data_in), + delta_t_sqr(make_vectorized_array(time_step *time_step)) { VectorizedArray one = make_vectorized_array (1.); @@ -170,59 +170,59 @@ namespace Step48 - // @sect4{SineGordonOperation::local_apply} - - // This operator implements the core operation - // of the program, the integration over a - // range of cells for the nonlinear operator - // of the Sine-Gordon problem. The - // implementation is based on the - // FEEvaluationGL class since we are using - // the cell-based implementation for - // Gauss-Lobatto elements. - - // The nonlinear function that we have to - // evaluate for the time stepping routine - // includes the value of the function at - // the present time @p current as well as - // the value at the previous time step @p - // old. Both values are passed to the - // operator in the collection of source - // vectors @p src, which is simply an STL - // vector of pointers to the actual - // solution vectors. This construct of - // collecting several source vectors into - // one is necessary as the cell loop in @p - // MatrixFree takes exactly one source - // and one destination vector, even if we - // happen to use many vectors like the two - // in this case. Note that the cell loop - // accepts any valid class for input and - // output, which does not only include - // vectors but general data types. However, - // only in case it encounters a - // parallel::distributed::Vector or - // an STL vector collecting these vectors, - // it calls functions that exchange data at - // the beginning and the end of the - // loop. In the loop over the cells, we - // first have to read in the values in the - // vectors related to the local - // values. Then, we evaluate the value and - // the gradient of the current solution - // vector and the values of the old vector - // at the quadrature points. Then, we - // combine the terms in the scheme in the - // loop over the quadrature - // points. Finally, we integrate the result - // against the test function and accumulate - // the result to the global solution vector - // @p dst. + // @sect4{SineGordonOperation::local_apply} + + // This operator implements the core operation + // of the program, the integration over a + // range of cells for the nonlinear operator + // of the Sine-Gordon problem. The + // implementation is based on the + // FEEvaluationGL class since we are using + // the cell-based implementation for + // Gauss-Lobatto elements. + + // The nonlinear function that we have to + // evaluate for the time stepping routine + // includes the value of the function at + // the present time @p current as well as + // the value at the previous time step @p + // old. Both values are passed to the + // operator in the collection of source + // vectors @p src, which is simply an STL + // vector of pointers to the actual + // solution vectors. This construct of + // collecting several source vectors into + // one is necessary as the cell loop in @p + // MatrixFree takes exactly one source + // and one destination vector, even if we + // happen to use many vectors like the two + // in this case. Note that the cell loop + // accepts any valid class for input and + // output, which does not only include + // vectors but general data types. However, + // only in case it encounters a + // parallel::distributed::Vector or + // an STL vector collecting these vectors, + // it calls functions that exchange data at + // the beginning and the end of the + // loop. In the loop over the cells, we + // first have to read in the values in the + // vectors related to the local + // values. Then, we evaluate the value and + // the gradient of the current solution + // vector and the values of the old vector + // at the quadrature points. Then, we + // combine the terms in the scheme in the + // loop over the quadrature + // points. Finally, we integrate the result + // against the test function and accumulate + // the result to the global solution vector + // @p dst. template void SineGordonOperation:: local_apply (const MatrixFree &data, parallel::distributed::Vector &dst, - const std::vector*>&src, + const std::vector*> &src, const std::pair &cell_range) const { AssertDimension (src.size(), 2); @@ -256,22 +256,22 @@ namespace Step48 - //@sect4{SineGordonOperation::apply} + //@sect4{SineGordonOperation::apply} - // This function performs the time stepping - // routine based on the cell-local - // strategy. First the destination vector is - // set to zero, then the cell-loop is called, - // and finally the solution is multiplied by - // the inverse mass matrix. The structure of - // the cell loop is implemented in the cell - // finite element operator class. On each cell - // it applies the routine defined as the - // local_apply() method of the - // class SineGordonOperation, - // i.e., this. One could also - // provide a function with the same signature - // that is not part of a class. + // This function performs the time stepping + // routine based on the cell-local + // strategy. First the destination vector is + // set to zero, then the cell-loop is called, + // and finally the solution is multiplied by + // the inverse mass matrix. The structure of + // the cell loop is implemented in the cell + // finite element operator class. On each cell + // it applies the routine defined as the + // local_apply() method of the + // class SineGordonOperation, + // i.e., this. One could also + // provide a function with the same signature + // that is not part of a class. template void SineGordonOperation:: apply (parallel::distributed::Vector &dst, @@ -284,21 +284,21 @@ namespace Step48 } - //@sect3{Equation data} + //@sect3{Equation data} - // We define a time-dependent function that is - // used as initial value. Different solutions - // can be obtained by varying the starting - // time. This function has already been - // explained in step-25. + // We define a time-dependent function that is + // used as initial value. Different solutions + // can be obtained by varying the starting + // time. This function has already been + // explained in step-25. template class ExactSolution : public Function { - public: - ExactSolution (const unsigned int n_components = 1, - const double time = 0.) : Function(n_components, time) {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + public: + ExactSolution (const unsigned int n_components = 1, + const double time = 0.) : Function(n_components, time) {} + virtual double value (const Point &p, + const unsigned int component = 0) const; }; template @@ -320,100 +320,100 @@ namespace Step48 - // @sect3{SineGordonProblem class} + // @sect3{SineGordonProblem class} - // This is the main class that builds on the - // class in step-25. However, we replaced - // the SparseMatrix class by the - // MatrixFree class to store - // the geometry data. Also, we use a - // distributed triangulation in this example. + // This is the main class that builds on the + // class in step-25. However, we replaced + // the SparseMatrix class by the + // MatrixFree class to store + // the geometry data. Also, we use a + // distributed triangulation in this example. template class SineGordonProblem { - public: - SineGordonProblem (); - void run (); + public: + SineGordonProblem (); + void run (); - private: - ConditionalOStream pcout; + private: + ConditionalOStream pcout; - void make_grid_and_dofs (); - void oldstyle_operation (); - void assemble_system (); - void output_results (const unsigned int timestep_number) const; + void make_grid_and_dofs (); + void oldstyle_operation (); + void assemble_system (); + void output_results (const unsigned int timestep_number) const; #ifdef DEAL_II_USE_P4EST - parallel::distributed::Triangulation triangulation; + parallel::distributed::Triangulation triangulation; #else - Triangulation triangulation; + Triangulation triangulation; #endif - FE_Q fe; - DoFHandler dof_handler; - ConstraintMatrix constraints; - IndexSet locally_relevant_dofs; + FE_Q fe; + DoFHandler dof_handler; + ConstraintMatrix constraints; + IndexSet locally_relevant_dofs; - MatrixFree matrix_free_data; + MatrixFree matrix_free_data; - parallel::distributed::Vector solution, old_solution, old_old_solution; + parallel::distributed::Vector solution, old_solution, old_old_solution; - const unsigned int n_global_refinements; - double time, time_step; - const double final_time; - const double cfl_number; - const unsigned int output_timestep_skip; + const unsigned int n_global_refinements; + double time, time_step; + const double final_time; + const double cfl_number; + const unsigned int output_timestep_skip; }; - //@sect4{SineGordonProblem::SineGordonProblem} - - // This is the constructor of the - // SineGordonProblem class. The time interval - // and time step size are defined - // here. Moreover, we use the degree of the - // finite element that we defined at the top - // of the program to initialize a FE_Q finite - // element based on Gauss-Lobatto support - // points. These points are convenient because - // in conjunction with a QGaussLobatto - // quadrature rule of the same order they give - // a diagonal mass matrix without compromising - // accuracy too much (note that the - // integration is inexact, though), see also - // the discussion in the introduction. + //@sect4{SineGordonProblem::SineGordonProblem} + + // This is the constructor of the + // SineGordonProblem class. The time interval + // and time step size are defined + // here. Moreover, we use the degree of the + // finite element that we defined at the top + // of the program to initialize a FE_Q finite + // element based on Gauss-Lobatto support + // points. These points are convenient because + // in conjunction with a QGaussLobatto + // quadrature rule of the same order they give + // a diagonal mass matrix without compromising + // accuracy too much (note that the + // integration is inexact, though), see also + // the discussion in the introduction. template SineGordonProblem::SineGordonProblem () - : - pcout (std::cout, - Utilities::System::get_this_mpi_process(MPI_COMM_WORLD)==0), + : + pcout (std::cout, + Utilities::System::get_this_mpi_process(MPI_COMM_WORLD)==0), #ifdef DEAL_II_USE_P4EST - triangulation (MPI_COMM_WORLD), + triangulation (MPI_COMM_WORLD), #endif - fe (QGaussLobatto<1>(fe_degree+1)), - dof_handler (triangulation), - n_global_refinements (11-2*dim), - time (-10), - final_time (10), - cfl_number (.1/fe_degree), - output_timestep_skip (200) + fe (QGaussLobatto<1>(fe_degree+1)), + dof_handler (triangulation), + n_global_refinements (11-2*dim), + time (-10), + final_time (10), + cfl_number (.1/fe_degree), + output_timestep_skip (200) {} - //@sect4{SineGordonProblem::make_grid_and_dofs} - - // As in step-25 this functions sets up a cube - // grid in dim dimensions of - // extent $[-15,15]$. We refine the mesh more - // in the center of the domain since the - // solution is concentrated there. We first - // refine all cells whose center is within a - // radius of 11, and then refine once more for - // a radius 6. This is simple ad-hoc - // refinement could be done better by adapting - // the mesh to the solution using error - // estimators during the time stepping as done - // in other example programs, and using - // parallel::distributed::SolutionTransfer to - // transfer the solution to the new mesh. + //@sect4{SineGordonProblem::make_grid_and_dofs} + + // As in step-25 this functions sets up a cube + // grid in dim dimensions of + // extent $[-15,15]$. We refine the mesh more + // in the center of the domain since the + // solution is concentrated there. We first + // refine all cells whose center is within a + // radius of 11, and then refine once more for + // a radius 6. This is simple ad-hoc + // refinement could be done better by adapting + // the mesh to the solution using error + // estimators during the time stepping as done + // in other example programs, and using + // parallel::distributed::SolutionTransfer to + // transfer the solution to the new mesh. template void SineGordonProblem::make_grid_and_dofs () { @@ -421,8 +421,8 @@ namespace Step48 triangulation.refine_global (n_global_refinements); { typename Triangulation::active_cell_iterator - cell = triangulation.begin_active(), - end_cell = triangulation.end(); + cell = triangulation.begin_active(), + end_cell = triangulation.end(); for ( ; cell != end_cell; ++cell) if (cell->is_locally_owned()) if (cell->center().norm() < 11) @@ -453,27 +453,27 @@ namespace Step48 << std::endl; - // We generate hanging node constraints for - // ensuring continuity of the solution. As in - // step-40, we need to equip the constraint - // matrix with the IndexSet of locally - // relevant degrees of freedom to avoid it to - // consume too much memory for big - // problems. Next, the MatrixFree - // for the problem is set up. Note - // that we specify the MPI communicator which - // we are going to use, and that we also want - // to use shared-memory parallelization (hence - // one would use multithreading for intra-node - // parallelism and not MPI; note that we here - // choose the standard option — if we - // wanted to disable shared memory - // parallelization, we would choose @p - // none). Finally, three solution vectors are - // initialized. MatrixFree stores the - // layout that is to be used by distributed - // vectors, so we just ask it to initialize - // the vectors. + // We generate hanging node constraints for + // ensuring continuity of the solution. As in + // step-40, we need to equip the constraint + // matrix with the IndexSet of locally + // relevant degrees of freedom to avoid it to + // consume too much memory for big + // problems. Next, the MatrixFree + // for the problem is set up. Note + // that we specify the MPI communicator which + // we are going to use, and that we also want + // to use shared-memory parallelization (hence + // one would use multithreading for intra-node + // parallelism and not MPI; note that we here + // choose the standard option — if we + // wanted to disable shared memory + // parallelization, we would choose @p + // none). Finally, three solution vectors are + // initialized. MatrixFree stores the + // layout that is to be used by distributed + // vectors, so we just ask it to initialize + // the vectors. DoFTools::extract_locally_relevant_dofs (dof_handler, locally_relevant_dofs); constraints.clear(); @@ -488,7 +488,7 @@ namespace Step48 MatrixFree::AdditionalData::partition_partition; matrix_free_data.reinit (dof_handler, constraints, - quadrature, additional_data); + quadrature, additional_data); matrix_free_data.initialize_dof_vector (solution); old_solution.reinit (solution); @@ -497,30 +497,30 @@ namespace Step48 - //@sect4{SineGordonProblem::output_results} - - // This function prints the norm of the - // solution and writes the solution vector to - // a file. The norm is standard (except for - // the fact that we need to be sure to only - // count norms on locally owned cells), and - // the second is similar to what we did in - // step-40. However, we first need to generate - // an appropriate vector for output: The ones - // we used during time stepping contained - // information about ghosts dofs that one - // needs write access to during the loops over - // cell. However, that is not the same as - // needed when outputting. So we first - // initialize a vector with locally relevant - // degrees of freedom by copying the solution - // (note how we use the function @p copy_from - // to transfer data between vectors with the - // same local range, but different layouts of - // ghosts). Then, we import the values on the - // ghost DoFs and then distribute the - // constraints (as constraints are zero in the - // vectors during loop over all cells). + //@sect4{SineGordonProblem::output_results} + + // This function prints the norm of the + // solution and writes the solution vector to + // a file. The norm is standard (except for + // the fact that we need to be sure to only + // count norms on locally owned cells), and + // the second is similar to what we did in + // step-40. However, we first need to generate + // an appropriate vector for output: The ones + // we used during time stepping contained + // information about ghosts dofs that one + // needs write access to during the loops over + // cell. However, that is not the same as + // needed when outputting. So we first + // initialize a vector with locally relevant + // degrees of freedom by copying the solution + // (note how we use the function @p copy_from + // to transfer data between vectors with the + // same local range, but different layouts of + // ghosts). Then, we import the values on the + // ghost DoFs and then distribute the + // constraints (as constraints are zero in the + // vectors during loop over all cells). template void SineGordonProblem::output_results (const unsigned int timestep_number) const @@ -580,28 +580,28 @@ namespace Step48 } - // @sect4{SineGordonProblem::run} - - // This function is called by the main - // function and calls the subroutines - // of the class. - // - // The first step is to set up the grid and - // the cell operator. Then, the time step is - // computed from the CFL number given in the - // constructor and the finest mesh size. The - // finest mesh size is computed as the - // diameter of the last cell in the - // triangulation, which is the last cell on - // the finest level of the mesh. This is only - // possible for Cartesian meshes, otherwise, - // one needs to loop over all cells). Note - // that we need to query all the processors - // for their finest cell since the not all - // processors might hold a region where the - // mesh is at the finest level. Then, we - // readjust the time step a little to hit the - // final time exactly if necessary. + // @sect4{SineGordonProblem::run} + + // This function is called by the main + // function and calls the subroutines + // of the class. + // + // The first step is to set up the grid and + // the cell operator. Then, the time step is + // computed from the CFL number given in the + // constructor and the finest mesh size. The + // finest mesh size is computed as the + // diameter of the last cell in the + // triangulation, which is the last cell on + // the finest level of the mesh. This is only + // possible for Cartesian meshes, otherwise, + // one needs to loop over all cells). Note + // that we need to query all the processors + // for their finest cell since the not all + // processors might hold a region where the + // mesh is at the finest level. Then, we + // readjust the time step a little to hit the + // final time exactly if necessary. template void SineGordonProblem::run () @@ -617,24 +617,24 @@ namespace Step48 pcout << " Time step size: " << time_step << ", finest cell: " << global_min_cell_diameter << std::endl << std::endl; - // Next the initial value is set. Since we - // have a two-step time stepping method, we - // also need a value of the solution at - // time-time_step. For accurate results, one - // would need to compute this from the time - // derivative of the solution at initial time, - // but here we ignore this difficulty and just - // set it to the initial value function at - // that artificial time. - - // We create an output of the initial - // value. Then we also need to collect - // the two starting solutions in an STL - // vector of pointers field and to set up - // an instance of the - // SineGordonOperation class - // based on the finite element degree - // specified at the top of this file. + // Next the initial value is set. Since we + // have a two-step time stepping method, we + // also need a value of the solution at + // time-time_step. For accurate results, one + // would need to compute this from the time + // derivative of the solution at initial time, + // but here we ignore this difficulty and just + // set it to the initial value function at + // that artificial time. + + // We create an output of the initial + // value. Then we also need to collect + // the two starting solutions in an STL + // vector of pointers field and to set up + // an instance of the + // SineGordonOperation class + // based on the finite element degree + // specified at the top of this file. VectorTools::interpolate (dof_handler, ExactSolution (1, time), solution); @@ -650,39 +650,39 @@ namespace Step48 SineGordonOperation sine_gordon_op (matrix_free_data, time_step); - // Now loop over the time steps. In each - // iteration, we shift the solution - // vectors by one and call the - // apply function of the - // SineGordonOperator . Then, we - // write the solution to a file. We clock - // the wall times for the computational - // time needed as wall as the time needed - // to create the output and report the - // numbers when the time stepping is - // finished. - // - // Note how this shift is implemented: We - // simply call the swap method on the two - // vectors which swaps only some pointers - // without the need to copy data - // around. Obviously, this is a more - // efficient way to move data around. Let - // us see what happens in more detail: - // First, we exchange - // old_solution with - // old_old_solution, which - // means that - // old_old_solution gets - // old_solution, which is - // what we expect. Similarly, - // old_solution gets the - // content from solution in - // the next step. Afterward, - // solution holds - // old_old_solution, but - // that will be overwritten during this - // step. + // Now loop over the time steps. In each + // iteration, we shift the solution + // vectors by one and call the + // apply function of the + // SineGordonOperator . Then, we + // write the solution to a file. We clock + // the wall times for the computational + // time needed as wall as the time needed + // to create the output and report the + // numbers when the time stepping is + // finished. + // + // Note how this shift is implemented: We + // simply call the swap method on the two + // vectors which swaps only some pointers + // without the need to copy data + // around. Obviously, this is a more + // efficient way to move data around. Let + // us see what happens in more detail: + // First, we exchange + // old_solution with + // old_old_solution, which + // means that + // old_old_solution gets + // old_solution, which is + // what we expect. Similarly, + // old_solution gets the + // content from solution in + // the next step. Afterward, + // solution holds + // old_old_solution, but + // that will be overwritten during this + // step. unsigned int timestep_number = 1; Timer timer; @@ -720,10 +720,10 @@ namespace Step48 - // @sect3{The main function} +// @sect3{The main function} - // This is as in all other programs: -int main (int argc, char ** argv) +// This is as in all other programs: +int main (int argc, char **argv) { using namespace Step48; using namespace dealii; diff --git a/deal.II/examples/step-5/step-5.cc b/deal.II/examples/step-5/step-5.cc index ff03e580f5..0b059c108d 100644 --- a/deal.II/examples/step-5/step-5.cc +++ b/deal.II/examples/step-5/step-5.cc @@ -9,11 +9,11 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // Again, the first few include files - // are already known, so we won't - // comment on them: +// Again, the first few include files +// are already known, so we won't +// comment on them: #include #include #include @@ -35,123 +35,123 @@ #include #include - // This one is new. We want to read a - // triangulation from disk, and the - // class which does this is declared - // in the following file: +// This one is new. We want to read a +// triangulation from disk, and the +// class which does this is declared +// in the following file: #include - // We will use a circular domain, and - // the object describing the boundary - // of it comes from this file: +// We will use a circular domain, and +// the object describing the boundary +// of it comes from this file: #include - // This is C++ ... +// This is C++ ... #include - // ... and this is too: We will - // convert integers to strings using - // the C++ stringstream class - // ostringstream: +// ... and this is too: We will +// convert integers to strings using +// the C++ stringstream class +// ostringstream: #include - // Finally, this has been discussed - // in previous tutorial programs - // before: +// Finally, this has been discussed +// in previous tutorial programs +// before: using namespace dealii; - // @sect3{The Step5 class template} +// @sect3{The Step5 class template} - // The main class is mostly as in the - // previous example. The most visible - // change is that the function - // make_grid_and_dofs has been - // removed, since creating the grid - // is now done in the run - // function and the rest of its - // functionality is now in - // setup_system. Apart from this, - // everything is as before. +// The main class is mostly as in the +// previous example. The most visible +// change is that the function +// make_grid_and_dofs has been +// removed, since creating the grid +// is now done in the run +// function and the rest of its +// functionality is now in +// setup_system. Apart from this, +// everything is as before. template class Step5 { - public: - Step5 (); - void run (); +public: + Step5 (); + void run (); - private: - void setup_system (); - void assemble_system (); - void solve (); - void output_results (const unsigned int cycle) const; +private: + void setup_system (); + void assemble_system (); + void solve (); + void output_results (const unsigned int cycle) const; - Triangulation triangulation; - FE_Q fe; - DoFHandler dof_handler; + Triangulation triangulation; + FE_Q fe; + DoFHandler dof_handler; - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; - Vector solution; - Vector system_rhs; + Vector solution; + Vector system_rhs; }; - // @sect3{Nonconstant coefficients, using Assert} - - // In step-4, we showed how to use - // non-constant boundary values and - // right hand side. In this example, - // we want to use a variable - // coefficient in the elliptic - // operator instead. Of course, the - // suitable object is a Function, - // as we have used for the right hand - // side and boundary values in the - // last example. We will use it - // again, but we implement another - // function value_list which - // takes a list of points and returns - // the values of the function at - // these points as a list. The reason - // why such a function is reasonable - // although we can get all the - // information from the value - // function as well will be explained - // below when assembling the matrix. - // - // The need to declare a seemingly - // useless default constructor exists - // here just as in the previous - // example. +// @sect3{Nonconstant coefficients, using Assert} + +// In step-4, we showed how to use +// non-constant boundary values and +// right hand side. In this example, +// we want to use a variable +// coefficient in the elliptic +// operator instead. Of course, the +// suitable object is a Function, +// as we have used for the right hand +// side and boundary values in the +// last example. We will use it +// again, but we implement another +// function value_list which +// takes a list of points and returns +// the values of the function at +// these points as a list. The reason +// why such a function is reasonable +// although we can get all the +// information from the value +// function as well will be explained +// below when assembling the matrix. +// +// The need to declare a seemingly +// useless default constructor exists +// here just as in the previous +// example. template class Coefficient : public Function { - public: - Coefficient () : Function() {} +public: + Coefficient () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; - // This is the implementation of the - // coefficient function for a single - // point. We let it return 20 if the - // distance to the origin is less - // than 0.5, and 1 otherwise. As in - // the previous example, we simply - // ignore the second parameter of the - // function that is used to denote - // different components of - // vector-valued functions (we deal - // only with a scalar function here, - // after all): +// This is the implementation of the +// coefficient function for a single +// point. We let it return 20 if the +// distance to the origin is less +// than 0.5, and 1 otherwise. As in +// the previous example, we simply +// ignore the second parameter of the +// function that is used to denote +// different components of +// vector-valued functions (we deal +// only with a scalar function here, +// after all): template double Coefficient::value (const Point &p, const unsigned int /*component*/) const @@ -164,109 +164,109 @@ double Coefficient::value (const Point &p, - // And this is the function that - // returns the value of the - // coefficient at a whole list of - // points at once. Of course, we need - // to make sure that the values are - // the same as if we would ask the - // value function for each point - // individually. - // - // This method takes three - // parameters: a list of points at - // which to evaluate the function, a - // list that will hold the values at - // these points, and the vector - // component that should be zero here - // since we only have a single scalar - // function. Now, of course the size - // of the output array (values) - // must be the same as that of the - // input array (points), and we - // could simply assume that. However, - // in practice, it turns out that - // more than 90 per cent of - // programming errors are invalid - // function parameters such as - // invalid array sizes, etc, so we - // should try to make sure that the - // parameters are valid. For this, - // the Assert macro is a good means, - // since it makes sure that the - // condition which is given as first - // argument is valid, and if not - // throws an exception (its second - // argument) which will usually - // terminate the program giving - // information where the error - // occurred and what the reason - // was. This generally reduces the - // time to find programming errors - // dramatically and we have found - // assertions an invaluable means to - // program fast. - // - // On the other hand, all these - // checks (there are more than 4200 - // of them in the library at present) - // should not slow down the program - // too much if you want to do large - // computations. To this end, the - // Assert macro is only used in - // debug mode and expands to nothing - // if in optimized mode. Therefore, - // while you test your program on - // small problems and debug it, the - // assertions will tell you where the - // problems are. Once your program - // is stable, you can switch off - // debugging and the program will run - // your real computations without the - // assertions and at maximum - // speed. (In fact, it turns out the - // switching off all the checks in - // the library that prevent you from - // calling functions with the wrong - // arguments by switching to - // optimized mode, makes most - // programs run faster by about a - // factor of four. This should, - // however, not try to induce you to - // always run in optimized mode: Most - // people who have tried that soon - // realize that they introduce lots - // of errors that would have easily - // been caught had they run the - // program in debug mode while - // developing.) For those who want to - // try: The way to switch from debug - // mode to optimized mode is to go - // edit the Makefile in this - // directory. It should have a line - // debug-mode = on; simply - // replace it by debug-mode = off - // and recompile your program. The - // output of the make program - // should already indicate to you - // that the program is now compiled - // in optimized mode, and it will - // later also be linked to libraries - // that have been compiled for - // optimized mode. - // - // Here, as has been said above, we - // would like to make sure that the - // size of the two arrays is equal, - // and if not throw an - // exception. Comparing the sizes of - // two arrays is one of the most - // frequent checks, which is why - // there is already an exception - // class ExcDimensionMismatch - // that takes the sizes of two - // vectors and prints some output in - // case the condition is violated: +// And this is the function that +// returns the value of the +// coefficient at a whole list of +// points at once. Of course, we need +// to make sure that the values are +// the same as if we would ask the +// value function for each point +// individually. +// +// This method takes three +// parameters: a list of points at +// which to evaluate the function, a +// list that will hold the values at +// these points, and the vector +// component that should be zero here +// since we only have a single scalar +// function. Now, of course the size +// of the output array (values) +// must be the same as that of the +// input array (points), and we +// could simply assume that. However, +// in practice, it turns out that +// more than 90 per cent of +// programming errors are invalid +// function parameters such as +// invalid array sizes, etc, so we +// should try to make sure that the +// parameters are valid. For this, +// the Assert macro is a good means, +// since it makes sure that the +// condition which is given as first +// argument is valid, and if not +// throws an exception (its second +// argument) which will usually +// terminate the program giving +// information where the error +// occurred and what the reason +// was. This generally reduces the +// time to find programming errors +// dramatically and we have found +// assertions an invaluable means to +// program fast. +// +// On the other hand, all these +// checks (there are more than 4200 +// of them in the library at present) +// should not slow down the program +// too much if you want to do large +// computations. To this end, the +// Assert macro is only used in +// debug mode and expands to nothing +// if in optimized mode. Therefore, +// while you test your program on +// small problems and debug it, the +// assertions will tell you where the +// problems are. Once your program +// is stable, you can switch off +// debugging and the program will run +// your real computations without the +// assertions and at maximum +// speed. (In fact, it turns out the +// switching off all the checks in +// the library that prevent you from +// calling functions with the wrong +// arguments by switching to +// optimized mode, makes most +// programs run faster by about a +// factor of four. This should, +// however, not try to induce you to +// always run in optimized mode: Most +// people who have tried that soon +// realize that they introduce lots +// of errors that would have easily +// been caught had they run the +// program in debug mode while +// developing.) For those who want to +// try: The way to switch from debug +// mode to optimized mode is to go +// edit the Makefile in this +// directory. It should have a line +// debug-mode = on; simply +// replace it by debug-mode = off +// and recompile your program. The +// output of the make program +// should already indicate to you +// that the program is now compiled +// in optimized mode, and it will +// later also be linked to libraries +// that have been compiled for +// optimized mode. +// +// Here, as has been said above, we +// would like to make sure that the +// size of the two arrays is equal, +// and if not throw an +// exception. Comparing the sizes of +// two arrays is one of the most +// frequent checks, which is why +// there is already an exception +// class ExcDimensionMismatch +// that takes the sizes of two +// vectors and prints some output in +// case the condition is violated: template void Coefficient::value_list (const std::vector > &points, @@ -275,72 +275,72 @@ void Coefficient::value_list (const std::vector > &points, { Assert (values.size() == points.size(), ExcDimensionMismatch (values.size(), points.size())); - // Since examples are not very good - // if they do not demonstrate their - // point, we will show how to - // trigger this exception at the - // end of the main program, and - // what output results from this - // (see the Results section of - // this example program). You will - // certainly notice that the output - // is quite well suited to quickly - // find what the problem is and - // what parameters are expected. An - // additional plus is that if the - // program is run inside a - // debugger, it will stop at the - // point where the exception is - // triggered, so you can go up the - // call stack to immediately find - // the place where the the array - // with the wrong size was set up. - - // While we're at it, we can do - // another check: the coefficient - // is a scalar, but the - // Function class also - // represents vector-valued - // function. A scalar function must - // therefore be considered as a - // vector-valued function with only - // one component, so the only valid - // component for which a user might - // ask is zero (we always count - // from zero). The following - // assertion checks this. If the - // condition in the Assert call - // is violated, an exception of - // type ExcRange will be - // triggered; that class takes the - // violating index as first - // argument, and the second and - // third arguments denote a range - // that includes the left point but - // is open at the right, i.e. here - // the interval [0,1). For integer - // arguments, this means that the - // only value in the range is the - // zero, of course. (The interval - // is half open since we also want - // to write exceptions like - // ExcRange(i,0,v.size()), - // where an index must be between - // zero but less than the size of - // an array. To save us the effort - // of writing v.size()-1 in - // many places, the range is - // defined as half-open.) + // Since examples are not very good + // if they do not demonstrate their + // point, we will show how to + // trigger this exception at the + // end of the main program, and + // what output results from this + // (see the Results section of + // this example program). You will + // certainly notice that the output + // is quite well suited to quickly + // find what the problem is and + // what parameters are expected. An + // additional plus is that if the + // program is run inside a + // debugger, it will stop at the + // point where the exception is + // triggered, so you can go up the + // call stack to immediately find + // the place where the the array + // with the wrong size was set up. + + // While we're at it, we can do + // another check: the coefficient + // is a scalar, but the + // Function class also + // represents vector-valued + // function. A scalar function must + // therefore be considered as a + // vector-valued function with only + // one component, so the only valid + // component for which a user might + // ask is zero (we always count + // from zero). The following + // assertion checks this. If the + // condition in the Assert call + // is violated, an exception of + // type ExcRange will be + // triggered; that class takes the + // violating index as first + // argument, and the second and + // third arguments denote a range + // that includes the left point but + // is open at the right, i.e. here + // the interval [0,1). For integer + // arguments, this means that the + // only value in the range is the + // zero, of course. (The interval + // is half open since we also want + // to write exceptions like + // ExcRange(i,0,v.size()), + // where an index must be between + // zero but less than the size of + // an array. To save us the effort + // of writing v.size()-1 in + // many places, the range is + // defined as half-open.) Assert (component == 0, ExcIndexRange (component, 0, 1)); - // The rest of the function is - // uneventful: we define - // n_q_points as an - // abbreviation for the number of - // points for which function values - // are requested, and then simply - // fill the output value: + // The rest of the function is + // uneventful: we define + // n_q_points as an + // abbreviation for the number of + // points for which function values + // are requested, and then simply + // fill the output value: const unsigned int n_points = points.size(); for (unsigned int i=0; i::value_list (const std::vector > &points, } - // @sect3{The Step5 class implementation} +// @sect3{The Step5 class implementation} - // @sect4{Step5::Step5} +// @sect4{Step5::Step5} - // This function is as before. +// This function is as before. template Step5::Step5 () : - fe (1), - dof_handler (triangulation) + fe (1), + dof_handler (triangulation) {} - // @sect4{Step5::setup_system} +// @sect4{Step5::setup_system} - // This is the function - // make_grid_and_dofs from the - // previous example, minus the - // generation of the grid. Everything - // else is unchanged: +// This is the function +// make_grid_and_dofs from the +// previous example, minus the +// generation of the grid. Everything +// else is unchanged: template void Step5::setup_system () { @@ -394,39 +394,39 @@ void Step5::setup_system () - // @sect4{Step5::assemble_system} - - // As in the previous examples, this - // function is not changed much with - // regard to its functionality, but - // there are still some optimizations - // which we will show. For this, it - // is important to note that if - // efficient solvers are used (such - // as the preconditions CG method), - // assembling the matrix and right - // hand side can take a comparable - // time, and you should think about - // using one or two optimizations at - // some places. - // - // What we will show here is how we - // can avoid calls to the - // shape_value, shape_grad, and - // quadrature_point functions of the - // FEValues object, and in particular - // optimize away most of the virtual - // function calls of the Function - // object. The way to do so will be - // explained in the following, while - // those parts of this function that - // are not changed with respect to - // the previous example are not - // commented on. - // - // The first parts of the function - // are completely unchanged from - // before: +// @sect4{Step5::assemble_system} + +// As in the previous examples, this +// function is not changed much with +// regard to its functionality, but +// there are still some optimizations +// which we will show. For this, it +// is important to note that if +// efficient solvers are used (such +// as the preconditions CG method), +// assembling the matrix and right +// hand side can take a comparable +// time, and you should think about +// using one or two optimizations at +// some places. +// +// What we will show here is how we +// can avoid calls to the +// shape_value, shape_grad, and +// quadrature_point functions of the +// FEValues object, and in particular +// optimize away most of the virtual +// function calls of the Function +// object. The way to do so will be +// explained in the following, while +// those parts of this function that +// are not changed with respect to +// the previous example are not +// commented on. +// +// The first parts of the function +// are completely unchanged from +// before: template void Step5::assemble_system () { @@ -444,91 +444,91 @@ void Step5::assemble_system () std::vector local_dof_indices (dofs_per_cell); - // Here is one difference: for this - // program, we will again use a - // constant right hand side - // function and zero boundary - // values, but a variable - // coefficient. We have already - // declared the class that - // represents this coefficient - // above, so we only have to - // declare a corresponding object - // here. - // - // Then, below, we will ask the - // coefficient function object - // to compute the values of the - // coefficient at all quadrature - // points on one cell at once. The - // reason for this is that, if you - // look back at how we did this in - // step-4, you will realize that we - // called the function computing - // the right hand side value inside - // nested loops over all degrees of - // freedom and over all quadrature - // points, - // i.e. dofs_per_cell*n_q_points - // times. For the coefficient that - // is used inside the matrix, this - // would actually be - // dofs_per_cell*dofs_per_cell*n_q_points. On - // the other hand, the function - // will of course return the same - // value every time it is called - // with the same quadrature point, - // independently of what shape - // function we presently treat; - // secondly, these are virtual - // function calls, so are rather - // expensive. Obviously, there are - // only n_q_point different values, - // and we shouldn't call the - // function more often than - // that. Or, even better than this, - // compute all of these values at - // once, and get away with a single - // function call per cell. - // - // This is exactly what we are - // going to do. For this, we need - // some space to store the values - // in. We therefore also have to - // declare an array to hold these - // values: + // Here is one difference: for this + // program, we will again use a + // constant right hand side + // function and zero boundary + // values, but a variable + // coefficient. We have already + // declared the class that + // represents this coefficient + // above, so we only have to + // declare a corresponding object + // here. + // + // Then, below, we will ask the + // coefficient function object + // to compute the values of the + // coefficient at all quadrature + // points on one cell at once. The + // reason for this is that, if you + // look back at how we did this in + // step-4, you will realize that we + // called the function computing + // the right hand side value inside + // nested loops over all degrees of + // freedom and over all quadrature + // points, + // i.e. dofs_per_cell*n_q_points + // times. For the coefficient that + // is used inside the matrix, this + // would actually be + // dofs_per_cell*dofs_per_cell*n_q_points. On + // the other hand, the function + // will of course return the same + // value every time it is called + // with the same quadrature point, + // independently of what shape + // function we presently treat; + // secondly, these are virtual + // function calls, so are rather + // expensive. Obviously, there are + // only n_q_point different values, + // and we shouldn't call the + // function more often than + // that. Or, even better than this, + // compute all of these values at + // once, and get away with a single + // function call per cell. + // + // This is exactly what we are + // going to do. For this, we need + // some space to store the values + // in. We therefore also have to + // declare an array to hold these + // values: const Coefficient coefficient; std::vector coefficient_values (n_q_points); - // Next is the typical loop over - // all cells to compute local - // contributions and then to - // transfer them into the global - // matrix and vector. - // - // The only two things in which - // this loop differs from step-4 is - // that we want to compute the - // value of the coefficient in all - // quadrature points on the present - // cell at the beginning, and then - // use it in the computation of the - // local contributions. This is - // what we do in the call to - // coefficient.value_list in - // the fourth line of the loop. - // - // The second change is how we make - // use of this coefficient in - // computing the cell matrix - // contributions. This is in the - // obvious way, and not worth more - // comments. For the right hand - // side, we use a constant value - // again. + // Next is the typical loop over + // all cells to compute local + // contributions and then to + // transfer them into the global + // matrix and vector. + // + // The only two things in which + // this loop differs from step-4 is + // that we want to compute the + // value of the coefficient in all + // quadrature points on the present + // cell at the beginning, and then + // use it in the computation of the + // local contributions. This is + // what we do in the call to + // coefficient.value_list in + // the fourth line of the loop. + // + // The second change is how we make + // use of this coefficient in + // computing the cell matrix + // contributions. This is in the + // obvious way, and not worth more + // comments. For the right hand + // side, we use a constant value + // again. typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { cell_matrix = 0; @@ -566,8 +566,8 @@ void Step5::assemble_system () } } - // With the matrix so built, we use - // zero boundary values again: + // With the matrix so built, we use + // zero boundary values again: std::map boundary_values; VectorTools::interpolate_boundary_values (dof_handler, 0, @@ -580,57 +580,57 @@ void Step5::assemble_system () } - // @sect4{Step5::solve} - - // The solution process again looks - // mostly like in the previous - // examples. However, we will now use - // a preconditioned conjugate - // gradient algorithm. It is not very - // difficult to make this change. In - // fact, the only thing we have to - // alter is that we need an object - // which will act as a - // preconditioner. We will use SSOR - // (symmetric successive - // overrelaxation), with a relaxation - // factor of 1.2. For this purpose, - // the SparseMatrix class has a - // function which does one SSOR step, - // and we need to package the address - // of this function together with the - // matrix on which it should act - // (which is the matrix to be - // inverted) and the relaxation - // factor into one object. The - // PreconditionSSOR class does - // this for us. (PreconditionSSOR - // class takes a template argument - // denoting the matrix type it is - // supposed to work on. The default - // value is SparseMatrix@, - // which is exactly what we need - // here, so we simply stick with the - // default and do not specify - // anything in the angle brackets.) - // - // Note that for the present case, - // SSOR doesn't really perform much - // better than most other - // preconditioners (though better - // than no preconditioning at all). A - // brief comparison of different - // preconditioners is presented in - // the Results section of the next - // tutorial program, step-6. - // - // With this, the rest of the - // function is trivial: instead of - // the PreconditionIdentity - // object we have created before, we - // now use the preconditioner we have - // declared, and the CG solver will - // do the rest for us: +// @sect4{Step5::solve} + +// The solution process again looks +// mostly like in the previous +// examples. However, we will now use +// a preconditioned conjugate +// gradient algorithm. It is not very +// difficult to make this change. In +// fact, the only thing we have to +// alter is that we need an object +// which will act as a +// preconditioner. We will use SSOR +// (symmetric successive +// overrelaxation), with a relaxation +// factor of 1.2. For this purpose, +// the SparseMatrix class has a +// function which does one SSOR step, +// and we need to package the address +// of this function together with the +// matrix on which it should act +// (which is the matrix to be +// inverted) and the relaxation +// factor into one object. The +// PreconditionSSOR class does +// this for us. (PreconditionSSOR +// class takes a template argument +// denoting the matrix type it is +// supposed to work on. The default +// value is SparseMatrix@, +// which is exactly what we need +// here, so we simply stick with the +// default and do not specify +// anything in the angle brackets.) +// +// Note that for the present case, +// SSOR doesn't really perform much +// better than most other +// preconditioners (though better +// than no preconditioning at all). A +// brief comparison of different +// preconditioners is presented in +// the Results section of the next +// tutorial program, step-6. +// +// With this, the rest of the +// function is trivial: instead of +// the PreconditionIdentity +// object we have created before, we +// now use the preconditioner we have +// declared, and the CG solver will +// do the rest for us: template void Step5::solve () { @@ -649,15 +649,15 @@ void Step5::solve () } - // @sect4{Step5::output_results and setting output flags} +// @sect4{Step5::output_results and setting output flags} - // Writing output to a file is mostly - // the same as for the previous - // example, but here we will show how - // to modify some output options and - // how to construct a different - // filename for each refinement - // cycle. +// Writing output to a file is mostly +// the same as for the previous +// example, but here we will show how +// to modify some output options and +// how to construct a different +// filename for each refinement +// cycle. template void Step5::output_results (const unsigned int cycle) const { @@ -668,126 +668,126 @@ void Step5::output_results (const unsigned int cycle) const data_out.build_patches (); - // For this example, we would like - // to write the output directly to - // a file in Encapsulated - // Postscript (EPS) format. The - // library supports this, but - // things may be a bit more - // difficult sometimes, since EPS - // is a printing format, unlike - // most other supported formats - // which serve as input for - // graphical tools. Therefore, you - // can't scale or rotate the image - // after it has been written to - // disk, and you have to decide - // about the viewpoint or the - // scaling in advance. - // - // The defaults in the library are - // usually quite reasonable, and - // regarding viewpoint and scaling - // they coincide with the defaults - // of Gnuplot. However, since this - // is a tutorial, we will - // demonstrate how to change - // them. For this, we first have to - // generate an object describing - // the flags for EPS output - // (similar flag classes exist for - // all supported output formats): + // For this example, we would like + // to write the output directly to + // a file in Encapsulated + // Postscript (EPS) format. The + // library supports this, but + // things may be a bit more + // difficult sometimes, since EPS + // is a printing format, unlike + // most other supported formats + // which serve as input for + // graphical tools. Therefore, you + // can't scale or rotate the image + // after it has been written to + // disk, and you have to decide + // about the viewpoint or the + // scaling in advance. + // + // The defaults in the library are + // usually quite reasonable, and + // regarding viewpoint and scaling + // they coincide with the defaults + // of Gnuplot. However, since this + // is a tutorial, we will + // demonstrate how to change + // them. For this, we first have to + // generate an object describing + // the flags for EPS output + // (similar flag classes exist for + // all supported output formats): DataOutBase::EpsFlags eps_flags; - // They are initialized with the - // default values, so we only have - // to change those that we don't - // like. For example, we would like - // to scale the z-axis differently - // (stretch each data point in - // z-direction by a factor of four): + // They are initialized with the + // default values, so we only have + // to change those that we don't + // like. For example, we would like + // to scale the z-axis differently + // (stretch each data point in + // z-direction by a factor of four): eps_flags.z_scaling = 4; - // Then we would also like to alter - // the viewpoint from which we look - // at the solution surface. The - // default is at an angle of 60 - // degrees down from the vertical - // axis, and 30 degrees rotated - // against it in mathematical - // positive sense. We raise our - // viewpoint a bit and look more - // along the y-axis: + // Then we would also like to alter + // the viewpoint from which we look + // at the solution surface. The + // default is at an angle of 60 + // degrees down from the vertical + // axis, and 30 degrees rotated + // against it in mathematical + // positive sense. We raise our + // viewpoint a bit and look more + // along the y-axis: eps_flags.azimut_angle = 40; eps_flags.turn_angle = 10; - // That shall suffice. There are - // more flags, for example whether - // to draw the mesh lines, which - // data vectors to use for - // colorization of the interior of - // the cells, and so on. You may - // want to take a look at the - // documentation of the EpsFlags - // structure to get an overview of - // what is possible. - // - // The only thing still to be done, - // is to tell the output object to - // use these flags: + // That shall suffice. There are + // more flags, for example whether + // to draw the mesh lines, which + // data vectors to use for + // colorization of the interior of + // the cells, and so on. You may + // want to take a look at the + // documentation of the EpsFlags + // structure to get an overview of + // what is possible. + // + // The only thing still to be done, + // is to tell the output object to + // use these flags: data_out.set_flags (eps_flags); - // The above way to modify flags - // requires recompilation each time - // we would like to use different - // flags. This is inconvenient, and - // we will see more advanced ways - // in step-19 where the output - // flags are determined at run time - // using an input file (step-19 - // doesn't show many other things; - // you should feel free to read - // over it even if you haven't done - // step-6 to step-18 yet). - - // Finally, we need the filename to - // which the results are to be - // written. We would like to have - // it of the form - // solution-N.eps, where N is - // the number of the refinement - // cycle. Thus, we have to convert - // an integer to a part of a - // string; this can be done using - // the sprintf function, but in - // C++ there is a more elegant way: - // write everything into a special - // stream (just like writing into a - // file or to the screen) and - // retrieve what you wrote as a - // string. This applies the usual - // conversions from integer to - // strings, and one could as well - // use stream modifiers such as - // setw, setprecision, and - // so on. In C++, you can do this - // by using the so-called stringstream - // classes: + // The above way to modify flags + // requires recompilation each time + // we would like to use different + // flags. This is inconvenient, and + // we will see more advanced ways + // in step-19 where the output + // flags are determined at run time + // using an input file (step-19 + // doesn't show many other things; + // you should feel free to read + // over it even if you haven't done + // step-6 to step-18 yet). + + // Finally, we need the filename to + // which the results are to be + // written. We would like to have + // it of the form + // solution-N.eps, where N is + // the number of the refinement + // cycle. Thus, we have to convert + // an integer to a part of a + // string; this can be done using + // the sprintf function, but in + // C++ there is a more elegant way: + // write everything into a special + // stream (just like writing into a + // file or to the screen) and + // retrieve what you wrote as a + // string. This applies the usual + // conversions from integer to + // strings, and one could as well + // use stream modifiers such as + // setw, setprecision, and + // so on. In C++, you can do this + // by using the so-called stringstream + // classes: std::ostringstream filename; - // In order to now actually - // generate a filename, we fill the - // stringstream variable with the - // base of the filename, then the - // number part, and finally the - // suffix indicating the file type: + // In order to now actually + // generate a filename, we fill the + // stringstream variable with the + // base of the filename, then the + // number part, and finally the + // suffix indicating the file type: filename << "solution-" << cycle << ".eps"; - // We can get whatever we wrote to the - // stream using the str() function. The - // result is a string which we have to - // convert to a char* using the c_str() - // function. Use that as filename for the - // output stream and then write the data to - // the file: + // We can get whatever we wrote to the + // stream using the str() function. The + // result is a string which we have to + // convert to a char* using the c_str() + // function. Use that as filename for the + // output stream and then write the data to + // the file: std::ofstream output (filename.str().c_str()); data_out.write_eps (output); @@ -795,122 +795,122 @@ void Step5::output_results (const unsigned int cycle) const - // @sect4{Step5::run} - - // The second to last thing in this - // program is the definition of the - // run() function. In contrast to - // the previous programs, we will - // compute on a sequence of meshes - // that after each iteration is - // globall refined. The function - // therefore consists of a loop over - // 6 cycles. In each cycle, we first - // print the cycle number, and then - // have to decide what to do with the - // mesh. If this is not the first - // cycle, we simply refine the - // existing mesh once - // globally. Before running through - // these cycles, however, - // we have to generate a mesh: - - // In previous examples, we have - // already used some of the functions - // from the - // GridGenerator - // class. Here we would like to read - // a grid from a file where the cells - // are stored and which may originate - // from someone else, or may be the - // product of a mesh generator tool. - // - // In order to read a grid from a - // file, we generate an object of - // data type GridIn and associate the - // triangulation to it (i.e. we tell - // it to fill our triangulation - // object when we ask it to read the - // file). Then we open the respective - // file and initialize the - // triangulation with the data in the - // file: +// @sect4{Step5::run} + +// The second to last thing in this +// program is the definition of the +// run() function. In contrast to +// the previous programs, we will +// compute on a sequence of meshes +// that after each iteration is +// globall refined. The function +// therefore consists of a loop over +// 6 cycles. In each cycle, we first +// print the cycle number, and then +// have to decide what to do with the +// mesh. If this is not the first +// cycle, we simply refine the +// existing mesh once +// globally. Before running through +// these cycles, however, +// we have to generate a mesh: + +// In previous examples, we have +// already used some of the functions +// from the +// GridGenerator +// class. Here we would like to read +// a grid from a file where the cells +// are stored and which may originate +// from someone else, or may be the +// product of a mesh generator tool. +// +// In order to read a grid from a +// file, we generate an object of +// data type GridIn and associate the +// triangulation to it (i.e. we tell +// it to fill our triangulation +// object when we ask it to read the +// file). Then we open the respective +// file and initialize the +// triangulation with the data in the +// file: template void Step5::run () { GridIn grid_in; grid_in.attach_triangulation (triangulation); std::ifstream input_file("circle-grid.inp"); - // We would now like to read the - // file. However, the input file is - // only for a two-dimensional - // triangulation, while this - // function is a template for - // arbitrary dimension. Since this - // is only a demonstration program, - // we will not use different input - // files for the different - // dimensions, but rather kill the - // whole program if we are not in - // 2D: + // We would now like to read the + // file. However, the input file is + // only for a two-dimensional + // triangulation, while this + // function is a template for + // arbitrary dimension. Since this + // is only a demonstration program, + // we will not use different input + // files for the different + // dimensions, but rather kill the + // whole program if we are not in + // 2D: Assert (dim==2, ExcInternalError()); - // ExcInternalError is a globally - // defined exception, which may be - // thrown whenever something is - // terribly wrong. Usually, one - // would like to use more specific - // exceptions, and particular in - // this case one would of course - // try to do something else if - // dim is not equal to - // two, e.g. create a grid using - // library functions. Aborting a - // program is usually not a good - // idea and assertions should - // really only be used for - // exceptional cases which should - // not occur, but might due to - // stupidity of the programmer, - // user, or someone else. The - // situation above is not a very - // clever use of Assert, but again: - // this is a tutorial and it might - // be worth to show what not to do, - // after all. - - // So if we got past the assertion, - // we know that dim==2, and we can - // now actually read the grid. It - // is in UCD (unstructured cell - // data) format (but the ending of - // the UCD-file is - // inp), as supported - // as input format by the AVS - // Explorer (a visualization - // program), for example: + // ExcInternalError is a globally + // defined exception, which may be + // thrown whenever something is + // terribly wrong. Usually, one + // would like to use more specific + // exceptions, and particular in + // this case one would of course + // try to do something else if + // dim is not equal to + // two, e.g. create a grid using + // library functions. Aborting a + // program is usually not a good + // idea and assertions should + // really only be used for + // exceptional cases which should + // not occur, but might due to + // stupidity of the programmer, + // user, or someone else. The + // situation above is not a very + // clever use of Assert, but again: + // this is a tutorial and it might + // be worth to show what not to do, + // after all. + + // So if we got past the assertion, + // we know that dim==2, and we can + // now actually read the grid. It + // is in UCD (unstructured cell + // data) format (but the ending of + // the UCD-file is + // inp), as supported + // as input format by the AVS + // Explorer (a visualization + // program), for example: grid_in.read_ucd (input_file); - // If you like to use another input - // format, you have to use an other - // grid_in.read_xxx - // function. (See the documentation - // of the GridIn class - // to find out what input formats - // are presently supported.) - - // The grid in the file describes a - // circle. Therefore we have to use - // a boundary object which tells - // the triangulation where to put - // new points on the boundary when - // the grid is refined. This works - // in the same way as in the first - // example. Note that the - // HyperBallBoundary constructor - // takes two parameters, the center - // of the ball and the radius, but - // that their default (the origin - // and 1.0) are the ones which we - // would like to use here. + // If you like to use another input + // format, you have to use an other + // grid_in.read_xxx + // function. (See the documentation + // of the GridIn class + // to find out what input formats + // are presently supported.) + + // The grid in the file describes a + // circle. Therefore we have to use + // a boundary object which tells + // the triangulation where to put + // new points on the boundary when + // the grid is refined. This works + // in the same way as in the first + // example. Note that the + // HyperBallBoundary constructor + // takes two parameters, the center + // of the ball and the radius, but + // that their default (the origin + // and 1.0) are the ones which we + // would like to use here. static const HyperBallBoundary boundary; triangulation.set_boundary (0, boundary); @@ -921,11 +921,11 @@ void Step5::run () if (cycle != 0) triangulation.refine_global (1); - // Now that we have a mesh for - // sure, we write some output - // and do all the things that - // we have already seen in the - // previous examples. + // Now that we have a mesh for + // sure, we write some output + // and do all the things that + // we have already seen in the + // previous examples. std::cout << " Number of active cells: " << triangulation.n_active_cells() << std::endl @@ -941,12 +941,12 @@ void Step5::run () } - // @sect3{The main function} +// @sect3{The main function} - // The main function looks mostly - // like the one in the previous - // example, so we won't comment on it - // further: +// The main function looks mostly +// like the one in the previous +// example, so we won't comment on it +// further: int main () { deallog.depth_console (0); @@ -954,34 +954,34 @@ int main () Step5<2> laplace_problem_2d; laplace_problem_2d.run (); - // Finally, we have promised to - // trigger an exception in the - // Coefficient class through - // the Assert macro we have - // introduced there. For this, we - // have to call its value_list - // function with two arrays of - // different size (the number in - // parentheses behind the - // declaration of the object). We - // have commented out these lines - // in order to allow the program to - // exit gracefully in normal - // situations (we use the program - // in day-to-day testing of changes - // to the library as well), so you - // will only get the exception by - // un-commenting the following - // lines. Take a look at the - // Results section of the program - // to see what happens when the - // code is actually run: -/* - Coefficient<2> coefficient; - std::vector > points (2); - std::vector coefficient_values (1); - coefficient.value_list (points, coefficient_values); -*/ + // Finally, we have promised to + // trigger an exception in the + // Coefficient class through + // the Assert macro we have + // introduced there. For this, we + // have to call its value_list + // function with two arrays of + // different size (the number in + // parentheses behind the + // declaration of the object). We + // have commented out these lines + // in order to allow the program to + // exit gracefully in normal + // situations (we use the program + // in day-to-day testing of changes + // to the library as well), so you + // will only get the exception by + // un-commenting the following + // lines. Take a look at the + // Results section of the program + // to see what happens when the + // code is actually run: + /* + Coefficient<2> coefficient; + std::vector > points (2); + std::vector coefficient_values (1); + coefficient.value_list (points, coefficient_values); + */ return 0; } diff --git a/deal.II/examples/step-6/step-6.cc b/deal.II/examples/step-6/step-6.cc index 71eebe9f80..9a3665a43e 100644 --- a/deal.II/examples/step-6/step-6.cc +++ b/deal.II/examples/step-6/step-6.cc @@ -9,12 +9,12 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // The first few files have already - // been covered in previous examples - // and will thus not be further - // commented on. +// The first few files have already +// been covered in previous examples +// and will thus not be further +// commented on. #include #include #include @@ -40,140 +40,140 @@ #include #include - // From the following include file we - // will import the declaration of - // H1-conforming finite element shape - // functions. This family of finite - // elements is called FE_Q, and - // was used in all examples before - // already to define the usual bi- or - // tri-linear elements, but we will - // now use it for bi-quadratic - // elements: +// From the following include file we +// will import the declaration of +// H1-conforming finite element shape +// functions. This family of finite +// elements is called FE_Q, and +// was used in all examples before +// already to define the usual bi- or +// tri-linear elements, but we will +// now use it for bi-quadratic +// elements: #include - // We will not read the grid from a - // file as in the previous example, - // but generate it using a function - // of the library. However, we will - // want to write out the locally - // refined grids (just the grid, not - // the solution) in each step, so we - // need the following include file - // instead of grid_in.h: +// We will not read the grid from a +// file as in the previous example, +// but generate it using a function +// of the library. However, we will +// want to write out the locally +// refined grids (just the grid, not +// the solution) in each step, so we +// need the following include file +// instead of grid_in.h: #include - // When using locally refined grids, we will - // get so-called hanging - // nodes. However, the standard finite - // element methods assumes that the discrete - // solution spaces be continuous, so we need - // to make sure that the degrees of freedom - // on hanging nodes conform to some - // constraints such that the global solution - // is continuous. We are also going to store - // the boundary conditions in this - // object. The following file contains a - // class which is used to handle these - // constraints: +// When using locally refined grids, we will +// get so-called hanging +// nodes. However, the standard finite +// element methods assumes that the discrete +// solution spaces be continuous, so we need +// to make sure that the degrees of freedom +// on hanging nodes conform to some +// constraints such that the global solution +// is continuous. We are also going to store +// the boundary conditions in this +// object. The following file contains a +// class which is used to handle these +// constraints: #include - // In order to refine our grids - // locally, we need a function from - // the library that decides which - // cells to flag for refinement or - // coarsening based on the error - // indicators we have computed. This - // function is defined here: +// In order to refine our grids +// locally, we need a function from +// the library that decides which +// cells to flag for refinement or +// coarsening based on the error +// indicators we have computed. This +// function is defined here: #include - // Finally, we need a simple way to - // actually compute the refinement - // indicators based on some error - // estimat. While in general, - // adaptivity is very - // problem-specific, the error - // indicator in the following file - // often yields quite nicely adapted - // grids for a wide class of - // problems. +// Finally, we need a simple way to +// actually compute the refinement +// indicators based on some error +// estimat. While in general, +// adaptivity is very +// problem-specific, the error +// indicator in the following file +// often yields quite nicely adapted +// grids for a wide class of +// problems. #include - // Finally, this is as in previous - // programs: +// Finally, this is as in previous +// programs: using namespace dealii; - // @sect3{The Step6 class template} - - // The main class is again almost - // unchanged. Two additions, however, - // are made: we have added the - // refine_grid function, which is - // used to adaptively refine the grid - // (instead of the global refinement - // in the previous examples), and a - // variable which will hold the - // constraints. In addition, we - // have added a destructor to the - // class for reasons that will become - // clear when we discuss its - // implementation. +// @sect3{The Step6 class template} + +// The main class is again almost +// unchanged. Two additions, however, +// are made: we have added the +// refine_grid function, which is +// used to adaptively refine the grid +// (instead of the global refinement +// in the previous examples), and a +// variable which will hold the +// constraints. In addition, we +// have added a destructor to the +// class for reasons that will become +// clear when we discuss its +// implementation. template class Step6 { - public: - Step6 (); - ~Step6 (); +public: + Step6 (); + ~Step6 (); - void run (); + void run (); - private: - void setup_system (); - void assemble_system (); - void solve (); - void refine_grid (); - void output_results (const unsigned int cycle) const; +private: + void setup_system (); + void assemble_system (); + void solve (); + void refine_grid (); + void output_results (const unsigned int cycle) const; - Triangulation triangulation; + Triangulation triangulation; - DoFHandler dof_handler; - FE_Q fe; + DoFHandler dof_handler; + FE_Q fe; - // This is the new variable in - // the main class. We need an - // object which holds a list of - // constraints to hold the - // hanging nodes and the - // boundary conditions. - ConstraintMatrix constraints; + // This is the new variable in + // the main class. We need an + // object which holds a list of + // constraints to hold the + // hanging nodes and the + // boundary conditions. + ConstraintMatrix constraints; - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; - Vector solution; - Vector system_rhs; + Vector solution; + Vector system_rhs; }; - // @sect3{Nonconstant coefficients} +// @sect3{Nonconstant coefficients} - // The implementation of nonconstant - // coefficients is copied verbatim - // from step-5: +// The implementation of nonconstant +// coefficients is copied verbatim +// from step-5: template class Coefficient : public Function { - public: - Coefficient () : Function() {} +public: + Coefficient () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; @@ -213,152 +213,152 @@ void Coefficient::value_list (const std::vector > &points, } - // @sect3{The Step6 class implementation} +// @sect3{The Step6 class implementation} - // @sect4{Step6::Step6} +// @sect4{Step6::Step6} - // The constructor of this class is - // mostly the same as before, but - // this time we want to use the - // quadratic element. To do so, we - // only have to replace the - // constructor argument (which was - // 1 in all previous examples) by - // the desired polynomial degree - // (here 2): +// The constructor of this class is +// mostly the same as before, but +// this time we want to use the +// quadratic element. To do so, we +// only have to replace the +// constructor argument (which was +// 1 in all previous examples) by +// the desired polynomial degree +// (here 2): template Step6::Step6 () - : - dof_handler (triangulation), - fe (2) + : + dof_handler (triangulation), + fe (2) {} - // @sect4{Step6::~Step6} - - // Here comes the added destructor of - // the class. The reason why we want - // to add it is a subtle change in - // the order of data elements in the - // class as compared to all previous - // examples: the dof_handler - // object was defined before and not - // after the fe object. Of course - // we could have left this order - // unchanged, but we would like to - // show what happens if the order is - // reversed since this produces a - // rather nasty side-effect and - // results in an error which is - // difficult to track down if one - // does not know what happens. - // - // Basically what happens is the - // following: when we distribute the - // degrees of freedom using the - // function call - // dof_handler.distribute_dofs(), - // the dof_handler also stores a - // pointer to the finite element in - // use. Since this pointer is used - // every now and then until either - // the degrees of freedom are - // re-distributed using another - // finite element object or until the - // dof_handler object is - // destroyed, it would be unwise if - // we would allow the finite element - // object to be deleted before the - // dof_handler object. To - // disallow this, the DoF handler - // increases a counter inside the - // finite element object which counts - // how many objects use that finite - // element (this is what the - // Subscriptor/SmartPointer - // class pair is used for, in case - // you want something like this for - // your own programs; see step-7 for - // a more complete discussion - // of this topic). The finite - // element object will refuse its - // destruction if that counter is - // larger than zero, since then some - // other objects might rely on the - // persistence of the finite element - // object. An exception will then be - // thrown and the program will - // usually abort upon the attempt to - // destroy the finite element. - // - // To be fair, such exceptions about - // still used objects are not - // particularly popular among - // programmers using deal.II, since - // they only tell us that something - // is wrong, namely that some other - // object is still using the object - // that is presently being - // destructed, but most of the time - // not who this user is. It is - // therefore often rather - // time-consuming to find out where - // the problem exactly is, although - // it is then usually straightforward - // to remedy the situation. However, - // we believe that the effort to find - // invalid references to objects that - // do no longer exist is less if the - // problem is detected once the - // reference becomes invalid, rather - // than when non-existent objects are - // actually accessed again, since - // then usually only invalid data is - // accessed, but no error is - // immediately raised. - // - // Coming back to the present - // situation, if we did not write - // this destructor, the compiler will - // generate code that triggers - // exactly the behavior sketched - // above. The reason is that member - // variables of the - // Step6 class are - // destructed bottom-up (i.e. in - // reverse order of their declaration - // in the class), as always in - // C++. Thus, the finite element - // object will be destructed before - // the DoF handler object, since its - // declaration is below the one of - // the DoF handler. This triggers the - // situation above, and an exception - // will be raised when the fe - // object is destructed. What needs - // to be done is to tell the - // dof_handler object to release - // its lock to the finite element. Of - // course, the dof_handler will - // only release its lock if it really - // does not need the finite element - // any more, i.e. when all finite - // element related data is deleted - // from it. For this purpose, the - // DoFHandler class has a - // function clear which deletes - // all degrees of freedom, and - // releases its lock to the finite - // element. After this, you can - // safely destruct the finite element - // object since its internal counter - // is then zero. - // - // For completeness, we add the - // output of the exception that would - // have been triggered without this - // destructor, to the end of the - // results section of this example. +// @sect4{Step6::~Step6} + +// Here comes the added destructor of +// the class. The reason why we want +// to add it is a subtle change in +// the order of data elements in the +// class as compared to all previous +// examples: the dof_handler +// object was defined before and not +// after the fe object. Of course +// we could have left this order +// unchanged, but we would like to +// show what happens if the order is +// reversed since this produces a +// rather nasty side-effect and +// results in an error which is +// difficult to track down if one +// does not know what happens. +// +// Basically what happens is the +// following: when we distribute the +// degrees of freedom using the +// function call +// dof_handler.distribute_dofs(), +// the dof_handler also stores a +// pointer to the finite element in +// use. Since this pointer is used +// every now and then until either +// the degrees of freedom are +// re-distributed using another +// finite element object or until the +// dof_handler object is +// destroyed, it would be unwise if +// we would allow the finite element +// object to be deleted before the +// dof_handler object. To +// disallow this, the DoF handler +// increases a counter inside the +// finite element object which counts +// how many objects use that finite +// element (this is what the +// Subscriptor/SmartPointer +// class pair is used for, in case +// you want something like this for +// your own programs; see step-7 for +// a more complete discussion +// of this topic). The finite +// element object will refuse its +// destruction if that counter is +// larger than zero, since then some +// other objects might rely on the +// persistence of the finite element +// object. An exception will then be +// thrown and the program will +// usually abort upon the attempt to +// destroy the finite element. +// +// To be fair, such exceptions about +// still used objects are not +// particularly popular among +// programmers using deal.II, since +// they only tell us that something +// is wrong, namely that some other +// object is still using the object +// that is presently being +// destructed, but most of the time +// not who this user is. It is +// therefore often rather +// time-consuming to find out where +// the problem exactly is, although +// it is then usually straightforward +// to remedy the situation. However, +// we believe that the effort to find +// invalid references to objects that +// do no longer exist is less if the +// problem is detected once the +// reference becomes invalid, rather +// than when non-existent objects are +// actually accessed again, since +// then usually only invalid data is +// accessed, but no error is +// immediately raised. +// +// Coming back to the present +// situation, if we did not write +// this destructor, the compiler will +// generate code that triggers +// exactly the behavior sketched +// above. The reason is that member +// variables of the +// Step6 class are +// destructed bottom-up (i.e. in +// reverse order of their declaration +// in the class), as always in +// C++. Thus, the finite element +// object will be destructed before +// the DoF handler object, since its +// declaration is below the one of +// the DoF handler. This triggers the +// situation above, and an exception +// will be raised when the fe +// object is destructed. What needs +// to be done is to tell the +// dof_handler object to release +// its lock to the finite element. Of +// course, the dof_handler will +// only release its lock if it really +// does not need the finite element +// any more, i.e. when all finite +// element related data is deleted +// from it. For this purpose, the +// DoFHandler class has a +// function clear which deletes +// all degrees of freedom, and +// releases its lock to the finite +// element. After this, you can +// safely destruct the finite element +// object since its internal counter +// is then zero. +// +// For completeness, we add the +// output of the exception that would +// have been triggered without this +// destructor, to the end of the +// results section of this example. template Step6::~Step6 () { @@ -366,44 +366,44 @@ Step6::~Step6 () } - // @sect4{Step6::setup_system} - - // The next function is setting up - // all the variables that describe - // the linear finite element problem, - // such as the DoF handler, the - // matrices, and vectors. The - // difference to what we did in - // step-5 is only that we now also - // have to take care of handing node - // constraints. These constraints are - // handled almost transparently by - // the library, i.e. you only need to - // know that they exist and how to - // get them, but you do not have to - // know how they are formed or what - // exactly is done with them. - // - // At the beginning of the function, - // you find all the things that are - // the same as in step-5: setting up - // the degrees of freedom (this time - // we have quadratic elements, but - // there is no difference from a user - // code perspective to the linear -- - // or cubic, for that matter -- - // case), generating the sparsity - // pattern, and initializing the - // solution and right hand side - // vectors. Note that the sparsity - // pattern will have significantly - // more entries per row now, since - // there are now 9 degrees of freedom - // per cell, not only four, that can - // couple with each other. The - // dof_Handler.max_couplings_between_dofs() - // call will take care of this, - // however: +// @sect4{Step6::setup_system} + +// The next function is setting up +// all the variables that describe +// the linear finite element problem, +// such as the DoF handler, the +// matrices, and vectors. The +// difference to what we did in +// step-5 is only that we now also +// have to take care of handing node +// constraints. These constraints are +// handled almost transparently by +// the library, i.e. you only need to +// know that they exist and how to +// get them, but you do not have to +// know how they are formed or what +// exactly is done with them. +// +// At the beginning of the function, +// you find all the things that are +// the same as in step-5: setting up +// the degrees of freedom (this time +// we have quadratic elements, but +// there is no difference from a user +// code perspective to the linear -- +// or cubic, for that matter -- +// case), generating the sparsity +// pattern, and initializing the +// solution and right hand side +// vectors. Note that the sparsity +// pattern will have significantly +// more entries per row now, since +// there are now 9 degrees of freedom +// per cell, not only four, that can +// couple with each other. The +// dof_Handler.max_couplings_between_dofs() +// call will take care of this, +// however: template void Step6::setup_system () { @@ -413,154 +413,154 @@ void Step6::setup_system () system_rhs.reinit (dof_handler.n_dofs()); - // After setting up all the degrees - // of freedoms, here are now the - // differences compared to step-5, - // all of which are related to - // constraints associated with the - // hanging nodes. In the class - // desclaration, we have already - // allocated space for an object - // constraints - // that will hold a list of these - // constraints (they form a matrix, - // which is reflected in the name - // of the class, but that is - // immaterial for the moment). Now - // we have to fill this - // object. This is done using the - // following function calls (the - // first clears the contents of the - // object that may still be left - // over from computations on the - // previous mesh before the last - // adaptive refinement): + // After setting up all the degrees + // of freedoms, here are now the + // differences compared to step-5, + // all of which are related to + // constraints associated with the + // hanging nodes. In the class + // desclaration, we have already + // allocated space for an object + // constraints + // that will hold a list of these + // constraints (they form a matrix, + // which is reflected in the name + // of the class, but that is + // immaterial for the moment). Now + // we have to fill this + // object. This is done using the + // following function calls (the + // first clears the contents of the + // object that may still be left + // over from computations on the + // previous mesh before the last + // adaptive refinement): constraints.clear (); DoFTools::make_hanging_node_constraints (dof_handler, constraints); - // Now we are ready to interpolate the - // ZeroFunction to our boundary with - // indicator 0 (the whole boundary) and - // store the resulting constraints in our - // constraints object. Note - // that we do not to apply the boundary - // conditions after assembly, like we did - // in earlier steps. As almost all the - // stuff, the interpolation of boundary - // values works also for higher order - // elements without the need to change your - // code for that. We note that for proper - // results, it is important that the - // elimination of boundary nodes from the - // system of equations happens *after* the - // elimination of hanging nodes. For that - // reason we are filling the boundary - // values into the ContraintMatrix after - // the hanging node constraints. + // Now we are ready to interpolate the + // ZeroFunction to our boundary with + // indicator 0 (the whole boundary) and + // store the resulting constraints in our + // constraints object. Note + // that we do not to apply the boundary + // conditions after assembly, like we did + // in earlier steps. As almost all the + // stuff, the interpolation of boundary + // values works also for higher order + // elements without the need to change your + // code for that. We note that for proper + // results, it is important that the + // elimination of boundary nodes from the + // system of equations happens *after* the + // elimination of hanging nodes. For that + // reason we are filling the boundary + // values into the ContraintMatrix after + // the hanging node constraints. VectorTools::interpolate_boundary_values (dof_handler, 0, ZeroFunction(), constraints); - // The next step is closing - // this object. After - // all constraints have been added, - // they need to be sorted and - // rearranged to perform some - // actions more efficiently. This - // postprocessing is done using the - // close() function, after which - // no further constraints may be - // added any more: + // The next step is closing + // this object. After + // all constraints have been added, + // they need to be sorted and + // rearranged to perform some + // actions more efficiently. This + // postprocessing is done using the + // close() function, after which + // no further constraints may be + // added any more: constraints.close (); - // Now we first build our compressed - // sparsity pattern like we did in the - // previous examples. Nevertheless, we do - // not copy it to the final sparsity - // pattern immediately. Note that we call - // a variant of make_sparsity_pattern that - // takes the ConstraintMatrix as the third - // argument. We are letting the routine - // know, the we will never write into the - // locations given by - // constraints by setting the - // argument - // keep_constrained_dofs to - // false. If we were to condense the - // constraints after assembling, we would - // have to pass true instead. + // Now we first build our compressed + // sparsity pattern like we did in the + // previous examples. Nevertheless, we do + // not copy it to the final sparsity + // pattern immediately. Note that we call + // a variant of make_sparsity_pattern that + // takes the ConstraintMatrix as the third + // argument. We are letting the routine + // know, the we will never write into the + // locations given by + // constraints by setting the + // argument + // keep_constrained_dofs to + // false. If we were to condense the + // constraints after assembling, we would + // have to pass true instead. CompressedSparsityPattern c_sparsity(dof_handler.n_dofs()); DoFTools::make_sparsity_pattern(dof_handler, - c_sparsity, - constraints, - false /*keep_constrained_dofs*/); - - // Now all non-zero entries of the - // matrix are known (i.e. those - // from regularly assembling the - // matrix and those that were - // introduced by eliminating - // constraints). We can thus copy - // our intermediate object to - // the sparsity pattern: + c_sparsity, + constraints, + false /*keep_constrained_dofs*/); + + // Now all non-zero entries of the + // matrix are known (i.e. those + // from regularly assembling the + // matrix and those that were + // introduced by eliminating + // constraints). We can thus copy + // our intermediate object to + // the sparsity pattern: sparsity_pattern.copy_from(c_sparsity); - // Finally, the so-constructed - // sparsity pattern serves as the - // basis on top of which we will - // create the sparse matrix: + // Finally, the so-constructed + // sparsity pattern serves as the + // basis on top of which we will + // create the sparse matrix: system_matrix.reinit (sparsity_pattern); } - // @sect4{Step6::assemble_system} - - // Next, we have to assemble the - // matrix again. There are two code - // changes compared to step-5: - // - // First, we have to use a higher-order - // quadrature formula to account for the - // higher polynomial degree in the finite - // element shape functions. This is easy to - // change: the constructor of the - // QGauss class takes the number - // of quadrature points in each space - // direction. Previously, we had two points - // for bilinear elements. Now we should use - // three points for biquadratic elements. - // - // Second, to copy the local matrix and - // vector on each cell into the global - // system, we are no longer using a - // hand-written loop. Instead, we use - // ConstraintMatrix::distribute_local_to_global - // that internally executes this loop and - // eliminates all the constraints at the same - // time. - // - // The rest of the code that forms the local - // contributions remains unchanged. It is - // worth noting, however, that under the hood - // several things are different than - // before. First, the variables - // dofs_per_cell and - // n_q_points now are 9 each, - // where they were 4 before. Introducing such - // variables as abbreviations is a good - // strategy to make code work with different - // elements without having to change too much - // code. Secondly, the fe_values - // object of course needs to do other things - // as well, since the shape functions are now - // quadratic, rather than linear, in each - // coordinate variable. Again, however, this - // is something that is completely - // transparent to user code and nothing that - // you have to worry about. +// @sect4{Step6::assemble_system} + +// Next, we have to assemble the +// matrix again. There are two code +// changes compared to step-5: +// +// First, we have to use a higher-order +// quadrature formula to account for the +// higher polynomial degree in the finite +// element shape functions. This is easy to +// change: the constructor of the +// QGauss class takes the number +// of quadrature points in each space +// direction. Previously, we had two points +// for bilinear elements. Now we should use +// three points for biquadratic elements. +// +// Second, to copy the local matrix and +// vector on each cell into the global +// system, we are no longer using a +// hand-written loop. Instead, we use +// ConstraintMatrix::distribute_local_to_global +// that internally executes this loop and +// eliminates all the constraints at the same +// time. +// +// The rest of the code that forms the local +// contributions remains unchanged. It is +// worth noting, however, that under the hood +// several things are different than +// before. First, the variables +// dofs_per_cell and +// n_q_points now are 9 each, +// where they were 4 before. Introducing such +// variables as abbreviations is a good +// strategy to make code work with different +// elements without having to change too much +// code. Secondly, the fe_values +// object of course needs to do other things +// as well, since the shape functions are now +// quadratic, rather than linear, in each +// coordinate variable. Again, however, this +// is something that is completely +// transparent to user code and nothing that +// you have to worry about. template void Step6::assemble_system () { @@ -582,8 +582,8 @@ void Step6::assemble_system () std::vector coefficient_values (n_q_points); typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { cell_matrix = 0; @@ -612,44 +612,44 @@ void Step6::assemble_system () // transfer the contributions from @p cell_matrix and @cell_rhs into the global objects. constraints.distribute_local_to_global(cell_matrix, cell_rhs, local_dof_indices, system_matrix, system_rhs); } - // Now we are done assembling the linear - // system. The constrained nodes are still - // in the linear system (there is a one on - // the diagonal of the matrix and all other - // entries for this line are set to zero) - // but the computed values are invalid. We - // compute the correct values for these - // nodes at the end of the - // solve function. + // Now we are done assembling the linear + // system. The constrained nodes are still + // in the linear system (there is a one on + // the diagonal of the matrix and all other + // entries for this line are set to zero) + // but the computed values are invalid. We + // compute the correct values for these + // nodes at the end of the + // solve function. } - // @sect4{Step6::solve} - - // We continue with gradual improvements. The - // function that solves the linear system - // again uses the SSOR preconditioner, and is - // again unchanged except that we have to - // incorporate hanging node constraints. As - // mentioned above, the degrees of freedom - // from the ConstraintMatrix corresponding to - // hanging node constraints and boundary - // values have been removed from the linear - // system by giving the rows and columns of - // the matrix a special treatment. This way, - // the values for these degrees of freedom - // have wrong, but well-defined values after - // solving the linear system. What we then - // have to do is to use the constraints to - // assign to them the values that they should - // have. This process, called - // distributing constraints, - // computes the values of constrained nodes - // from the values of the unconstrained ones, - // and requires only a single additional - // function call that you find at the end of - // this function: +// @sect4{Step6::solve} + +// We continue with gradual improvements. The +// function that solves the linear system +// again uses the SSOR preconditioner, and is +// again unchanged except that we have to +// incorporate hanging node constraints. As +// mentioned above, the degrees of freedom +// from the ConstraintMatrix corresponding to +// hanging node constraints and boundary +// values have been removed from the linear +// system by giving the rows and columns of +// the matrix a special treatment. This way, +// the values for these degrees of freedom +// have wrong, but well-defined values after +// solving the linear system. What we then +// have to do is to use the constraints to +// assign to them the values that they should +// have. This process, called +// distributing constraints, +// computes the values of constrained nodes +// from the values of the unconstrained ones, +// and requires only a single additional +// function call that you find at the end of +// this function: template void Step6::solve () @@ -667,121 +667,121 @@ void Step6::solve () } - // @sect4{Step6::refine_grid} - - // Instead of global refinement, we - // now use a slightly more elaborate - // scheme. We will use the - // KellyErrorEstimator class - // which implements an error - // estimator for the Laplace - // equation; it can in principle - // handle variable coefficients, but - // we will not use these advanced - // features, but rather use its most - // simple form since we are not - // interested in quantitative results - // but only in a quick way to - // generate locally refined grids. - // - // Although the error estimator - // derived by Kelly et al. was - // originally developed for the Laplace - // equation, we have found that it is - // also well suited to quickly - // generate locally refined grids for - // a wide class of - // problems. Basically, it looks at - // the jumps of the gradients of the - // solution over the faces of cells - // (which is a measure for the second - // derivatives) and scales it by the - // size of the cell. It is therefore - // a measure for the local smoothness - // of the solution at the place of - // each cell and it is thus - // understandable that it yields - // reasonable grids also for - // hyperbolic transport problems or - // the wave equation as well, - // although these grids are certainly - // suboptimal compared to approaches - // specially tailored to the - // problem. This error estimator may - // therefore be understood as a quick - // way to test an adaptive program. - // - // The way the estimator works is to - // take a DoFHandler object - // describing the degrees of freedom - // and a vector of values for each - // degree of freedom as input and - // compute a single indicator value - // for each active cell of the - // triangulation (i.e. one value for - // each of the - // triangulation.n_active_cells() - // cells). To do so, it needs two - // additional pieces of information: - // a quadrature formula on the faces - // (i.e. quadrature formula on - // dim-1 dimensional objects. We - // use a 3-point Gauss rule again, a - // pick that is consistent and - // appropriate with the choice - // bi-quadratic finite element shape - // functions in this program. - // (What constitutes a suitable - // quadrature rule here of course - // depends on knowledge of the way - // the error estimator evaluates - // the solution field. As said - // above, the jump of the gradient - // is integrated over each face, - // which would be a quadratic - // function on each face for the - // quadratic elements in use in - // this example. In fact, however, - // it is the square of the jump of - // the gradient, as explained in - // the documentation of that class, - // and that is a quartic function, - // for which a 3 point Gauss - // formula is sufficient since it - // integrates polynomials up to - // order 5 exactly.) - // - // Secondly, the function wants a - // list of boundaries where we have - // imposed Neumann value, and the - // corresponding Neumann values. This - // information is represented by an - // object of type - // FunctionMap::type that is - // essentially a map from boundary - // indicators to function objects - // describing Neumann boundary values - // (in the present example program, - // we do not use Neumann boundary - // values, so this map is empty, and - // in fact constructed using the - // default constructor of the map in - // the place where the function call - // expects the respective function - // argument). - // - // The output, as mentioned is a - // vector of values for all - // cells. While it may make sense to - // compute the *value* of a degree of - // freedom very accurately, it is - // usually not helpful to compute the - // *error indicator* corresponding to - // a cell particularly accurately. We - // therefore typically use a vector - // of floats instead of a vector of - // doubles to represent error - // indicators. +// @sect4{Step6::refine_grid} + +// Instead of global refinement, we +// now use a slightly more elaborate +// scheme. We will use the +// KellyErrorEstimator class +// which implements an error +// estimator for the Laplace +// equation; it can in principle +// handle variable coefficients, but +// we will not use these advanced +// features, but rather use its most +// simple form since we are not +// interested in quantitative results +// but only in a quick way to +// generate locally refined grids. +// +// Although the error estimator +// derived by Kelly et al. was +// originally developed for the Laplace +// equation, we have found that it is +// also well suited to quickly +// generate locally refined grids for +// a wide class of +// problems. Basically, it looks at +// the jumps of the gradients of the +// solution over the faces of cells +// (which is a measure for the second +// derivatives) and scales it by the +// size of the cell. It is therefore +// a measure for the local smoothness +// of the solution at the place of +// each cell and it is thus +// understandable that it yields +// reasonable grids also for +// hyperbolic transport problems or +// the wave equation as well, +// although these grids are certainly +// suboptimal compared to approaches +// specially tailored to the +// problem. This error estimator may +// therefore be understood as a quick +// way to test an adaptive program. +// +// The way the estimator works is to +// take a DoFHandler object +// describing the degrees of freedom +// and a vector of values for each +// degree of freedom as input and +// compute a single indicator value +// for each active cell of the +// triangulation (i.e. one value for +// each of the +// triangulation.n_active_cells() +// cells). To do so, it needs two +// additional pieces of information: +// a quadrature formula on the faces +// (i.e. quadrature formula on +// dim-1 dimensional objects. We +// use a 3-point Gauss rule again, a +// pick that is consistent and +// appropriate with the choice +// bi-quadratic finite element shape +// functions in this program. +// (What constitutes a suitable +// quadrature rule here of course +// depends on knowledge of the way +// the error estimator evaluates +// the solution field. As said +// above, the jump of the gradient +// is integrated over each face, +// which would be a quadratic +// function on each face for the +// quadratic elements in use in +// this example. In fact, however, +// it is the square of the jump of +// the gradient, as explained in +// the documentation of that class, +// and that is a quartic function, +// for which a 3 point Gauss +// formula is sufficient since it +// integrates polynomials up to +// order 5 exactly.) +// +// Secondly, the function wants a +// list of boundaries where we have +// imposed Neumann value, and the +// corresponding Neumann values. This +// information is represented by an +// object of type +// FunctionMap::type that is +// essentially a map from boundary +// indicators to function objects +// describing Neumann boundary values +// (in the present example program, +// we do not use Neumann boundary +// values, so this map is empty, and +// in fact constructed using the +// default constructor of the map in +// the place where the function call +// expects the respective function +// argument). +// +// The output, as mentioned is a +// vector of values for all +// cells. While it may make sense to +// compute the *value* of a degree of +// freedom very accurately, it is +// usually not helpful to compute the +// *error indicator* corresponding to +// a cell particularly accurately. We +// therefore typically use a vector +// of floats instead of a vector of +// doubles to represent error +// indicators. template void Step6::refine_grid () { @@ -793,137 +793,137 @@ void Step6::refine_grid () solution, estimated_error_per_cell); - // The above function returned one - // error indicator value for each - // cell in the - // estimated_error_per_cell - // array. Refinement is now done as - // follows: refine those 30 per - // cent of the cells with the - // highest error values, and - // coarsen the 3 per cent of cells - // with the lowest values. - // - // One can easily verify that if - // the second number were zero, - // this would approximately result - // in a doubling of cells in each - // step in two space dimensions, - // since for each of the 30 per - // cent of cells, four new would be - // replaced, while the remaining 70 - // per cent of cells remain - // untouched. In practice, some - // more cells are usually produced - // since it is disallowed that a - // cell is refined twice while the - // neighbor cell is not refined; in - // that case, the neighbor cell - // would be refined as well. - // - // In many applications, the number - // of cells to be coarsened would - // be set to something larger than - // only three per cent. A non-zero - // value is useful especially if - // for some reason the initial - // (coarse) grid is already rather - // refined. In that case, it might - // be necessary to refine it in - // some regions, while coarsening - // in some other regions is - // useful. In our case here, the - // initial grid is very coarse, so - // coarsening is only necessary in - // a few regions where - // over-refinement may have taken - // place. Thus a small, non-zero - // value is appropriate here. - // - // The following function now takes - // these refinement indicators and - // flags some cells of the - // triangulation for refinement or - // coarsening using the method - // described above. It is from a - // class that implements - // several different algorithms to - // refine a triangulation based on - // cell-wise error indicators. + // The above function returned one + // error indicator value for each + // cell in the + // estimated_error_per_cell + // array. Refinement is now done as + // follows: refine those 30 per + // cent of the cells with the + // highest error values, and + // coarsen the 3 per cent of cells + // with the lowest values. + // + // One can easily verify that if + // the second number were zero, + // this would approximately result + // in a doubling of cells in each + // step in two space dimensions, + // since for each of the 30 per + // cent of cells, four new would be + // replaced, while the remaining 70 + // per cent of cells remain + // untouched. In practice, some + // more cells are usually produced + // since it is disallowed that a + // cell is refined twice while the + // neighbor cell is not refined; in + // that case, the neighbor cell + // would be refined as well. + // + // In many applications, the number + // of cells to be coarsened would + // be set to something larger than + // only three per cent. A non-zero + // value is useful especially if + // for some reason the initial + // (coarse) grid is already rather + // refined. In that case, it might + // be necessary to refine it in + // some regions, while coarsening + // in some other regions is + // useful. In our case here, the + // initial grid is very coarse, so + // coarsening is only necessary in + // a few regions where + // over-refinement may have taken + // place. Thus a small, non-zero + // value is appropriate here. + // + // The following function now takes + // these refinement indicators and + // flags some cells of the + // triangulation for refinement or + // coarsening using the method + // described above. It is from a + // class that implements + // several different algorithms to + // refine a triangulation based on + // cell-wise error indicators. GridRefinement::refine_and_coarsen_fixed_number (triangulation, estimated_error_per_cell, 0.3, 0.03); - // After the previous function has - // exited, some cells are flagged - // for refinement, and some other - // for coarsening. The refinement - // or coarsening itself is not - // performed by now, however, since - // there are cases where further - // modifications of these flags is - // useful. Here, we don't want to - // do any such thing, so we can - // tell the triangulation to - // perform the actions for which - // the cells are flagged: + // After the previous function has + // exited, some cells are flagged + // for refinement, and some other + // for coarsening. The refinement + // or coarsening itself is not + // performed by now, however, since + // there are cases where further + // modifications of these flags is + // useful. Here, we don't want to + // do any such thing, so we can + // tell the triangulation to + // perform the actions for which + // the cells are flagged: triangulation.execute_coarsening_and_refinement (); } - // @sect4{Step6::output_results} - - // At the end of computations on each - // grid, and just before we continue - // the next cycle with mesh - // refinement, we want to output the - // results from this cycle. - // - // In the present program, we will - // not write the solution (except for - // in the last step, see the next - // function), but only the meshes - // that we generated, as a - // two-dimensional Encapsulated - // Postscript (EPS) file. - // - // We have already seen in step-1 how - // this can be achieved. The only - // thing we have to change is the - // generation of the file name, since - // it should contain the number of - // the present refinement cycle - // provided to this function as an - // argument. The most general way is - // to use the std::stringstream class - // as shown in step-5, but here's a - // little hack that makes it simpler - // if we know that we have less than - // 10 iterations: assume that the - // %numbers `0' through `9' are - // represented consecutively in the - // character set used on your machine - // (this is in fact the case in all - // known character sets), then - // '0'+cycle gives the character - // corresponding to the present cycle - // number. Of course, this will only - // work if the number of cycles is - // actually less than 10, and rather - // than waiting for the disaster to - // happen, we safeguard our little - // hack with an explicit assertion at - // the beginning of the function. If - // this assertion is triggered, - // i.e. when cycle is larger than - // or equal to 10, an exception of - // type ExcNotImplemented is - // raised, indicating that some - // functionality is not implemented - // for this case (the functionality - // that is missing, of course, is the - // generation of file names for that - // case): +// @sect4{Step6::output_results} + +// At the end of computations on each +// grid, and just before we continue +// the next cycle with mesh +// refinement, we want to output the +// results from this cycle. +// +// In the present program, we will +// not write the solution (except for +// in the last step, see the next +// function), but only the meshes +// that we generated, as a +// two-dimensional Encapsulated +// Postscript (EPS) file. +// +// We have already seen in step-1 how +// this can be achieved. The only +// thing we have to change is the +// generation of the file name, since +// it should contain the number of +// the present refinement cycle +// provided to this function as an +// argument. The most general way is +// to use the std::stringstream class +// as shown in step-5, but here's a +// little hack that makes it simpler +// if we know that we have less than +// 10 iterations: assume that the +// %numbers `0' through `9' are +// represented consecutively in the +// character set used on your machine +// (this is in fact the case in all +// known character sets), then +// '0'+cycle gives the character +// corresponding to the present cycle +// number. Of course, this will only +// work if the number of cycles is +// actually less than 10, and rather +// than waiting for the disaster to +// happen, we safeguard our little +// hack with an explicit assertion at +// the beginning of the function. If +// this assertion is triggered, +// i.e. when cycle is larger than +// or equal to 10, an exception of +// type ExcNotImplemented is +// raised, indicating that some +// functionality is not implemented +// for this case (the functionality +// that is missing, of course, is the +// generation of file names for that +// case): template void Step6::output_results (const unsigned int cycle) const { @@ -941,57 +941,57 @@ void Step6::output_results (const unsigned int cycle) const - // @sect4{Step6::run} - - // The final function before - // main() is again the main - // driver of the class, run(). It - // is similar to the one of step-5, - // except that we generate a file in - // the program again instead of - // reading it from disk, in that we - // adaptively instead of globally - // refine the mesh, and that we - // output the solution on the final - // mesh in the present function. - // - // The first block in the main loop - // of the function deals with mesh - // generation. If this is the first - // cycle of the program, instead of - // reading the grid from a file on - // disk as in the previous example, - // we now again create it using a - // library function. The domain is - // again a circle, which is why we - // have to provide a suitable - // boundary object as well. We place - // the center of the circle at the - // origin and have the radius be one - // (these are the two hidden - // arguments to the function, which - // have default values). - // - // You will notice by looking at the - // coarse grid that it is of inferior - // quality than the one which we read - // from the file in the previous - // example: the cells are less - // equally formed. However, using the - // library function this program - // works in any space dimension, - // which was not the case before. - // - // In case we find that this is not - // the first cycle, we want to refine - // the grid. Unlike the global - // refinement employed in the last - // example program, we now use the - // adaptive procedure described - // above. - // - // The rest of the loop looks as - // before: +// @sect4{Step6::run} + +// The final function before +// main() is again the main +// driver of the class, run(). It +// is similar to the one of step-5, +// except that we generate a file in +// the program again instead of +// reading it from disk, in that we +// adaptively instead of globally +// refine the mesh, and that we +// output the solution on the final +// mesh in the present function. +// +// The first block in the main loop +// of the function deals with mesh +// generation. If this is the first +// cycle of the program, instead of +// reading the grid from a file on +// disk as in the previous example, +// we now again create it using a +// library function. The domain is +// again a circle, which is why we +// have to provide a suitable +// boundary object as well. We place +// the center of the circle at the +// origin and have the radius be one +// (these are the two hidden +// arguments to the function, which +// have default values). +// +// You will notice by looking at the +// coarse grid that it is of inferior +// quality than the one which we read +// from the file in the previous +// example: the cells are less +// equally formed. However, using the +// library function this program +// works in any space dimension, +// which was not the case before. +// +// In case we find that this is not +// the first cycle, we want to refine +// the grid. Unlike the global +// refinement employed in the last +// example program, we now use the +// adaptive procedure described +// above. +// +// The rest of the loop looks as +// before: template void Step6::run () { @@ -1027,18 +1027,18 @@ void Step6::run () output_results (cycle); } - // After we have finished computing - // the solution on the finest mesh, - // and writing all the grids to - // disk, we want to also write the - // actual solution on this final - // mesh to a file. As already done - // in one of the previous examples, - // we use the EPS format for - // output, and to obtain a - // reasonable view on the solution, - // we rescale the z-axis by a - // factor of four. + // After we have finished computing + // the solution on the finest mesh, + // and writing all the grids to + // disk, we want to also write the + // actual solution on this final + // mesh to a file. As already done + // in one of the previous examples, + // we use the EPS format for + // output, and to obtain a + // reasonable view on the solution, + // we rescale the z-axis by a + // factor of four. DataOutBase::EpsFlags eps_flags; eps_flags.z_scaling = 4; @@ -1054,54 +1054,54 @@ void Step6::run () } - // @sect3{The main function} - - // The main function is unaltered in - // its functionality from the - // previous example, but we have - // taken a step of additional - // caution. Sometimes, something goes - // wrong (such as insufficient disk - // space upon writing an output file, - // not enough memory when trying to - // allocate a vector or a matrix, or - // if we can't read from or write to - // a file for whatever reason), and - // in these cases the library will - // throw exceptions. Since these are - // run-time problems, not programming - // errors that can be fixed once and - // for all, this kind of exceptions - // is not switched off in optimized - // mode, in contrast to the - // Assert macro which we have - // used to test against programming - // errors. If uncaught, these - // exceptions propagate the call tree - // up to the main function, and - // if they are not caught there - // either, the program is aborted. In - // many cases, like if there is not - // enough memory or disk space, we - // can't do anything but we can at - // least print some text trying to - // explain the reason why the program - // failed. A way to do so is shown in - // the following. It is certainly - // useful to write any larger program - // in this way, and you can do so by - // more or less copying this function - // except for the try block that - // actually encodes the functionality - // particular to the present - // application. +// @sect3{The main function} + +// The main function is unaltered in +// its functionality from the +// previous example, but we have +// taken a step of additional +// caution. Sometimes, something goes +// wrong (such as insufficient disk +// space upon writing an output file, +// not enough memory when trying to +// allocate a vector or a matrix, or +// if we can't read from or write to +// a file for whatever reason), and +// in these cases the library will +// throw exceptions. Since these are +// run-time problems, not programming +// errors that can be fixed once and +// for all, this kind of exceptions +// is not switched off in optimized +// mode, in contrast to the +// Assert macro which we have +// used to test against programming +// errors. If uncaught, these +// exceptions propagate the call tree +// up to the main function, and +// if they are not caught there +// either, the program is aborted. In +// many cases, like if there is not +// enough memory or disk space, we +// can't do anything but we can at +// least print some text trying to +// explain the reason why the program +// failed. A way to do so is shown in +// the following. It is certainly +// useful to write any larger program +// in this way, and you can do so by +// more or less copying this function +// except for the try block that +// actually encodes the functionality +// particular to the present +// application. int main () { - // The general idea behind the - // layout of this function is as - // follows: let's try to run the - // program as we did before... + // The general idea behind the + // layout of this function is as + // follows: let's try to run the + // program as we did before... try { deallog.depth_console (0); @@ -1109,41 +1109,41 @@ int main () Step6<2> laplace_problem_2d; laplace_problem_2d.run (); } - // ...and if this should fail, try - // to gather as much information as - // possible. Specifically, if the - // exception that was thrown is an - // object of a class that is - // derived from the C++ standard - // class exception, then we can - // use the what member function - // to get a string which describes - // the reason why the exception was - // thrown. - // - // The deal.II exception classes - // are all derived from the - // standard class, and in - // particular, the exc.what() - // function will return - // approximately the same string as - // would be generated if the - // exception was thrown using the - // Assert macro. You have seen - // the output of such an exception - // in the previous example, and you - // then know that it contains the - // file and line number of where - // the exception occured, and some - // other information. This is also - // what the following statements - // would print. - // - // Apart from this, there isn't - // much that we can do except - // exiting the program with an - // error code (this is what the - // return 1; does): + // ...and if this should fail, try + // to gather as much information as + // possible. Specifically, if the + // exception that was thrown is an + // object of a class that is + // derived from the C++ standard + // class exception, then we can + // use the what member function + // to get a string which describes + // the reason why the exception was + // thrown. + // + // The deal.II exception classes + // are all derived from the + // standard class, and in + // particular, the exc.what() + // function will return + // approximately the same string as + // would be generated if the + // exception was thrown using the + // Assert macro. You have seen + // the output of such an exception + // in the previous example, and you + // then know that it contains the + // file and line number of where + // the exception occured, and some + // other information. This is also + // what the following statements + // would print. + // + // Apart from this, there isn't + // much that we can do except + // exiting the program with an + // error code (this is what the + // return 1; does): catch (std::exception &exc) { std::cerr << std::endl << std::endl @@ -1157,13 +1157,13 @@ int main () return 1; } - // If the exception that was thrown - // somewhere was not an object of a - // class derived from the standard - // exception class, then we - // can't do anything at all. We - // then simply print an error - // message and exit. + // If the exception that was thrown + // somewhere was not an object of a + // class derived from the standard + // exception class, then we + // can't do anything at all. We + // then simply print an error + // message and exit. catch (...) { std::cerr << std::endl << std::endl @@ -1176,14 +1176,14 @@ int main () return 1; } - // If we got to this point, there - // was no exception which - // propagated up to the main - // function (there may have been - // exceptions, but they were caught - // somewhere in the program or the - // library). Therefore, the program - // performed as was expected and we - // can return without error. + // If we got to this point, there + // was no exception which + // propagated up to the main + // function (there may have been + // exceptions, but they were caught + // somewhere in the program or the + // library). Therefore, the program + // performed as was expected and we + // can return without error. return 0; } diff --git a/deal.II/examples/step-7/step-7.cc b/deal.II/examples/step-7/step-7.cc index 6b2e40229e..abe9a0dd07 100644 --- a/deal.II/examples/step-7/step-7.cc +++ b/deal.II/examples/step-7/step-7.cc @@ -9,12 +9,12 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // These first include files have all - // been treated in previous examples, - // so we won't explain what is in - // them again. +// These first include files have all +// been treated in previous examples, +// so we won't explain what is in +// them again. #include #include #include @@ -38,240 +38,242 @@ #include #include - // In this example, we will not use the - // numeration scheme which is used per - // default by the DoFHandler class, but - // will renumber them using the Cuthill-McKee - // algorithm. As has already been explained - // in step-2, the necessary functions are - // declared in the following file: +// In this example, we will not use the +// numeration scheme which is used per +// default by the DoFHandler class, but +// will renumber them using the Cuthill-McKee +// algorithm. As has already been explained +// in step-2, the necessary functions are +// declared in the following file: #include - // Then we will show a little trick - // how we can make sure that objects - // are not deleted while they are - // still in use. For this purpose, - // deal.II has the SmartPointer - // helper class, which is declared in - // this file: +// Then we will show a little trick +// how we can make sure that objects +// are not deleted while they are +// still in use. For this purpose, +// deal.II has the SmartPointer +// helper class, which is declared in +// this file: #include - // Next, we will want to use the function - // VectorTools::integrate_difference() - // mentioned in the introduction, and we are - // going to use a ConvergenceTable that - // collects all important data during a run - // and prints it at the end as a table. These - // comes from the following two files: +// Next, we will want to use the function +// VectorTools::integrate_difference() +// mentioned in the introduction, and we are +// going to use a ConvergenceTable that +// collects all important data during a run +// and prints it at the end as a table. These +// comes from the following two files: #include #include - // And finally, we need to use the - // FEFaceValues class, which is - // declared in the same file as the - // FEValues class: +// And finally, we need to use the +// FEFaceValues class, which is +// declared in the same file as the +// FEValues class: #include - // We need one more include from standard - // C++, which is necessary when we try to - // find out the actual type behind a pointer - // to a base class. We will explain this in - // slightly more detail below. The other two - // include files are obvious then: +// We need one more include from standard +// C++, which is necessary when we try to +// find out the actual type behind a pointer +// to a base class. We will explain this in +// slightly more detail below. The other two +// include files are obvious then: #include #include #include - // The last step before we go on with the - // actual implementation is to open a - // namespace Step7 into which we - // will put everything, as discussed at the - // end of the introduction, and to import the - // members of namespace dealii - // into it: +// The last step before we go on with the +// actual implementation is to open a +// namespace Step7 into which we +// will put everything, as discussed at the +// end of the introduction, and to import the +// members of namespace dealii +// into it: namespace Step7 { using namespace dealii; - // @sect3{Equation data} - - // Before implementing the classes that - // actually solve something, we first declare - // and define some function classes that - // represent right hand side and solution - // classes. Since we want to compare the - // numerically obtained solution to the exact - // continuous one, we need a function object - // that represents the continuous - // solution. On the other hand, we need the - // right hand side function, and that one of - // course shares some characteristics with - // the solution. In order to reduce - // dependencies which arise if we have to - // change something in both classes at the - // same time, we move the common - // characteristics of both functions into a - // base class. - // - // The common characteristics for solution - // (as explained in the introduction, we - // choose a sum of three exponentials) and - // right hand side, are these: the number of - // exponentials, their centers, and their - // half width. We declare them in the - // following class. Since the number of - // exponentials is a constant scalar integral - // quantity, C++ allows its definition - // (i.e. assigning a value) right at the - // place of declaration (i.e. where we - // declare that such a variable exists). + // @sect3{Equation data} + + // Before implementing the classes that + // actually solve something, we first declare + // and define some function classes that + // represent right hand side and solution + // classes. Since we want to compare the + // numerically obtained solution to the exact + // continuous one, we need a function object + // that represents the continuous + // solution. On the other hand, we need the + // right hand side function, and that one of + // course shares some characteristics with + // the solution. In order to reduce + // dependencies which arise if we have to + // change something in both classes at the + // same time, we move the common + // characteristics of both functions into a + // base class. + // + // The common characteristics for solution + // (as explained in the introduction, we + // choose a sum of three exponentials) and + // right hand side, are these: the number of + // exponentials, their centers, and their + // half width. We declare them in the + // following class. Since the number of + // exponentials is a constant scalar integral + // quantity, C++ allows its definition + // (i.e. assigning a value) right at the + // place of declaration (i.e. where we + // declare that such a variable exists). template class SolutionBase { - protected: - static const unsigned int n_source_centers = 3; - static const Point source_centers[n_source_centers]; - static const double width; + protected: + static const unsigned int n_source_centers = 3; + static const Point source_centers[n_source_centers]; + static const double width; }; - // The variables which denote the - // centers and the width of the - // exponentials have just been - // declared, now we still need to - // assign values to them. Here, we - // can show another small piece of - // template sorcery, namely how we - // can assign different values to - // these variables depending on the - // dimension. We will only use the 2d - // case in the program, but we show - // the 1d case for exposition of a - // useful technique. - // - // First we assign values to the centers for - // the 1d case, where we place the centers - // equidistantly at -1/3, 0, and 1/3. The - // template <> header for this definition - // indicates an explicit specialization. This - // means, that the variable belongs to a - // template, but that instead of providing - // the compiler with a template from which it - // can specialize a concrete variable by - // substituting dim with some concrete - // value, we provide a specialization - // ourselves, in this case for dim=1. If - // the compiler then sees a reference to this - // variable in a place where the template - // argument equals one, it knows that it - // doesn't have to generate the variable from - // a template by substituting dim, but - // can immediately use the following - // definition: + // The variables which denote the + // centers and the width of the + // exponentials have just been + // declared, now we still need to + // assign values to them. Here, we + // can show another small piece of + // template sorcery, namely how we + // can assign different values to + // these variables depending on the + // dimension. We will only use the 2d + // case in the program, but we show + // the 1d case for exposition of a + // useful technique. + // + // First we assign values to the centers for + // the 1d case, where we place the centers + // equidistantly at -1/3, 0, and 1/3. The + // template <> header for this definition + // indicates an explicit specialization. This + // means, that the variable belongs to a + // template, but that instead of providing + // the compiler with a template from which it + // can specialize a concrete variable by + // substituting dim with some concrete + // value, we provide a specialization + // ourselves, in this case for dim=1. If + // the compiler then sees a reference to this + // variable in a place where the template + // argument equals one, it knows that it + // doesn't have to generate the variable from + // a template by substituting dim, but + // can immediately use the following + // definition: template <> const Point<1> SolutionBase<1>::source_centers[SolutionBase<1>::n_source_centers] - = { Point<1>(-1.0 / 3.0), - Point<1>(0.0), - Point<1>(+1.0 / 3.0) }; + = { Point<1>(-1.0 / 3.0), + Point<1>(0.0), + Point<1>(+1.0 / 3.0) + }; - // Likewise, we can provide an explicit - // specialization for dim=2. We place the - // centers for the 2d case as follows: + // Likewise, we can provide an explicit + // specialization for dim=2. We place the + // centers for the 2d case as follows: template <> const Point<2> SolutionBase<2>::source_centers[SolutionBase<2>::n_source_centers] - = { Point<2>(-0.5, +0.5), - Point<2>(-0.5, -0.5), - Point<2>(+0.5, -0.5) }; - - // There remains to assign a value to the - // half-width of the exponentials. We would - // like to use the same value for all - // dimensions. In this case, we simply - // provide the compiler with a template from - // which it can generate a concrete - // instantiation by substituting dim with - // a concrete value: + = { Point<2>(-0.5, +0.5), + Point<2>(-0.5, -0.5), + Point<2>(+0.5, -0.5) + }; + + // There remains to assign a value to the + // half-width of the exponentials. We would + // like to use the same value for all + // dimensions. In this case, we simply + // provide the compiler with a template from + // which it can generate a concrete + // instantiation by substituting dim with + // a concrete value: template const double SolutionBase::width = 1./3.; - // After declaring and defining the - // characteristics of solution and - // right hand side, we can declare - // the classes representing these - // two. They both represent - // continuous functions, so they are - // derived from the Function<dim> - // base class, and they also inherit - // the characteristics defined in the - // SolutionBase class. - // - // The actual classes are declared in the - // following. Note that in order to compute - // the error of the numerical solution - // against the continuous one in the L2 and - // H1 norms, we have to provide value and - // gradient of the exact solution. This is - // more than we have done in previous - // examples, where all we provided was the - // value at one or a list of - // points. Fortunately, the Function - // class also has virtual functions for the - // gradient, so we can simply overload the - // respective virtual member functions in the - // Function base class. Note that the - // gradient of a function in dim space - // dimensions is a vector of size dim, - // i.e. a tensor of rank 1 and dimension - // dim. As for so many other things, the - // library provides a suitable class for - // this. - // - // Just as in previous examples, we - // are forced by the C++ language - // specification to declare a - // seemingly useless default - // constructor. + // After declaring and defining the + // characteristics of solution and + // right hand side, we can declare + // the classes representing these + // two. They both represent + // continuous functions, so they are + // derived from the Function<dim> + // base class, and they also inherit + // the characteristics defined in the + // SolutionBase class. + // + // The actual classes are declared in the + // following. Note that in order to compute + // the error of the numerical solution + // against the continuous one in the L2 and + // H1 norms, we have to provide value and + // gradient of the exact solution. This is + // more than we have done in previous + // examples, where all we provided was the + // value at one or a list of + // points. Fortunately, the Function + // class also has virtual functions for the + // gradient, so we can simply overload the + // respective virtual member functions in the + // Function base class. Note that the + // gradient of a function in dim space + // dimensions is a vector of size dim, + // i.e. a tensor of rank 1 and dimension + // dim. As for so many other things, the + // library provides a suitable class for + // this. + // + // Just as in previous examples, we + // are forced by the C++ language + // specification to declare a + // seemingly useless default + // constructor. template class Solution : public Function, - protected SolutionBase + protected SolutionBase { - public: - Solution () : Function() {} + public: + Solution () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; }; - // The actual definition of the values and - // gradients of the exact solution class is - // according to their mathematical definition - // and does not need much explanation. - // - // The only thing that is worth - // mentioning is that if we access - // elements of a base class that is - // template dependent (in this case - // the elements of - // SolutionBase<dim>), then the - // C++ language forces us to write - // this->n_source_centers (for - // example). Note that the this-> - // qualification is not necessary if - // the base class is not template - // dependent, and also that the gcc - // compilers prior to version 3.4 don't - // enforce this requirement of the - // C++ standard. The reason why this - // is necessary is complicated; some - // books on C++ may explain it, so if - // you are interested you can look it - // up under the phrase two-stage - // (name) lookup. + // The actual definition of the values and + // gradients of the exact solution class is + // according to their mathematical definition + // and does not need much explanation. + // + // The only thing that is worth + // mentioning is that if we access + // elements of a base class that is + // template dependent (in this case + // the elements of + // SolutionBase<dim>), then the + // C++ language forces us to write + // this->n_source_centers (for + // example). Note that the this-> + // qualification is not necessary if + // the base class is not template + // dependent, and also that the gcc + // compilers prior to version 3.4 don't + // enforce this requirement of the + // C++ standard. The reason why this + // is necessary is complicated; some + // books on C++ may explain it, so if + // you are interested you can look it + // up under the phrase two-stage + // (name) lookup. template double Solution::value (const Point &p, const unsigned int) const @@ -288,44 +290,44 @@ namespace Step7 } - // Likewise, this is the computation of the - // gradient of the solution. In order to - // accumulate the gradient from the - // contributions of the exponentials, we - // allocate an object return_value that - // denotes the mathematical quantity of a - // tensor of rank 1 and dimension - // dim. Its default constructor sets it - // to the vector containing only zeroes, so - // we need not explicitly care for its - // initialization. - // - // Note that we could as well have taken the - // type of the object to be Point<dim> - // instead of Tensor<1,dim>. Tensors of - // rank 1 and points are almost exchangeable, - // and have only very slightly different - // mathematical meanings. In fact, the - // Point<dim> class is derived from the - // Tensor<1,dim> class, which makes up - // for their mutual exchange ability. Their - // main difference is in what they logically - // mean: points are points in space, such as - // the location at which we want to evaluate - // a function (see the type of the first - // argument of this function for example). On - // the other hand, tensors of rank 1 share - // the same transformation properties, for - // example that they need to be rotated in a - // certain way when we change the coordinate - // system; however, they do not share the - // same connotation that points have and are - // only objects in a more abstract space than - // the one spanned by the coordinate - // directions. (In fact, gradients live in - // `reciprocal' space, since the dimension of - // their components is not that of a length, - // but one over length). + // Likewise, this is the computation of the + // gradient of the solution. In order to + // accumulate the gradient from the + // contributions of the exponentials, we + // allocate an object return_value that + // denotes the mathematical quantity of a + // tensor of rank 1 and dimension + // dim. Its default constructor sets it + // to the vector containing only zeroes, so + // we need not explicitly care for its + // initialization. + // + // Note that we could as well have taken the + // type of the object to be Point<dim> + // instead of Tensor<1,dim>. Tensors of + // rank 1 and points are almost exchangeable, + // and have only very slightly different + // mathematical meanings. In fact, the + // Point<dim> class is derived from the + // Tensor<1,dim> class, which makes up + // for their mutual exchange ability. Their + // main difference is in what they logically + // mean: points are points in space, such as + // the location at which we want to evaluate + // a function (see the type of the first + // argument of this function for example). On + // the other hand, tensors of rank 1 share + // the same transformation properties, for + // example that they need to be rotated in a + // certain way when we change the coordinate + // system; however, they do not share the + // same connotation that points have and are + // only objects in a more abstract space than + // the one spanned by the coordinate + // directions. (In fact, gradients live in + // `reciprocal' space, since the dimension of + // their components is not that of a length, + // but one over length). template Tensor<1,dim> Solution::gradient (const Point &p, const unsigned int) const @@ -336,12 +338,12 @@ namespace Step7 { const Point x_minus_xi = p - this->source_centers[i]; - // For the gradient, note that - // its direction is along - // (x-x_i), so we add up - // multiples of this distance - // vector, where the factor is - // given by the exponentials. + // For the gradient, note that + // its direction is along + // (x-x_i), so we add up + // multiples of this distance + // vector, where the factor is + // given by the exponentials. return_value += (-2 / (this->width * this->width) * std::exp(-x_minus_xi.square() / (this->width * this->width)) * @@ -353,35 +355,35 @@ namespace Step7 - // Besides the function that - // represents the exact solution, we - // also need a function which we can - // use as right hand side when - // assembling the linear system of - // discretized equations. This is - // accomplished using the following - // class and the following definition - // of its function. Note that here we - // only need the value of the - // function, not its gradients or - // higher derivatives. + // Besides the function that + // represents the exact solution, we + // also need a function which we can + // use as right hand side when + // assembling the linear system of + // discretized equations. This is + // accomplished using the following + // class and the following definition + // of its function. Note that here we + // only need the value of the + // function, not its gradients or + // higher derivatives. template class RightHandSide : public Function, - protected SolutionBase + protected SolutionBase { - public: - RightHandSide () : Function() {} + public: + RightHandSide () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; }; - // The value of the right hand side - // is given by the negative Laplacian - // of the solution plus the solution - // itself, since we wanted to solve - // Helmholtz's equation: + // The value of the right hand side + // is given by the negative Laplacian + // of the solution plus the solution + // itself, since we wanted to solve + // Helmholtz's equation: template double RightHandSide::value (const Point &p, const unsigned int) const @@ -391,15 +393,15 @@ namespace Step7 { const Point x_minus_xi = p - this->source_centers[i]; - // The first contribution is - // the Laplacian: + // The first contribution is + // the Laplacian: return_value += ((2*dim - 4*x_minus_xi.square()/ (this->width * this->width)) / (this->width * this->width) * std::exp(-x_minus_xi.square() / (this->width * this->width))); - // And the second is the - // solution itself: + // And the second is the + // solution itself: return_value += std::exp(-x_minus_xi.square() / (this->width * this->width)); } @@ -408,306 +410,307 @@ namespace Step7 } - // @sect3{The Helmholtz solver class} - - // Then we need the class that does all the - // work. Except for its name, its interface - // is mostly the same as in previous - // examples. - // - // One of the differences is that we will use - // this class in several modes: for different - // finite elements, as well as for adaptive - // and global refinement. The decision - // whether global or adaptive refinement - // shall be used is communicated to the - // constructor of this class through an - // enumeration type declared at the top of - // the class. The constructor then takes a - // finite element object and the refinement - // mode as arguments. - // - // The rest of the member functions are as - // before except for the process_solution - // function: After the solution has been - // computed, we perform some analysis on it, - // such as computing the error in various - // norms. To enable some output, it requires - // the number of the refinement cycle, and - // consequently gets it as an argument. + // @sect3{The Helmholtz solver class} + + // Then we need the class that does all the + // work. Except for its name, its interface + // is mostly the same as in previous + // examples. + // + // One of the differences is that we will use + // this class in several modes: for different + // finite elements, as well as for adaptive + // and global refinement. The decision + // whether global or adaptive refinement + // shall be used is communicated to the + // constructor of this class through an + // enumeration type declared at the top of + // the class. The constructor then takes a + // finite element object and the refinement + // mode as arguments. + // + // The rest of the member functions are as + // before except for the process_solution + // function: After the solution has been + // computed, we perform some analysis on it, + // such as computing the error in various + // norms. To enable some output, it requires + // the number of the refinement cycle, and + // consequently gets it as an argument. template class HelmholtzProblem { - public: - enum RefinementMode { - global_refinement, adaptive_refinement - }; - - HelmholtzProblem (const FiniteElement &fe, - const RefinementMode refinement_mode); - - ~HelmholtzProblem (); - - void run (); - - private: - void setup_system (); - void assemble_system (); - void solve (); - void refine_grid (); - void process_solution (const unsigned int cycle); - - // Now for the data elements of - // this class. Among the variables - // that we have already used in - // previous examples, only the - // finite element object differs: - // The finite elements which the - // objects of this class operate - // on are passed to the - // constructor of this class. It - // has to store a pointer to the - // finite element for the member - // functions to use. Now, for the - // present class there is no big - // deal in that, but since we - // want to show techniques rather - // than solutions in these - // programs, we will here point - // out a problem that often - // occurs -- and of course the - // right solution as well. - // - // Consider the following - // situation that occurs in all - // the example programs: we have - // a triangulation object, and we - // have a finite element object, - // and we also have an object of - // type DoFHandler that uses - // both of the first two. These - // three objects all have a - // lifetime that is rather long - // compared to most other - // objects: they are basically - // set at the beginning of the - // program or an outer loop, and - // they are destroyed at the very - // end. The question is: can we - // guarantee that the two objects - // which the DoFHandler uses, - // live at least as long as they - // are in use? This means that - // the DoFHandler must have some - // kind of lock on the - // destruction of the other - // objects, and it can only - // release this lock once it has - // cleared all active references - // to these objects. We have seen - // what happens if we violate - // this order of destruction in - // the previous example program: - // an exception is thrown that - // terminates the program in - // order to notify the programmer - // of this potentially dangerous - // state where an object is - // pointed to that no longer - // persists. - // - // We will show here how the - // library managed to find out - // that there are still active - // references to an - // object. Basically, the method - // is along the following line: - // all objects that are subject - // to such potentially dangerous - // pointers are derived from a - // class called - // Subscriptor. For example, - // the Triangulation, - // DoFHandler, and a base - // class of the FiniteElement - // class are derived from - // Subscriptor. This latter - // class does not offer much - // functionality, but it has a - // built-in counter which we can - // subscribe to, thus the name of - // the class. Whenever we - // initialize a pointer to that - // object, we can increase its use - // counter, and when we move away - // our pointer or do not need it - // any more, we decrease the - // counter again. This way, we - // can always check how many - // objects still use that - // object. - // - // On the other hand, if an object of a - // class that is derived from the - // Subscriptor class is destroyed, it - // also has to call the destructor of the - // Subscriptor class. In this - // destructor, there - // will then be a check whether the - // counter is really zero. If - // yes, then there are no active - // references to this object any - // more, and we can safely - // destroy it. If the counter is - // non-zero, however, then the - // destruction would result in - // stale and thus potentially - // dangerous pointers, and we - // rather throw an exception to - // alert the programmer that this - // is doing something dangerous - // and the program better be - // fixed. - // - // While this certainly all - // sounds very well, it has some - // problems in terms of - // usability: what happens if I - // forget to increase the counter - // when I let a pointer point to - // such an object? And what - // happens if I forget to - // decrease it again? Note that - // this may lead to extremely - // difficult to find bugs, since - // the place where we have - // forgotten something may be - // far away from the place - // where the check for zeroness - // of the counter upon - // destruction actually - // fails. This kind of bug is - // rather annoying and usually very - // hard to fix. - // - // The solution to this problem - // is to again use some C++ - // trickery: we create a class - // that acts just like a pointer, - // i.e. can be dereferenced, can - // be assigned to and from other - // pointers, and so on. This can - // be done by overloading the - // several dereferencing - // operators of that - // class. Within the - // constructors, destructors, and - // assignment operators of that - // class, we can however also - // manage increasing or - // decreasing the use counters of - // the objects we point - // to. Objects of that class - // therefore can be used just - // like ordinary pointers to - // objects, but they also serve - // to change the use counters of - // those objects without the need - // for the programmer to do so - // herself. The class that - // actually does all this is - // called SmartPointer and - // takes as template parameter - // the data type of the object - // which it shall point to. The - // latter type may be any class, - // as long as it is derived from - // the Subscriptor class. - // - // In the present example program, we - // want to protect the finite element - // object from the situation that for - // some reason the finite element pointed - // to is destroyed while still in use. We - // therefore use a SmartPointer to - // the finite element object; since the - // finite element object is actually - // never changed in our computations, we - // pass a const FiniteElement<dim> as - // template argument to the - // SmartPointer class. Note that the - // pointer so declared is assigned at - // construction time of the solve object, - // and destroyed upon destruction, so the - // lock on the destruction of the finite - // element object extends throughout the - // lifetime of this HelmholtzProblem - // object. - Triangulation triangulation; - DoFHandler dof_handler; - - SmartPointer > fe; - - ConstraintMatrix hanging_node_constraints; - - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - - Vector solution; - Vector system_rhs; - - // The second to last variable - // stores the refinement mode - // passed to the - // constructor. Since it is only - // set in the constructor, we can - // declare this variable - // constant, to avoid that - // someone sets it involuntarily - // (e.g. in an `if'-statement - // where == was written as = by - // chance). - const RefinementMode refinement_mode; - - // For each refinement level some data - // (like the number of cells, or the L2 - // error of the numerical solution) will - // be generated and later printed. The - // TableHandler can be used to - // collect all this data and to output it - // at the end of the run as a table in a - // simple text or in LaTeX - // format. Here we don't only use the - // TableHandler but we use the - // derived class ConvergenceTable - // that additionally evaluates rates of - // convergence: - ConvergenceTable convergence_table; + public: + enum RefinementMode + { + global_refinement, adaptive_refinement + }; + + HelmholtzProblem (const FiniteElement &fe, + const RefinementMode refinement_mode); + + ~HelmholtzProblem (); + + void run (); + + private: + void setup_system (); + void assemble_system (); + void solve (); + void refine_grid (); + void process_solution (const unsigned int cycle); + + // Now for the data elements of + // this class. Among the variables + // that we have already used in + // previous examples, only the + // finite element object differs: + // The finite elements which the + // objects of this class operate + // on are passed to the + // constructor of this class. It + // has to store a pointer to the + // finite element for the member + // functions to use. Now, for the + // present class there is no big + // deal in that, but since we + // want to show techniques rather + // than solutions in these + // programs, we will here point + // out a problem that often + // occurs -- and of course the + // right solution as well. + // + // Consider the following + // situation that occurs in all + // the example programs: we have + // a triangulation object, and we + // have a finite element object, + // and we also have an object of + // type DoFHandler that uses + // both of the first two. These + // three objects all have a + // lifetime that is rather long + // compared to most other + // objects: they are basically + // set at the beginning of the + // program or an outer loop, and + // they are destroyed at the very + // end. The question is: can we + // guarantee that the two objects + // which the DoFHandler uses, + // live at least as long as they + // are in use? This means that + // the DoFHandler must have some + // kind of lock on the + // destruction of the other + // objects, and it can only + // release this lock once it has + // cleared all active references + // to these objects. We have seen + // what happens if we violate + // this order of destruction in + // the previous example program: + // an exception is thrown that + // terminates the program in + // order to notify the programmer + // of this potentially dangerous + // state where an object is + // pointed to that no longer + // persists. + // + // We will show here how the + // library managed to find out + // that there are still active + // references to an + // object. Basically, the method + // is along the following line: + // all objects that are subject + // to such potentially dangerous + // pointers are derived from a + // class called + // Subscriptor. For example, + // the Triangulation, + // DoFHandler, and a base + // class of the FiniteElement + // class are derived from + // Subscriptor. This latter + // class does not offer much + // functionality, but it has a + // built-in counter which we can + // subscribe to, thus the name of + // the class. Whenever we + // initialize a pointer to that + // object, we can increase its use + // counter, and when we move away + // our pointer or do not need it + // any more, we decrease the + // counter again. This way, we + // can always check how many + // objects still use that + // object. + // + // On the other hand, if an object of a + // class that is derived from the + // Subscriptor class is destroyed, it + // also has to call the destructor of the + // Subscriptor class. In this + // destructor, there + // will then be a check whether the + // counter is really zero. If + // yes, then there are no active + // references to this object any + // more, and we can safely + // destroy it. If the counter is + // non-zero, however, then the + // destruction would result in + // stale and thus potentially + // dangerous pointers, and we + // rather throw an exception to + // alert the programmer that this + // is doing something dangerous + // and the program better be + // fixed. + // + // While this certainly all + // sounds very well, it has some + // problems in terms of + // usability: what happens if I + // forget to increase the counter + // when I let a pointer point to + // such an object? And what + // happens if I forget to + // decrease it again? Note that + // this may lead to extremely + // difficult to find bugs, since + // the place where we have + // forgotten something may be + // far away from the place + // where the check for zeroness + // of the counter upon + // destruction actually + // fails. This kind of bug is + // rather annoying and usually very + // hard to fix. + // + // The solution to this problem + // is to again use some C++ + // trickery: we create a class + // that acts just like a pointer, + // i.e. can be dereferenced, can + // be assigned to and from other + // pointers, and so on. This can + // be done by overloading the + // several dereferencing + // operators of that + // class. Within the + // constructors, destructors, and + // assignment operators of that + // class, we can however also + // manage increasing or + // decreasing the use counters of + // the objects we point + // to. Objects of that class + // therefore can be used just + // like ordinary pointers to + // objects, but they also serve + // to change the use counters of + // those objects without the need + // for the programmer to do so + // herself. The class that + // actually does all this is + // called SmartPointer and + // takes as template parameter + // the data type of the object + // which it shall point to. The + // latter type may be any class, + // as long as it is derived from + // the Subscriptor class. + // + // In the present example program, we + // want to protect the finite element + // object from the situation that for + // some reason the finite element pointed + // to is destroyed while still in use. We + // therefore use a SmartPointer to + // the finite element object; since the + // finite element object is actually + // never changed in our computations, we + // pass a const FiniteElement<dim> as + // template argument to the + // SmartPointer class. Note that the + // pointer so declared is assigned at + // construction time of the solve object, + // and destroyed upon destruction, so the + // lock on the destruction of the finite + // element object extends throughout the + // lifetime of this HelmholtzProblem + // object. + Triangulation triangulation; + DoFHandler dof_handler; + + SmartPointer > fe; + + ConstraintMatrix hanging_node_constraints; + + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + + Vector solution; + Vector system_rhs; + + // The second to last variable + // stores the refinement mode + // passed to the + // constructor. Since it is only + // set in the constructor, we can + // declare this variable + // constant, to avoid that + // someone sets it involuntarily + // (e.g. in an `if'-statement + // where == was written as = by + // chance). + const RefinementMode refinement_mode; + + // For each refinement level some data + // (like the number of cells, or the L2 + // error of the numerical solution) will + // be generated and later printed. The + // TableHandler can be used to + // collect all this data and to output it + // at the end of the run as a table in a + // simple text or in LaTeX + // format. Here we don't only use the + // TableHandler but we use the + // derived class ConvergenceTable + // that additionally evaluates rates of + // convergence: + ConvergenceTable convergence_table; }; - // @sect3{The HelmholtzProblem class implementation} + // @sect3{The HelmholtzProblem class implementation} - // @sect4{HelmholtzProblem::HelmholtzProblem} + // @sect4{HelmholtzProblem::HelmholtzProblem} - // In the constructor of this class, - // we only set the variables passed - // as arguments, and associate the - // DoF handler object with the - // triangulation (which is empty at - // present, however). + // In the constructor of this class, + // we only set the variables passed + // as arguments, and associate the + // DoF handler object with the + // triangulation (which is empty at + // present, however). template HelmholtzProblem::HelmholtzProblem (const FiniteElement &fe, const RefinementMode refinement_mode) : - dof_handler (triangulation), - fe (&fe), - refinement_mode (refinement_mode) + dof_handler (triangulation), + fe (&fe), + refinement_mode (refinement_mode) {} - // @sect4{HelmholtzProblem::~HelmholtzProblem} + // @sect4{HelmholtzProblem::~HelmholtzProblem} - // This is no different than before: + // This is no different than before: template HelmholtzProblem::~HelmholtzProblem () { @@ -715,55 +718,55 @@ namespace Step7 } - // @sect4{HelmholtzProblem::setup_system} - - // The following function sets up the - // degrees of freedom, sizes of - // matrices and vectors, etc. Most of - // its functionality has been showed - // in previous examples, the only - // difference being the renumbering - // step immediately after first - // distributing degrees of freedom. - // - // Renumbering the degrees of - // freedom is not overly difficult, - // as long as you use one of the - // algorithms included in the - // library. It requires only a single - // line of code. Some more information - // on this can be found in step-2. - // - // Note, however, that when you - // renumber the degrees of freedom, - // you must do so immediately after - // distributing them, since such - // things as hanging nodes, the - // sparsity pattern etc. depend on - // the absolute numbers which are - // altered by renumbering. - // - // The reason why we introduce renumbering - // here is that it is a relatively cheap - // operation but often has a beneficial - // effect: While the CG iteration itself is - // independent of the actual ordering of - // degrees of freedom, we will use SSOR as a - // preconditioner. SSOR goes through all - // degrees of freedom and does some - // operations that depend on what happened - // before; the SSOR operation is therefore - // not independent of the numbering of - // degrees of freedom, and it is known that - // its performance improves by using - // renumbering techniques. A little - // experiment shows that indeed, for example, - // the number of CG iterations for the fifth - // refinement cycle of adaptive refinement - // with the Q1 program used here is 40 - // without, but 36 with renumbering. Similar - // savings can generally be observed for all - // the computations in this program. + // @sect4{HelmholtzProblem::setup_system} + + // The following function sets up the + // degrees of freedom, sizes of + // matrices and vectors, etc. Most of + // its functionality has been showed + // in previous examples, the only + // difference being the renumbering + // step immediately after first + // distributing degrees of freedom. + // + // Renumbering the degrees of + // freedom is not overly difficult, + // as long as you use one of the + // algorithms included in the + // library. It requires only a single + // line of code. Some more information + // on this can be found in step-2. + // + // Note, however, that when you + // renumber the degrees of freedom, + // you must do so immediately after + // distributing them, since such + // things as hanging nodes, the + // sparsity pattern etc. depend on + // the absolute numbers which are + // altered by renumbering. + // + // The reason why we introduce renumbering + // here is that it is a relatively cheap + // operation but often has a beneficial + // effect: While the CG iteration itself is + // independent of the actual ordering of + // degrees of freedom, we will use SSOR as a + // preconditioner. SSOR goes through all + // degrees of freedom and does some + // operations that depend on what happened + // before; the SSOR operation is therefore + // not independent of the numbering of + // degrees of freedom, and it is known that + // its performance improves by using + // renumbering techniques. A little + // experiment shows that indeed, for example, + // the number of CG iterations for the fifth + // refinement cycle of adaptive refinement + // with the Q1 program used here is 40 + // without, but 36 with renumbering. Similar + // savings can generally be observed for all + // the computations in this program. template void HelmholtzProblem::setup_system () { @@ -789,27 +792,27 @@ namespace Step7 } - // @sect4{HelmholtzProblem::assemble_system} - - // Assembling the system of equations - // for the problem at hand is mostly - // as for the example programs - // before. However, some things have - // changed anyway, so we comment on - // this function fairly extensively. - // - // At the top of the function you will find - // the usual assortment of variable - // declarations. Compared to previous - // programs, of importance is only that we - // expect to solve problems also with - // bi-quadratic elements and therefore have - // to use sufficiently accurate quadrature - // formula. In addition, we need to compute - // integrals over faces, i.e. dim-1 - // dimensional objects. The declaration of a - // face quadrature formula is then - // straightforward: + // @sect4{HelmholtzProblem::assemble_system} + + // Assembling the system of equations + // for the problem at hand is mostly + // as for the example programs + // before. However, some things have + // changed anyway, so we comment on + // this function fairly extensively. + // + // At the top of the function you will find + // the usual assortment of variable + // declarations. Compared to previous + // programs, of importance is only that we + // expect to solve problems also with + // bi-quadratic elements and therefore have + // to use sufficiently accurate quadrature + // formula. In addition, we need to compute + // integrals over faces, i.e. dim-1 + // dimensional objects. The declaration of a + // face quadrature formula is then + // straightforward: template void HelmholtzProblem::assemble_system () { @@ -826,52 +829,52 @@ namespace Step7 std::vector local_dof_indices (dofs_per_cell); - // Then we need objects which can - // evaluate the values, gradients, - // etc of the shape functions at - // the quadrature points. While it - // seems that it should be feasible - // to do it with one object for - // both domain and face integrals, - // there is a subtle difference - // since the weights in the domain - // integrals include the measure of - // the cell in the domain, while - // the face integral quadrature - // requires the measure of the face - // in a lower-dimensional - // manifold. Internally these two - // classes are rooted in a common - // base class which does most of - // the work and offers the same - // interface to both domain and - // interface integrals. - // - // For the domain integrals in the - // bilinear form for Helmholtz's - // equation, we need to compute the - // values and gradients, as well as - // the weights at the quadrature - // points. Furthermore, we need the - // quadrature points on the real - // cell (rather than on the unit - // cell) to evaluate the right hand - // side function. The object we use - // to get at this information is - // the FEValues class discussed - // previously. - // - // For the face integrals, we only - // need the values of the shape - // functions, as well as the - // weights. We also need the normal - // vectors and quadrature points on - // the real cell since we want to - // determine the Neumann values - // from the exact solution object - // (see below). The class that gives - // us this information is called - // FEFaceValues: + // Then we need objects which can + // evaluate the values, gradients, + // etc of the shape functions at + // the quadrature points. While it + // seems that it should be feasible + // to do it with one object for + // both domain and face integrals, + // there is a subtle difference + // since the weights in the domain + // integrals include the measure of + // the cell in the domain, while + // the face integral quadrature + // requires the measure of the face + // in a lower-dimensional + // manifold. Internally these two + // classes are rooted in a common + // base class which does most of + // the work and offers the same + // interface to both domain and + // interface integrals. + // + // For the domain integrals in the + // bilinear form for Helmholtz's + // equation, we need to compute the + // values and gradients, as well as + // the weights at the quadrature + // points. Furthermore, we need the + // quadrature points on the real + // cell (rather than on the unit + // cell) to evaluate the right hand + // side function. The object we use + // to get at this information is + // the FEValues class discussed + // previously. + // + // For the face integrals, we only + // need the values of the shape + // functions, as well as the + // weights. We also need the normal + // vectors and quadrature points on + // the real cell since we want to + // determine the Neumann values + // from the exact solution object + // (see below). The class that gives + // us this information is called + // FEFaceValues: FEValues fe_values (*fe, quadrature_formula, update_values | update_gradients | update_quadrature_points | update_JxW_values); @@ -880,49 +883,49 @@ namespace Step7 update_values | update_quadrature_points | update_normal_vectors | update_JxW_values); - // Then we need some objects - // already known from previous - // examples: An object denoting the - // right hand side function, its - // values at the quadrature points - // on a cell, the cell matrix and - // right hand side, and the indices - // of the degrees of freedom on a - // cell. - // - // Note that the operations we will do with - // the right hand side object are only - // querying data, never changing the - // object. We can therefore declare it - // const: + // Then we need some objects + // already known from previous + // examples: An object denoting the + // right hand side function, its + // values at the quadrature points + // on a cell, the cell matrix and + // right hand side, and the indices + // of the degrees of freedom on a + // cell. + // + // Note that the operations we will do with + // the right hand side object are only + // querying data, never changing the + // object. We can therefore declare it + // const: const RightHandSide right_hand_side; std::vector rhs_values (n_q_points); - // Finally we define an object - // denoting the exact solution - // function. We will use it to - // compute the Neumann values at - // the boundary from it. Usually, - // one would of course do so using - // a separate object, in particular - // since the exact solution is generally - // unknown while the Neumann values - // are prescribed. We will, - // however, be a little bit lazy - // and use what we already have in - // information. Real-life programs - // would to go other ways here, of - // course. + // Finally we define an object + // denoting the exact solution + // function. We will use it to + // compute the Neumann values at + // the boundary from it. Usually, + // one would of course do so using + // a separate object, in particular + // since the exact solution is generally + // unknown while the Neumann values + // are prescribed. We will, + // however, be a little bit lazy + // and use what we already have in + // information. Real-life programs + // would to go other ways here, of + // course. const Solution exact_solution; - // Now for the main loop over all - // cells. This is mostly unchanged - // from previous examples, so we - // only comment on the things that - // have changed. + // Now for the main loop over all + // cells. This is mostly unchanged + // from previous examples, so we + // only comment on the things that + // have changed. typename DoFHandler::active_cell_iterator - cell = dof_handler.begin_active(), - endc = dof_handler.end(); + cell = dof_handler.begin_active(), + endc = dof_handler.end(); for (; cell!=endc; ++cell) { cell_matrix = 0; @@ -937,13 +940,13 @@ namespace Step7 for (unsigned int i=0; i1, - // which is the value that we - // have assigned to that - // portions of the boundary - // composing Gamma2 in the - // run() function further - // below. (The - // default value of boundary - // indicators is 0, so faces - // can only have an indicator - // equal to 1 if we have - // explicitly set it.) + // Then there is that second + // term on the right hand side, + // the contour integral. First + // we have to find out whether + // the intersection of the faces + // of this cell with the + // boundary part Gamma2 is + // nonzero. To this end, we + // loop over all faces and + // check whether its boundary + // indicator equals 1, + // which is the value that we + // have assigned to that + // portions of the boundary + // composing Gamma2 in the + // run() function further + // below. (The + // default value of boundary + // indicators is 0, so faces + // can only have an indicator + // equal to 1 if we have + // explicitly set it.) for (unsigned int face=0; face::faces_per_cell; ++face) if (cell->face(face)->at_boundary() && (cell->face(face)->boundary_indicator() == 1)) { - // If we came into here, - // then we have found an - // external face - // belonging to - // Gamma2. Next, we have - // to compute the values - // of the shape functions - // and the other - // quantities which we - // will need for the - // computation of the - // contour integral. This - // is done using the - // reinit function - // which we already know - // from the FEValue - // class: + // If we came into here, + // then we have found an + // external face + // belonging to + // Gamma2. Next, we have + // to compute the values + // of the shape functions + // and the other + // quantities which we + // will need for the + // computation of the + // contour integral. This + // is done using the + // reinit function + // which we already know + // from the FEValue + // class: fe_face_values.reinit (cell, face); - // And we can then - // perform the - // integration by using a - // loop over all - // quadrature points. - // - // On each quadrature point, we - // first compute the value of the - // normal derivative. We do so - // using the gradient of the - // exact solution and the normal - // vector to the face at the - // present quadrature point - // obtained from the - // fe_face_values - // object. This is then used to - // compute the additional - // contribution of this face to - // the right hand side: + // And we can then + // perform the + // integration by using a + // loop over all + // quadrature points. + // + // On each quadrature point, we + // first compute the value of the + // normal derivative. We do so + // using the gradient of the + // exact solution and the normal + // vector to the face at the + // present quadrature point + // obtained from the + // fe_face_values + // object. This is then used to + // compute the additional + // contribution of this face to + // the right hand side: for (unsigned int q_point=0; q_pointget_dof_indices (local_dof_indices); for (unsigned int i=0; iinterpolate_boundary_values) - // does not represent the whole - // boundary any more. Rather, it is - // that portion of the boundary - // which we have not assigned - // another indicator (see - // below). The degrees of freedom - // at the boundary that do not - // belong to Gamma1 are therefore - // excluded from the interpolation - // of boundary values, just as - // we want. + // Likewise, elimination and treatment of + // boundary values has been shown + // previously. + // + // We note, however that now + // the boundary indicator for which + // we interpolate boundary values + // (denoted by the second parameter + // to + // interpolate_boundary_values) + // does not represent the whole + // boundary any more. Rather, it is + // that portion of the boundary + // which we have not assigned + // another indicator (see + // below). The degrees of freedom + // at the boundary that do not + // belong to Gamma1 are therefore + // excluded from the interpolation + // of boundary values, just as + // we want. hanging_node_constraints.condense (system_matrix); hanging_node_constraints.condense (system_rhs); @@ -1088,10 +1091,10 @@ namespace Step7 } - // @sect4{HelmholtzProblem::solve} + // @sect4{HelmholtzProblem::solve} - // Solving the system of equations is - // done in the same way as before: + // Solving the system of equations is + // done in the same way as before: template void HelmholtzProblem::solve () { @@ -1108,161 +1111,161 @@ namespace Step7 } - // @sect4{HelmholtzProblem::refine_grid} - - // Now for the function doing grid - // refinement. Depending on the - // refinement mode passed to the - // constructor, we do global or - // adaptive refinement. - // - // Global refinement is simple, - // so there is - // not much to comment on. - // In case of adaptive - // refinement, we use the same - // functions and classes as in - // the previous example - // program. Note that one - // could treat Neumann - // boundaries differently than - // Dirichlet boundaries, and - // one should in fact do so - // here since we have Neumann - // boundary conditions on part - // of the boundaries, but - // since we don't have a - // function here that - // describes the Neumann - // values (we only construct - // these values from the exact - // solution when assembling - // the matrix), we omit this - // detail even though they would - // not be hard to add. - // - // At the end of the switch, we have a - // default case that looks slightly strange: - // an Assert statement with a false - // condition. Since the Assert macro - // raises an error whenever the condition is - // false, this means that whenever we hit - // this statement the program will be - // aborted. This in intentional: Right now we - // have only implemented two refinement - // strategies (global and adaptive), but - // someone might want to add a third strategy - // (for example adaptivity with a different - // refinement criterion) and add a third - // member to the enumeration that determines - // the refinement mode. If it weren't for the - // default case of the switch statement, this - // function would simply run to its end - // without doing anything. This is most - // likely not what was intended. One of the - // defensive programming techniques that you - // will find all over the deal.II library is - // therefore to always have default cases - // that abort, to make sure that values not - // considered when listing the cases in the - // switch statement are eventually caught, - // and forcing programmers to add code to - // handle them. We will use this same - // technique in other places further down as - // well. + // @sect4{HelmholtzProblem::refine_grid} + + // Now for the function doing grid + // refinement. Depending on the + // refinement mode passed to the + // constructor, we do global or + // adaptive refinement. + // + // Global refinement is simple, + // so there is + // not much to comment on. + // In case of adaptive + // refinement, we use the same + // functions and classes as in + // the previous example + // program. Note that one + // could treat Neumann + // boundaries differently than + // Dirichlet boundaries, and + // one should in fact do so + // here since we have Neumann + // boundary conditions on part + // of the boundaries, but + // since we don't have a + // function here that + // describes the Neumann + // values (we only construct + // these values from the exact + // solution when assembling + // the matrix), we omit this + // detail even though they would + // not be hard to add. + // + // At the end of the switch, we have a + // default case that looks slightly strange: + // an Assert statement with a false + // condition. Since the Assert macro + // raises an error whenever the condition is + // false, this means that whenever we hit + // this statement the program will be + // aborted. This in intentional: Right now we + // have only implemented two refinement + // strategies (global and adaptive), but + // someone might want to add a third strategy + // (for example adaptivity with a different + // refinement criterion) and add a third + // member to the enumeration that determines + // the refinement mode. If it weren't for the + // default case of the switch statement, this + // function would simply run to its end + // without doing anything. This is most + // likely not what was intended. One of the + // defensive programming techniques that you + // will find all over the deal.II library is + // therefore to always have default cases + // that abort, to make sure that values not + // considered when listing the cases in the + // switch statement are eventually caught, + // and forcing programmers to add code to + // handle them. We will use this same + // technique in other places further down as + // well. template void HelmholtzProblem::refine_grid () { switch (refinement_mode) { - case global_refinement: - { - triangulation.refine_global (1); - break; - } - - case adaptive_refinement: - { - Vector estimated_error_per_cell (triangulation.n_active_cells()); - - typename FunctionMap::type neumann_boundary; - KellyErrorEstimator::estimate (dof_handler, - QGauss(3), - neumann_boundary, - solution, - estimated_error_per_cell); - - GridRefinement::refine_and_coarsen_fixed_number (triangulation, - estimated_error_per_cell, - 0.3, 0.03); - - triangulation.execute_coarsening_and_refinement (); - - break; - } - - default: - { - Assert (false, ExcNotImplemented()); - } + case global_refinement: + { + triangulation.refine_global (1); + break; + } + + case adaptive_refinement: + { + Vector estimated_error_per_cell (triangulation.n_active_cells()); + + typename FunctionMap::type neumann_boundary; + KellyErrorEstimator::estimate (dof_handler, + QGauss(3), + neumann_boundary, + solution, + estimated_error_per_cell); + + GridRefinement::refine_and_coarsen_fixed_number (triangulation, + estimated_error_per_cell, + 0.3, 0.03); + + triangulation.execute_coarsening_and_refinement (); + + break; + } + + default: + { + Assert (false, ExcNotImplemented()); + } } } - // @sect4{HelmholtzProblem::process_solution} + // @sect4{HelmholtzProblem::process_solution} - // Finally we want to process the solution - // after it has been computed. For this, we - // integrate the error in various norms, and - // we generate tables that will later be used - // to display the convergence against the - // continuous solution in a nice format. + // Finally we want to process the solution + // after it has been computed. For this, we + // integrate the error in various norms, and + // we generate tables that will later be used + // to display the convergence against the + // continuous solution in a nice format. template void HelmholtzProblem::process_solution (const unsigned int cycle) { - // Our first task is to compute - // error norms. In order to integrate - // the difference between computed - // numerical solution and the - // continuous solution (described - // by the Solution class - // defined at the top of this - // file), we first need a vector - // that will hold the norm of the - // error on each cell. Since - // accuracy with 16 digits is not - // so important for these - // quantities, we save some memory - // by using float instead of - // double values. - // - // The next step is to use a function - // from the library which computes the - // error in the L2 norm on each cell. - // We have to pass it the DoF handler - // object, the vector holding the - // nodal values of the numerical - // solution, the continuous - // solution as a function object, - // the vector into which it shall - // place the norm of the error on - // each cell, a quadrature rule by - // which this norm shall be - // computed, and the type of norm - // to be used. Here, we use a Gauss - // formula with three points in - // each space direction, and - // compute the L2 norm. - // - // Finally, we want to get the - // global L2 norm. This can of - // course be obtained by summing - // the squares of the norms on each - // cell, and taking the square root - // of that value. This is - // equivalent to taking the l2 - // (lower case l) norm of the - // vector of norms on each cell: + // Our first task is to compute + // error norms. In order to integrate + // the difference between computed + // numerical solution and the + // continuous solution (described + // by the Solution class + // defined at the top of this + // file), we first need a vector + // that will hold the norm of the + // error on each cell. Since + // accuracy with 16 digits is not + // so important for these + // quantities, we save some memory + // by using float instead of + // double values. + // + // The next step is to use a function + // from the library which computes the + // error in the L2 norm on each cell. + // We have to pass it the DoF handler + // object, the vector holding the + // nodal values of the numerical + // solution, the continuous + // solution as a function object, + // the vector into which it shall + // place the norm of the error on + // each cell, a quadrature rule by + // which this norm shall be + // computed, and the type of norm + // to be used. Here, we use a Gauss + // formula with three points in + // each space direction, and + // compute the L2 norm. + // + // Finally, we want to get the + // global L2 norm. This can of + // course be obtained by summing + // the squares of the norms on each + // cell, and taking the square root + // of that value. This is + // equivalent to taking the l2 + // (lower case l) norm of the + // vector of norms on each cell: Vector difference_per_cell (triangulation.n_active_cells()); VectorTools::integrate_difference (dof_handler, solution, @@ -1272,11 +1275,11 @@ namespace Step7 VectorTools::L2_norm); const double L2_error = difference_per_cell.l2_norm(); - // By same procedure we get the H1 - // semi-norm. We re-use the - // difference_per_cell vector since it - // is no longer used after computing the - // L2_error variable above. + // By same procedure we get the H1 + // semi-norm. We re-use the + // difference_per_cell vector since it + // is no longer used after computing the + // L2_error variable above. VectorTools::integrate_difference (dof_handler, solution, Solution(), @@ -1285,38 +1288,38 @@ namespace Step7 VectorTools::H1_seminorm); const double H1_error = difference_per_cell.l2_norm(); - // Finally, we compute the maximum - // norm. Of course, we can't - // actually compute the true maximum, - // but only the maximum at the - // quadrature points. Since this - // depends quite sensitively on the - // quadrature rule being used, and - // since we would like to avoid - // false results due to - // super-convergence effects at - // some points, we use a special - // quadrature rule that is obtained - // by iterating the trapezoidal - // rule five times in each space - // direction. Note that the - // constructor of the QIterated - // class takes a one-dimensional - // quadrature rule and a number - // that tells it how often it shall - // use this rule in each space - // direction. - // - // Using this special quadrature rule, we - // can then try to find the maximal error - // on each cell. Finally, we compute the - // global L infinity error from the L - // infinite errors on each cell. Instead of - // summing squares, we now have to take the - // maximum value over all cell-wise - // entries, an operation that is - // conveniently done using the - // Vector::linfty() function: + // Finally, we compute the maximum + // norm. Of course, we can't + // actually compute the true maximum, + // but only the maximum at the + // quadrature points. Since this + // depends quite sensitively on the + // quadrature rule being used, and + // since we would like to avoid + // false results due to + // super-convergence effects at + // some points, we use a special + // quadrature rule that is obtained + // by iterating the trapezoidal + // rule five times in each space + // direction. Note that the + // constructor of the QIterated + // class takes a one-dimensional + // quadrature rule and a number + // that tells it how often it shall + // use this rule in each space + // direction. + // + // Using this special quadrature rule, we + // can then try to find the maximal error + // on each cell. Finally, we compute the + // global L infinity error from the L + // infinite errors on each cell. Instead of + // summing squares, we now have to take the + // maximum value over all cell-wise + // entries, an operation that is + // conveniently done using the + // Vector::linfty() function: const QTrapez<1> q_trapez; const QIterated q_iterated (q_trapez, 5); VectorTools::integrate_difference (dof_handler, @@ -1327,19 +1330,19 @@ namespace Step7 VectorTools::Linfty_norm); const double Linfty_error = difference_per_cell.linfty_norm(); - // After all these errors have been - // computed, we finally write some - // output. In addition, we add the - // important data to the - // TableHandler by specifying - // the key of the column and the value. - // Note that it is not necessary to - // define column keys beforehand -- it is - // sufficient to just add values, - // and columns will be - // introduced into the table in the - // order values are added the - // first time. + // After all these errors have been + // computed, we finally write some + // output. In addition, we add the + // important data to the + // TableHandler by specifying + // the key of the column and the value. + // Note that it is not necessary to + // define column keys beforehand -- it is + // sufficient to just add values, + // and columns will be + // introduced into the table in the + // order values are added the + // first time. const unsigned int n_active_cells=triangulation.n_active_cells(); const unsigned int n_dofs=dof_handler.n_dofs(); @@ -1361,77 +1364,77 @@ namespace Step7 } - // @sect4{HelmholtzProblem::run} - - // As in previous example programs, - // the run function controls the - // flow of execution. The basic - // layout is as in previous examples: - // an outer loop over successively - // refined grids, and in this loop - // first problem setup, assembling - // the linear system, solution, and - // post-processing. - // - // The first task in the main loop is - // creation and refinement of - // grids. This is as in previous - // examples, with the only difference - // that we want to have part of the - // boundary marked as Neumann type, - // rather than Dirichlet. - // - // For this, we will use the - // following convention: Faces - // belonging to Gamma1 will have the - // boundary indicator 0 - // (which is the default, so we don't - // have to set it explicitely), and - // faces belonging to Gamma2 will use - // 1 as boundary - // indicator. To set these values, - // we loop over all cells, then over - // all faces of a given cell, check - // whether it is part of the boundary - // that we want to denote by Gamma2, - // and if so set its boundary - // indicator to 1. For - // the present program, we consider - // the left and bottom boundaries as - // Gamma2. We determine whether a - // face is part of that boundary by - // asking whether the x or y - // coordinates (i.e. vector - // components 0 and 1) of the - // midpoint of a face equals -1, up - // to some small wiggle room that we - // have to give since it is instable - // to compare floating point numbers - // that are subject to round off in - // intermediate computations. - // - // It is worth noting that we have to - // loop over all cells here, not only - // the active ones. The reason is - // that upon refinement, newly - // created faces inherit the boundary - // indicator of their parent face. If - // we now only set the boundary - // indicator for active faces, - // coarsen some cells and refine them - // later on, they will again have the - // boundary indicator of the parent - // cell which we have not modified, - // instead of the one we - // intended. Consequently, we have to - // change the boundary indicators of - // faces of all cells on Gamma2, - // whether they are active or not. - // Alternatively, we could of course - // have done this job on the coarsest - // mesh (i.e. before the first - // refinement step) and refined the - // mesh only after that. + // @sect4{HelmholtzProblem::run} + + // As in previous example programs, + // the run function controls the + // flow of execution. The basic + // layout is as in previous examples: + // an outer loop over successively + // refined grids, and in this loop + // first problem setup, assembling + // the linear system, solution, and + // post-processing. + // + // The first task in the main loop is + // creation and refinement of + // grids. This is as in previous + // examples, with the only difference + // that we want to have part of the + // boundary marked as Neumann type, + // rather than Dirichlet. + // + // For this, we will use the + // following convention: Faces + // belonging to Gamma1 will have the + // boundary indicator 0 + // (which is the default, so we don't + // have to set it explicitely), and + // faces belonging to Gamma2 will use + // 1 as boundary + // indicator. To set these values, + // we loop over all cells, then over + // all faces of a given cell, check + // whether it is part of the boundary + // that we want to denote by Gamma2, + // and if so set its boundary + // indicator to 1. For + // the present program, we consider + // the left and bottom boundaries as + // Gamma2. We determine whether a + // face is part of that boundary by + // asking whether the x or y + // coordinates (i.e. vector + // components 0 and 1) of the + // midpoint of a face equals -1, up + // to some small wiggle room that we + // have to give since it is instable + // to compare floating point numbers + // that are subject to round off in + // intermediate computations. + // + // It is worth noting that we have to + // loop over all cells here, not only + // the active ones. The reason is + // that upon refinement, newly + // created faces inherit the boundary + // indicator of their parent face. If + // we now only set the boundary + // indicator for active faces, + // coarsen some cells and refine them + // later on, they will again have the + // boundary indicator of the parent + // cell which we have not modified, + // instead of the one we + // intended. Consequently, we have to + // change the boundary indicators of + // faces of all cells on Gamma2, + // whether they are active or not. + // Alternatively, we could of course + // have done this job on the coarsest + // mesh (i.e. before the first + // refinement step) and refined the + // mesh only after that. template void HelmholtzProblem::run () { @@ -1443,8 +1446,8 @@ namespace Step7 triangulation.refine_global (1); typename Triangulation::cell_iterator - cell = triangulation.begin (), - endc = triangulation.end(); + cell = triangulation.begin (), + endc = triangulation.end(); for (; cell!=endc; ++cell) for (unsigned int face=0; face::faces_per_cell; @@ -1458,99 +1461,99 @@ namespace Step7 refine_grid (); - // The next steps are already - // known from previous - // examples. This is mostly the - // basic set-up of every finite - // element program: + // The next steps are already + // known from previous + // examples. This is mostly the + // basic set-up of every finite + // element program: setup_system (); assemble_system (); solve (); - // The last step in this chain - // of function calls is usually - // the evaluation of the computed - // solution for the quantities - // one is interested in. This - // is done in the following - // function. Since the function - // generates output that indicates - // the number of the present - // refinement step, we pass this - // number as an argument. + // The last step in this chain + // of function calls is usually + // the evaluation of the computed + // solution for the quantities + // one is interested in. This + // is done in the following + // function. Since the function + // generates output that indicates + // the number of the present + // refinement step, we pass this + // number as an argument. process_solution (cycle); } - // @sect5{Output of graphical data} - - // After the last iteration we output the - // solution on the finest grid. This is - // done using the following sequence of - // statements which we have already - // discussed in previous examples. The - // first step is to generate a suitable - // filename (called gmv_filename here, - // since we want to output data in GMV - // format; we add the prefix to distinguish - // the filename from that used for other - // output files further down below). Here, - // we augment the name by the mesh - // refinement algorithm, and as above we - // make sure that we abort the program if - // another refinement method is added and - // not handled by the following switch - // statement: + // @sect5{Output of graphical data} + + // After the last iteration we output the + // solution on the finest grid. This is + // done using the following sequence of + // statements which we have already + // discussed in previous examples. The + // first step is to generate a suitable + // filename (called gmv_filename here, + // since we want to output data in GMV + // format; we add the prefix to distinguish + // the filename from that used for other + // output files further down below). Here, + // we augment the name by the mesh + // refinement algorithm, and as above we + // make sure that we abort the program if + // another refinement method is added and + // not handled by the following switch + // statement: std::string gmv_filename; switch (refinement_mode) { - case global_refinement: - gmv_filename = "solution-global"; - break; - case adaptive_refinement: - gmv_filename = "solution-adaptive"; - break; - default: - Assert (false, ExcNotImplemented()); + case global_refinement: + gmv_filename = "solution-global"; + break; + case adaptive_refinement: + gmv_filename = "solution-adaptive"; + break; + default: + Assert (false, ExcNotImplemented()); } - // We augment the filename by a postfix - // denoting the finite element which we - // have used in the computation. To this - // end, the finite element base class - // stores the maximal polynomial degree of - // shape functions in each coordinate - // variable as a variable degree, and - // we use for the switch statement (note - // that the polynomial degree of bilinear - // shape functions is really 2, since they - // contain the term x*y; however, the - // polynomial degree in each coordinate - // variable is still only 1). We again use - // the same defensive programming technique - // to safeguard against the case that the - // polynomial degree has an unexpected - // value, using the Assert (false, - // ExcNotImplemented()) idiom in the - // default branch of the switch statement: + // We augment the filename by a postfix + // denoting the finite element which we + // have used in the computation. To this + // end, the finite element base class + // stores the maximal polynomial degree of + // shape functions in each coordinate + // variable as a variable degree, and + // we use for the switch statement (note + // that the polynomial degree of bilinear + // shape functions is really 2, since they + // contain the term x*y; however, the + // polynomial degree in each coordinate + // variable is still only 1). We again use + // the same defensive programming technique + // to safeguard against the case that the + // polynomial degree has an unexpected + // value, using the Assert (false, + // ExcNotImplemented()) idiom in the + // default branch of the switch statement: switch (fe->degree) { - case 1: - gmv_filename += "-q1"; - break; - case 2: - gmv_filename += "-q2"; - break; - - default: - Assert (false, ExcNotImplemented()); + case 1: + gmv_filename += "-q1"; + break; + case 2: + gmv_filename += "-q2"; + break; + + default: + Assert (false, ExcNotImplemented()); } - // Once we have the base name for the - // output file, we add an extension - // appropriate for GMV output, open a file, - // and add the solution vector to the - // object that will do the actual output: + // Once we have the base name for the + // output file, we add an extension + // appropriate for GMV output, open a file, + // and add the solution vector to the + // object that will do the actual output: gmv_filename += ".gmv"; std::ofstream output (gmv_filename.c_str()); @@ -1558,92 +1561,92 @@ namespace Step7 data_out.attach_dof_handler (dof_handler); data_out.add_data_vector (solution, "solution"); - // Now building the intermediate - // format as before is the next - // step. We introduce one more - // feature of deal.II here. The - // background is the following: in - // some of the runs of this - // function, we have used - // biquadratic finite - // elements. However, since almost - // all output formats only support - // bilinear data, the data is - // written only bilinear, and - // information is consequently lost. - // Of course, we can't - // change the format in which - // graphic programs accept their - // inputs, but we can write the - // data differently such that we - // more closely resemble the - // information available in the - // quadratic approximation. We can, - // for example, write each cell as - // four sub-cells with bilinear data - // each, such that we have nine - // data points for each cell in the - // triangulation. The graphic - // programs will, of course, - // display this data still only - // bilinear, but at least we have - // given some more of the - // information we have. - // - // In order to allow writing more - // than one sub-cell per actual - // cell, the build_patches - // function accepts a parameter - // (the default is 1, which is - // why you haven't seen this - // parameter in previous - // examples). This parameter - // denotes into how many sub-cells - // per space direction each cell - // shall be subdivided for - // output. For example, if you give - // 2, this leads to 4 cells in - // 2D and 8 cells in 3D. For - // quadratic elements, two - // sub-cells per space direction is - // obviously the right choice, so - // this is what we choose. In - // general, for elements of - // polynomial order q, we use - // q subdivisions, and the - // order of the elements is - // determined in the same way as - // above. - // - // With the intermediate format - // so generated, we can then actually - // write the graphical output in GMV - // format: + // Now building the intermediate + // format as before is the next + // step. We introduce one more + // feature of deal.II here. The + // background is the following: in + // some of the runs of this + // function, we have used + // biquadratic finite + // elements. However, since almost + // all output formats only support + // bilinear data, the data is + // written only bilinear, and + // information is consequently lost. + // Of course, we can't + // change the format in which + // graphic programs accept their + // inputs, but we can write the + // data differently such that we + // more closely resemble the + // information available in the + // quadratic approximation. We can, + // for example, write each cell as + // four sub-cells with bilinear data + // each, such that we have nine + // data points for each cell in the + // triangulation. The graphic + // programs will, of course, + // display this data still only + // bilinear, but at least we have + // given some more of the + // information we have. + // + // In order to allow writing more + // than one sub-cell per actual + // cell, the build_patches + // function accepts a parameter + // (the default is 1, which is + // why you haven't seen this + // parameter in previous + // examples). This parameter + // denotes into how many sub-cells + // per space direction each cell + // shall be subdivided for + // output. For example, if you give + // 2, this leads to 4 cells in + // 2D and 8 cells in 3D. For + // quadratic elements, two + // sub-cells per space direction is + // obviously the right choice, so + // this is what we choose. In + // general, for elements of + // polynomial order q, we use + // q subdivisions, and the + // order of the elements is + // determined in the same way as + // above. + // + // With the intermediate format + // so generated, we can then actually + // write the graphical output in GMV + // format: data_out.build_patches (fe->degree); data_out.write_gmv (output); - // @sect5{Output of convergence tables} - - // After graphical output, we would also - // like to generate tables from the error - // computations we have done in - // process_solution. There, we have - // filled a table object with the number of - // cells for each refinement step as well - // as the errors in different norms. - - // For a nicer textual output of this data, - // one may want to set the precision with - // which the values will be written upon - // output. We use 3 digits for this, which - // is usually sufficient for error - // norms. By default, data is written in - // fixed point notation. However, for - // columns one would like to see in - // scientific notation another function - // call sets the scientific_flag to - // true, leading to floating point - // representation of numbers. + // @sect5{Output of convergence tables} + + // After graphical output, we would also + // like to generate tables from the error + // computations we have done in + // process_solution. There, we have + // filled a table object with the number of + // cells for each refinement step as well + // as the errors in different norms. + + // For a nicer textual output of this data, + // one may want to set the precision with + // which the values will be written upon + // output. We use 3 digits for this, which + // is usually sufficient for error + // norms. By default, data is written in + // fixed point notation. However, for + // columns one would like to see in + // scientific notation another function + // call sets the scientific_flag to + // true, leading to floating point + // representation of numbers. convergence_table.set_precision("L2", 3); convergence_table.set_precision("H1", 3); convergence_table.set_precision("Linfty", 3); @@ -1652,75 +1655,75 @@ namespace Step7 convergence_table.set_scientific("H1", true); convergence_table.set_scientific("Linfty", true); - // For the output of a table into a LaTeX - // file, the default captions of the - // columns are the keys given as argument - // to the add_value functions. To have - // TeX captions that differ from the - // default ones you can specify them by the - // following function calls. - // Note, that `\\' is reduced to - // `\' by the compiler such that the - // real TeX caption is, e.g., - // `$L^\infty$-error'. + // For the output of a table into a LaTeX + // file, the default captions of the + // columns are the keys given as argument + // to the add_value functions. To have + // TeX captions that differ from the + // default ones you can specify them by the + // following function calls. + // Note, that `\\' is reduced to + // `\' by the compiler such that the + // real TeX caption is, e.g., + // `$L^\infty$-error'. convergence_table.set_tex_caption("cells", "\\# cells"); convergence_table.set_tex_caption("dofs", "\\# dofs"); convergence_table.set_tex_caption("L2", "$L^2$-error"); convergence_table.set_tex_caption("H1", "$H^1$-error"); convergence_table.set_tex_caption("Linfty", "$L^\\infty$-error"); - // Finally, the default LaTeX format for - // each column of the table is `c' - // (centered). To specify a different - // (e.g. `right') one, the following - // function may be used: + // Finally, the default LaTeX format for + // each column of the table is `c' + // (centered). To specify a different + // (e.g. `right') one, the following + // function may be used: convergence_table.set_tex_format("cells", "r"); convergence_table.set_tex_format("dofs", "r"); - // After this, we can finally write the - // table to the standard output stream - // std::cout (after one extra empty - // line, to make things look - // prettier). Note, that the output in text - // format is quite simple and that - // captions may not be printed directly - // above the specific columns. + // After this, we can finally write the + // table to the standard output stream + // std::cout (after one extra empty + // line, to make things look + // prettier). Note, that the output in text + // format is quite simple and that + // captions may not be printed directly + // above the specific columns. std::cout << std::endl; convergence_table.write_text(std::cout); - // The table can also be written - // into a LaTeX file. The (nicely) - // formatted table can be viewed at - // after calling `latex filename' - // and e.g. `xdvi filename', where - // filename is the name of the file - // to which we will write output - // now. We construct the file name - // in the same way as before, but - // with a different prefix "error": + // The table can also be written + // into a LaTeX file. The (nicely) + // formatted table can be viewed at + // after calling `latex filename' + // and e.g. `xdvi filename', where + // filename is the name of the file + // to which we will write output + // now. We construct the file name + // in the same way as before, but + // with a different prefix "error": std::string error_filename = "error"; switch (refinement_mode) { - case global_refinement: - error_filename += "-global"; - break; - case adaptive_refinement: - error_filename += "-adaptive"; - break; - default: - Assert (false, ExcNotImplemented()); + case global_refinement: + error_filename += "-global"; + break; + case adaptive_refinement: + error_filename += "-adaptive"; + break; + default: + Assert (false, ExcNotImplemented()); } switch (fe->degree) { - case 1: - error_filename += "-q1"; - break; - case 2: - error_filename += "-q2"; - break; - default: - Assert (false, ExcNotImplemented()); + case 1: + error_filename += "-q1"; + break; + case 2: + error_filename += "-q2"; + break; + default: + Assert (false, ExcNotImplemented()); } error_filename += ".tex"; @@ -1729,120 +1732,120 @@ namespace Step7 convergence_table.write_tex(error_table_file); - // @sect5{Further table manipulations} - - // In case of global refinement, it - // might be of interest to also - // output the convergence - // rates. This may be done by the - // functionality the - // ConvergenceTable offers over - // the regular - // TableHandler. However, we do - // it only for global refinement, - // since for adaptive refinement - // the determination of something - // like an order of convergence is - // somewhat more involved. While we - // are at it, we also show a few - // other things that can be done - // with tables. + // @sect5{Further table manipulations} + + // In case of global refinement, it + // might be of interest to also + // output the convergence + // rates. This may be done by the + // functionality the + // ConvergenceTable offers over + // the regular + // TableHandler. However, we do + // it only for global refinement, + // since for adaptive refinement + // the determination of something + // like an order of convergence is + // somewhat more involved. While we + // are at it, we also show a few + // other things that can be done + // with tables. if (refinement_mode==global_refinement) { - // The first thing is that one - // can group individual columns - // together to form so-called - // super columns. Essentially, - // the columns remain the same, - // but the ones that were - // grouped together will get a - // caption running across all - // columns in a group. For - // example, let's merge the - // "cycle" and "cells" columns - // into a super column named "n - // cells": + // The first thing is that one + // can group individual columns + // together to form so-called + // super columns. Essentially, + // the columns remain the same, + // but the ones that were + // grouped together will get a + // caption running across all + // columns in a group. For + // example, let's merge the + // "cycle" and "cells" columns + // into a super column named "n + // cells": convergence_table.add_column_to_supercolumn("cycle", "n cells"); convergence_table.add_column_to_supercolumn("cells", "n cells"); - // Next, it isn't necessary to - // always output all columns, - // or in the order in which - // they were originally added - // during the run. Selecting - // and re-ordering the columns - // works as follows (note that - // this includes super - // columns): + // Next, it isn't necessary to + // always output all columns, + // or in the order in which + // they were originally added + // during the run. Selecting + // and re-ordering the columns + // works as follows (note that + // this includes super + // columns): std::vector new_order; new_order.push_back("n cells"); new_order.push_back("H1"); new_order.push_back("L2"); convergence_table.set_column_order (new_order); - // For everything that happened - // to the ConvergenceTable - // until this point, it would - // have been sufficient to use - // a simple - // TableHandler. Indeed, the - // ConvergenceTable is - // derived from the - // TableHandler but it offers - // the additional functionality - // of automatically evaluating - // convergence rates. For - // example, here is how we can - // let the table compute - // reduction and convergence - // rates (convergence rates are - // the binary logarithm of the - // reduction rate): + // For everything that happened + // to the ConvergenceTable + // until this point, it would + // have been sufficient to use + // a simple + // TableHandler. Indeed, the + // ConvergenceTable is + // derived from the + // TableHandler but it offers + // the additional functionality + // of automatically evaluating + // convergence rates. For + // example, here is how we can + // let the table compute + // reduction and convergence + // rates (convergence rates are + // the binary logarithm of the + // reduction rate): convergence_table - .evaluate_convergence_rates("L2", ConvergenceTable::reduction_rate); + .evaluate_convergence_rates("L2", ConvergenceTable::reduction_rate); convergence_table - .evaluate_convergence_rates("L2", ConvergenceTable::reduction_rate_log2); + .evaluate_convergence_rates("L2", ConvergenceTable::reduction_rate_log2); convergence_table - .evaluate_convergence_rates("H1", ConvergenceTable::reduction_rate_log2); - // Each of these - // function calls produces an - // additional column that is - // merged with the original - // column (in our example the - // `L2' and the `H1' column) to - // a supercolumn. - - // Finally, we want to write - // this convergence chart - // again, first to the screen - // and then, in LaTeX format, - // to disk. The filename is - // again constructed as above. + .evaluate_convergence_rates("H1", ConvergenceTable::reduction_rate_log2); + // Each of these + // function calls produces an + // additional column that is + // merged with the original + // column (in our example the + // `L2' and the `H1' column) to + // a supercolumn. + + // Finally, we want to write + // this convergence chart + // again, first to the screen + // and then, in LaTeX format, + // to disk. The filename is + // again constructed as above. std::cout << std::endl; convergence_table.write_text(std::cout); std::string conv_filename = "convergence"; switch (refinement_mode) { - case global_refinement: - conv_filename += "-global"; - break; - case adaptive_refinement: - conv_filename += "-adaptive"; - break; - default: - Assert (false, ExcNotImplemented()); + case global_refinement: + conv_filename += "-global"; + break; + case adaptive_refinement: + conv_filename += "-adaptive"; + break; + default: + Assert (false, ExcNotImplemented()); } switch (fe->degree) { - case 1: - conv_filename += "-q1"; - break; - case 2: - conv_filename += "-q2"; - break; - default: - Assert (false, ExcNotImplemented()); + case 1: + conv_filename += "-q1"; + break; + case 2: + conv_filename += "-q2"; + break; + default: + Assert (false, ExcNotImplemented()); } conv_filename += ".tex"; @@ -1851,34 +1854,34 @@ namespace Step7 } } - // The final step before going to - // main() is then to close the - // namespace Step7 into which - // we have put everything we needed for - // this program: + // The final step before going to + // main() is then to close the + // namespace Step7 into which + // we have put everything we needed for + // this program: } - // @sect3{Main function} - - // The main function is mostly as - // before. The only difference is - // that we solve three times, once - // for Q1 and adaptive refinement, - // once for Q1 elements and global - // refinement, and once for Q2 - // elements and global refinement. - // - // Since we instantiate several - // template classes below for two - // space dimensions, we make this - // more generic by declaring a - // constant at the beginning of the - // function denoting the number of - // space dimensions. If you want to - // run the program in 1d or 2d, you - // will then only have to change this - // one instance, rather than all uses - // below: +// @sect3{Main function} + +// The main function is mostly as +// before. The only difference is +// that we solve three times, once +// for Q1 and adaptive refinement, +// once for Q1 elements and global +// refinement, and once for Q2 +// elements and global refinement. +// +// Since we instantiate several +// template classes below for two +// space dimensions, we make this +// more generic by declaring a +// constant at the beginning of the +// function denoting the number of +// space dimensions. If you want to +// run the program in 1d or 2d, you +// will then only have to change this +// one instance, rather than all uses +// below: int main () { const unsigned int dim = 2; @@ -1890,23 +1893,23 @@ int main () deallog.depth_console (0); - // Now for the three calls to - // the main class. Each call is - // blocked into curly braces in - // order to destroy the - // respective objects (i.e. the - // finite element and the - // HelmholtzProblem object) - // at the end of the block and - // before we go to the next - // run. This avoids conflicts - // with variable names, and - // also makes sure that memory - // is released immediately - // after one of the three runs - // has finished, and not only - // at the end of the try - // block. + // Now for the three calls to + // the main class. Each call is + // blocked into curly braces in + // order to destroy the + // respective objects (i.e. the + // finite element and the + // HelmholtzProblem object) + // at the end of the block and + // before we go to the next + // run. This avoids conflicts + // with variable names, and + // also makes sure that memory + // is released immediately + // after one of the three runs + // has finished, and not only + // at the end of the try + // block. { std::cout << "Solving with Q1 elements, adaptive refinement" << std::endl << "=============================================" << std::endl @@ -1914,7 +1917,7 @@ int main () FE_Q fe(1); HelmholtzProblem - helmholtz_problem_2d (fe, HelmholtzProblem::adaptive_refinement); + helmholtz_problem_2d (fe, HelmholtzProblem::adaptive_refinement); helmholtz_problem_2d.run (); @@ -1928,7 +1931,7 @@ int main () FE_Q fe(1); HelmholtzProblem - helmholtz_problem_2d (fe, HelmholtzProblem::global_refinement); + helmholtz_problem_2d (fe, HelmholtzProblem::global_refinement); helmholtz_problem_2d.run (); @@ -1942,7 +1945,7 @@ int main () FE_Q fe(2); HelmholtzProblem - helmholtz_problem_2d (fe, HelmholtzProblem::global_refinement); + helmholtz_problem_2d (fe, HelmholtzProblem::global_refinement); helmholtz_problem_2d.run (); @@ -1978,22 +1981,22 @@ int main () } - // What comes here is basically just - // an annoyance that you can ignore - // if you are not working on an AIX - // system: on this system, static - // member variables are not - // instantiated automatically when - // their enclosing class is - // instantiated. This leads to linker - // errors if these variables are not - // explicitly instantiated. As said, - // this is, strictly C++ standards - // speaking, not necessary, but it - // doesn't hurt either on other - // systems, and since it is necessary - // to get things running on AIX, why - // not do it: +// What comes here is basically just +// an annoyance that you can ignore +// if you are not working on an AIX +// system: on this system, static +// member variables are not +// instantiated automatically when +// their enclosing class is +// instantiated. This leads to linker +// errors if these variables are not +// explicitly instantiated. As said, +// this is, strictly C++ standards +// speaking, not necessary, but it +// doesn't hurt either on other +// systems, and since it is necessary +// to get things running on AIX, why +// not do it: namespace Step7 { template const double SolutionBase<2>::width; diff --git a/deal.II/examples/step-8/step-8.cc b/deal.II/examples/step-8/step-8.cc index 4f29f8120f..ae1da7c04d 100644 --- a/deal.II/examples/step-8/step-8.cc +++ b/deal.II/examples/step-8/step-8.cc @@ -9,11 +9,11 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // @sect3{Include files} +// @sect3{Include files} - // As usual, the first few include - // files are already known, so we - // will not comment on them further. +// As usual, the first few include +// files are already known, so we +// will not comment on them further. #include #include #include @@ -38,217 +38,217 @@ #include #include - // In this example, we need - // vector-valued finite elements. The - // support for these can be found in - // the following include file: +// In this example, we need +// vector-valued finite elements. The +// support for these can be found in +// the following include file: #include - // We will compose the vector-valued - // finite elements from regular Q1 - // elements which can be found here, - // as usual: +// We will compose the vector-valued +// finite elements from regular Q1 +// elements which can be found here, +// as usual: #include - // This again is C++: +// This again is C++: #include #include - // The last step is as in previous - // programs. In particular, just like in - // step-7, we pack everything that's specific - // to this program into a namespace of its - // own. +// The last step is as in previous +// programs. In particular, just like in +// step-7, we pack everything that's specific +// to this program into a namespace of its +// own. namespace Step8 { using namespace dealii; - // @sect3{The ElasticProblem class template} - - // The main class is, except for its - // name, almost unchanged with - // respect to the step-6 example. - // - // The only change is the use of a - // different class for the fe - // variable: Instead of a concrete - // finite element class such as - // FE_Q, we now use a more - // generic one, FESystem. In - // fact, FESystem is not really a - // finite element itself in that it - // does not implement shape functions - // of its own. Rather, it is a class - // that can be used to stack several - // other elements together to form - // one vector-valued finite - // element. In our case, we will - // compose the vector-valued element - // of FE_Q(1) objects, as shown - // below in the constructor of this - // class. + // @sect3{The ElasticProblem class template} + + // The main class is, except for its + // name, almost unchanged with + // respect to the step-6 example. + // + // The only change is the use of a + // different class for the fe + // variable: Instead of a concrete + // finite element class such as + // FE_Q, we now use a more + // generic one, FESystem. In + // fact, FESystem is not really a + // finite element itself in that it + // does not implement shape functions + // of its own. Rather, it is a class + // that can be used to stack several + // other elements together to form + // one vector-valued finite + // element. In our case, we will + // compose the vector-valued element + // of FE_Q(1) objects, as shown + // below in the constructor of this + // class. template class ElasticProblem { - public: - ElasticProblem (); - ~ElasticProblem (); - void run (); + public: + ElasticProblem (); + ~ElasticProblem (); + void run (); - private: - void setup_system (); - void assemble_system (); - void solve (); - void refine_grid (); - void output_results (const unsigned int cycle) const; + private: + void setup_system (); + void assemble_system (); + void solve (); + void refine_grid (); + void output_results (const unsigned int cycle) const; - Triangulation triangulation; - DoFHandler dof_handler; + Triangulation triangulation; + DoFHandler dof_handler; - FESystem fe; + FESystem fe; - ConstraintMatrix hanging_node_constraints; + ConstraintMatrix hanging_node_constraints; - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; - Vector solution; - Vector system_rhs; + Vector solution; + Vector system_rhs; }; - // @sect3{Right hand side values} - - // Before going over to the - // implementation of the main class, - // we declare and define the class - // which describes the right hand - // side. This time, the right hand - // side is vector-valued, as is the - // solution, so we will describe the - // changes required for this in some - // more detail. - // - // The first thing is that - // vector-valued functions have to - // have a constructor, since they - // need to pass down to the base - // class of how many components the - // function consists. The default - // value in the constructor of the - // base class is one (i.e.: a scalar - // function), which is why we did not - // need not define a constructor for - // the scalar function used in - // previous programs. + // @sect3{Right hand side values} + + // Before going over to the + // implementation of the main class, + // we declare and define the class + // which describes the right hand + // side. This time, the right hand + // side is vector-valued, as is the + // solution, so we will describe the + // changes required for this in some + // more detail. + // + // The first thing is that + // vector-valued functions have to + // have a constructor, since they + // need to pass down to the base + // class of how many components the + // function consists. The default + // value in the constructor of the + // base class is one (i.e.: a scalar + // function), which is why we did not + // need not define a constructor for + // the scalar function used in + // previous programs. template class RightHandSide : public Function { - public: - RightHandSide (); - - // The next change is that we - // want a replacement for the - // value function of the - // previous examples. There, a - // second parameter component - // was given, which denoted which - // component was requested. Here, - // we implement a function that - // returns the whole vector of - // values at the given place at - // once, in the second argument - // of the function. The obvious - // name for such a replacement - // function is vector_value. - // - // Secondly, in analogy to the - // value_list function, there - // is a function - // vector_value_list, which - // returns the values of the - // vector-valued function at - // several points at once: - virtual void vector_value (const Point &p, - Vector &values) const; - - virtual void vector_value_list (const std::vector > &points, - std::vector > &value_list) const; + public: + RightHandSide (); + + // The next change is that we + // want a replacement for the + // value function of the + // previous examples. There, a + // second parameter component + // was given, which denoted which + // component was requested. Here, + // we implement a function that + // returns the whole vector of + // values at the given place at + // once, in the second argument + // of the function. The obvious + // name for such a replacement + // function is vector_value. + // + // Secondly, in analogy to the + // value_list function, there + // is a function + // vector_value_list, which + // returns the values of the + // vector-valued function at + // several points at once: + virtual void vector_value (const Point &p, + Vector &values) const; + + virtual void vector_value_list (const std::vector > &points, + std::vector > &value_list) const; }; - // This is the constructor of the - // right hand side class. As said - // above, it only passes down to the - // base class the number of - // components, which is dim in - // the present case (one force - // component in each of the dim - // space directions). - // - // Some people would have moved the - // definition of such a short - // function right into the class - // declaration. We do not do that, as - // a matter of style: the deal.II - // style guides require that class - // declarations contain only - // declarations, and that definitions - // are always to be found - // outside. This is, obviously, as - // much as matter of taste as - // indentation, but we try to be - // consistent in this direction. + // This is the constructor of the + // right hand side class. As said + // above, it only passes down to the + // base class the number of + // components, which is dim in + // the present case (one force + // component in each of the dim + // space directions). + // + // Some people would have moved the + // definition of such a short + // function right into the class + // declaration. We do not do that, as + // a matter of style: the deal.II + // style guides require that class + // declarations contain only + // declarations, and that definitions + // are always to be found + // outside. This is, obviously, as + // much as matter of taste as + // indentation, but we try to be + // consistent in this direction. template RightHandSide::RightHandSide () - : - Function (dim) + : + Function (dim) {} - // Next the function that returns - // the whole vector of values at the - // point p at once. - // - // To prevent cases where the return - // vector has not previously been set - // to the right size we test for this - // case and otherwise throw an - // exception at the beginning of the - // function. Note that enforcing that - // output arguments already have the - // correct size is a convention in - // deal.II, and enforced almost - // everywhere. The reason is that we - // would otherwise have to check at - // the beginning of the function and - // possibly change the size of the - // output vector. This is expensive, - // and would almost always be - // unnecessary (the first call to the - // function would set the vector to - // the right size, and subsequent - // calls would only have to do - // redundant checks). In addition, - // checking and possibly resizing the - // vector is an operation that can - // not be removed if we can't rely on - // the assumption that the vector - // already has the correct size; this - // is in contract to the Assert - // call that is completely removed if - // the program is compiled in - // optimized mode. - // - // Likewise, if by some accident - // someone tried to compile and run - // the program in only one space - // dimension (in which the elastic - // equations do not make much sense - // since they reduce to the ordinary - // Laplace equation), we terminate - // the program in the second - // assertion. The program will work - // just fine in 3d, however. + // Next the function that returns + // the whole vector of values at the + // point p at once. + // + // To prevent cases where the return + // vector has not previously been set + // to the right size we test for this + // case and otherwise throw an + // exception at the beginning of the + // function. Note that enforcing that + // output arguments already have the + // correct size is a convention in + // deal.II, and enforced almost + // everywhere. The reason is that we + // would otherwise have to check at + // the beginning of the function and + // possibly change the size of the + // output vector. This is expensive, + // and would almost always be + // unnecessary (the first call to the + // function would set the vector to + // the right size, and subsequent + // calls would only have to do + // redundant checks). In addition, + // checking and possibly resizing the + // vector is an operation that can + // not be removed if we can't rely on + // the assumption that the vector + // already has the correct size; this + // is in contract to the Assert + // call that is completely removed if + // the program is compiled in + // optimized mode. + // + // Likewise, if by some accident + // someone tried to compile and run + // the program in only one space + // dimension (in which the elastic + // equations do not make much sense + // since they reduce to the ordinary + // Laplace equation), we terminate + // the program in the second + // assertion. The program will work + // just fine in 3d, however. template inline void RightHandSide::vector_value (const Point &p, @@ -258,43 +258,43 @@ namespace Step8 ExcDimensionMismatch (values.size(), dim)); Assert (dim >= 2, ExcNotImplemented()); - // The rest of the function - // implements computing force - // values. We will use a constant - // (unit) force in x-direction - // located in two little circles - // (or spheres, in 3d) around - // points (0.5,0) and (-0.5,0), and - // y-force in an area around the - // origin; in 3d, the z-component - // of these centers is zero as - // well. - // - // For this, let us first define - // two objects that denote the - // centers of these areas. Note - // that upon construction of the - // Point objects, all - // components are set to zero. + // The rest of the function + // implements computing force + // values. We will use a constant + // (unit) force in x-direction + // located in two little circles + // (or spheres, in 3d) around + // points (0.5,0) and (-0.5,0), and + // y-force in an area around the + // origin; in 3d, the z-component + // of these centers is zero as + // well. + // + // For this, let us first define + // two objects that denote the + // centers of these areas. Note + // that upon construction of the + // Point objects, all + // components are set to zero. Point point_1, point_2; point_1(0) = 0.5; point_2(0) = -0.5; - // If now the point p is in a - // circle (sphere) of radius 0.2 - // around one of these points, then - // set the force in x-direction to - // one, otherwise to zero: + // If now the point p is in a + // circle (sphere) of radius 0.2 + // around one of these points, then + // set the force in x-direction to + // one, otherwise to zero: if (((p-point_1).square() < 0.2*0.2) || ((p-point_2).square() < 0.2*0.2)) values(0) = 1; else values(0) = 0; - // Likewise, if p is in the - // vicinity of the origin, then set - // the y-force to 1, otherwise to - // zero: + // Likewise, if p is in the + // vicinity of the origin, then set + // the y-force to 1, otherwise to + // zero: if (p.square() < 0.2*0.2) values(1) = 1; else @@ -303,19 +303,19 @@ namespace Step8 - // Now, this is the function of the - // right hand side class that returns - // the values at several points at - // once. The function starts out with - // checking that the number of input - // and output arguments is equal (the - // sizes of the individual output - // vectors will be checked in the - // function that we call further down - // below). Next, we define an - // abbreviation for the number of - // points which we shall work on, to - // make some things simpler below. + // Now, this is the function of the + // right hand side class that returns + // the values at several points at + // once. The function starts out with + // checking that the number of input + // and output arguments is equal (the + // sizes of the individual output + // vectors will be checked in the + // function that we call further down + // below). Next, we define an + // abbreviation for the number of + // points which we shall work on, to + // make some things simpler below. template void RightHandSide::vector_value_list (const std::vector > &points, std::vector > &value_list) const @@ -325,79 +325,79 @@ namespace Step8 const unsigned int n_points = points.size(); - // Finally we treat each of the - // points. In one of the previous - // examples, we have explained why - // the - // value_list/vector_value_list - // function had been introduced: to - // prevent us from calling virtual - // functions too frequently. On the - // other hand, we now need to - // implement the same function - // twice, which can lead to - // confusion if one function is - // changed but the other is - // not. - // - // We can prevent this situation by - // calling - // RightHandSide::vector_value - // on each point in the input - // list. Note that by giving the - // full name of the function, - // including the class name, we - // instruct the compiler to - // explicitly call this function, - // and not to use the virtual - // function call mechanism that - // would be used if we had just - // called vector_value. This is - // important, since the compiler - // generally can't make any - // assumptions which function is - // called when using virtual - // functions, and it therefore - // can't inline the called function - // into the site of the call. On - // the contrary, here we give the - // fully qualified name, which - // bypasses the virtual function - // call, and consequently the - // compiler knows exactly which - // function is called and will - // inline above function into the - // present location. (Note that we - // have declared the - // vector_value function above - // inline, though modern - // compilers are also able to - // inline functions even if they - // have not been declared as - // inline). - // - // It is worth noting why we go to - // such length explaining what we - // do. Using this construct, we - // manage to avoid any - // inconsistency: if we want to - // change the right hand side - // function, it would be difficult - // to always remember that we - // always have to change two - // functions in the same way. Using - // this forwarding mechanism, we - // only have to change a single - // place (the vector_value - // function), and the second place - // (the vector_value_list - // function) will always be - // consistent with it. At the same - // time, using virtual function - // call bypassing, the code is no - // less efficient than if we had - // written it twice in the first - // place: + // Finally we treat each of the + // points. In one of the previous + // examples, we have explained why + // the + // value_list/vector_value_list + // function had been introduced: to + // prevent us from calling virtual + // functions too frequently. On the + // other hand, we now need to + // implement the same function + // twice, which can lead to + // confusion if one function is + // changed but the other is + // not. + // + // We can prevent this situation by + // calling + // RightHandSide::vector_value + // on each point in the input + // list. Note that by giving the + // full name of the function, + // including the class name, we + // instruct the compiler to + // explicitly call this function, + // and not to use the virtual + // function call mechanism that + // would be used if we had just + // called vector_value. This is + // important, since the compiler + // generally can't make any + // assumptions which function is + // called when using virtual + // functions, and it therefore + // can't inline the called function + // into the site of the call. On + // the contrary, here we give the + // fully qualified name, which + // bypasses the virtual function + // call, and consequently the + // compiler knows exactly which + // function is called and will + // inline above function into the + // present location. (Note that we + // have declared the + // vector_value function above + // inline, though modern + // compilers are also able to + // inline functions even if they + // have not been declared as + // inline). + // + // It is worth noting why we go to + // such length explaining what we + // do. Using this construct, we + // manage to avoid any + // inconsistency: if we want to + // change the right hand side + // function, it would be difficult + // to always remember that we + // always have to change two + // functions in the same way. Using + // this forwarding mechanism, we + // only have to change a single + // place (the vector_value + // function), and the second place + // (the vector_value_list + // function) will always be + // consistent with it. At the same + // time, using virtual function + // call bypassing, the code is no + // less efficient than if we had + // written it twice in the first + // place: for (unsigned int p=0; p::vector_value (points[p], value_list[p]); @@ -405,53 +405,53 @@ namespace Step8 - // @sect3{The ElasticProblem class implementation} - - // @sect4{ElasticProblem::ElasticProblem} - - // Following is the constructor of - // the main class. As said before, we - // would like to construct a - // vector-valued finite element that - // is composed of several scalar - // finite elements (i.e., we want to - // build the vector-valued element so - // that each of its vector components - // consists of the shape functions of - // a scalar element). Of course, the - // number of scalar finite elements we - // would like to stack together - // equals the number of components - // the solution function has, which - // is dim since we consider - // displacement in each space - // direction. The FESystem class - // can handle this: we pass it the - // finite element of which we would - // like to compose the system of, and - // how often it shall be repeated: + // @sect3{The ElasticProblem class implementation} + + // @sect4{ElasticProblem::ElasticProblem} + + // Following is the constructor of + // the main class. As said before, we + // would like to construct a + // vector-valued finite element that + // is composed of several scalar + // finite elements (i.e., we want to + // build the vector-valued element so + // that each of its vector components + // consists of the shape functions of + // a scalar element). Of course, the + // number of scalar finite elements we + // would like to stack together + // equals the number of components + // the solution function has, which + // is dim since we consider + // displacement in each space + // direction. The FESystem class + // can handle this: we pass it the + // finite element of which we would + // like to compose the system of, and + // how often it shall be repeated: template ElasticProblem::ElasticProblem () - : - dof_handler (triangulation), - fe (FE_Q(1), dim) + : + dof_handler (triangulation), + fe (FE_Q(1), dim) {} - // In fact, the FESystem class - // has several more constructors - // which can perform more complex - // operations than just stacking - // together several scalar finite - // elements of the same type into - // one; we will get to know these - // possibilities in later examples. + // In fact, the FESystem class + // has several more constructors + // which can perform more complex + // operations than just stacking + // together several scalar finite + // elements of the same type into + // one; we will get to know these + // possibilities in later examples. - // @sect4{ElasticProblem::~ElasticProblem} + // @sect4{ElasticProblem::~ElasticProblem} - // The destructor, on the other hand, - // is exactly as in step-6: + // The destructor, on the other hand, + // is exactly as in step-6: template ElasticProblem::~ElasticProblem () { @@ -459,29 +459,29 @@ namespace Step8 } - // @sect4{ElasticProblem::setup_system} - - // Setting up the system of equations - // is identitical to the function - // used in the step-6 example. The - // DoFHandler class and all other - // classes used here are fully aware - // that the finite element we want to - // use is vector-valued, and take - // care of the vector-valuedness of - // the finite element themselves. (In - // fact, they do not, but this does - // not need to bother you: since they - // only need to know how many degrees - // of freedom there are per vertex, - // line and cell, and they do not ask - // what they represent, i.e. whether - // the finite element under - // consideration is vector-valued or - // whether it is, for example, a - // scalar Hermite element with - // several degrees of freedom on each - // vertex). + // @sect4{ElasticProblem::setup_system} + + // Setting up the system of equations + // is identitical to the function + // used in the step-6 example. The + // DoFHandler class and all other + // classes used here are fully aware + // that the finite element we want to + // use is vector-valued, and take + // care of the vector-valuedness of + // the finite element themselves. (In + // fact, they do not, but this does + // not need to bother you: since they + // only need to know how many degrees + // of freedom there are per vertex, + // line and cell, and they do not ask + // what they represent, i.e. whether + // the finite element under + // consideration is vector-valued or + // whether it is, for example, a + // scalar Hermite element with + // several degrees of freedom on each + // vertex). template void ElasticProblem::setup_system () { @@ -506,37 +506,37 @@ namespace Step8 } - // @sect4{ElasticProblem::assemble_system} - - // The big changes in this program - // are in the creation of matrix and - // right hand side, since they are - // problem-dependent. We will go - // through that process step-by-step, - // since it is a bit more complicated - // than in previous examples. - // - // The first parts of this function - // are the same as before, however: - // setting up a suitable quadrature - // formula, initializing an - // FEValues object for the - // (vector-valued) finite element we - // use as well as the quadrature - // object, and declaring a number of - // auxiliary arrays. In addition, we - // declare the ever same two - // abbreviations: n_q_points and - // dofs_per_cell. The number of - // degrees of freedom per cell we now - // obviously ask from the composed - // finite element rather than from - // the underlying scalar Q1 - // element. Here, it is dim times - // the number of degrees of freedom - // per cell of the Q1 element, though - // this is not explicit knowledge we - // need to care about: + // @sect4{ElasticProblem::assemble_system} + + // The big changes in this program + // are in the creation of matrix and + // right hand side, since they are + // problem-dependent. We will go + // through that process step-by-step, + // since it is a bit more complicated + // than in previous examples. + // + // The first parts of this function + // are the same as before, however: + // setting up a suitable quadrature + // formula, initializing an + // FEValues object for the + // (vector-valued) finite element we + // use as well as the quadrature + // object, and declaring a number of + // auxiliary arrays. In addition, we + // declare the ever same two + // abbreviations: n_q_points and + // dofs_per_cell. The number of + // degrees of freedom per cell we now + // obviously ask from the composed + // finite element rather than from + // the underlying scalar Q1 + // element. Here, it is dim times + // the number of degrees of freedom + // per cell of the Q1 element, though + // this is not explicit knowledge we + // need to care about: template void ElasticProblem::assemble_system () { @@ -554,49 +554,49 @@ namespace Step8 std::vector local_dof_indices (dofs_per_cell); - // As was shown in previous - // examples as well, we need a - // place where to store the values - // of the coefficients at all the - // quadrature points on a cell. In - // the present situation, we have - // two coefficients, lambda and mu. + // As was shown in previous + // examples as well, we need a + // place where to store the values + // of the coefficients at all the + // quadrature points on a cell. In + // the present situation, we have + // two coefficients, lambda and mu. std::vector lambda_values (n_q_points); std::vector mu_values (n_q_points); - // Well, we could as well have - // omitted the above two arrays - // since we will use constant - // coefficients for both lambda and - // mu, which can be declared like - // this. They both represent - // functions always returning the - // constant value 1.0. Although we - // could omit the respective - // factors in the assemblage of the - // matrix, we use them here for - // purpose of demonstration. + // Well, we could as well have + // omitted the above two arrays + // since we will use constant + // coefficients for both lambda and + // mu, which can be declared like + // this. They both represent + // functions always returning the + // constant value 1.0. Although we + // could omit the respective + // factors in the assemblage of the + // matrix, we use them here for + // purpose of demonstration. ConstantFunction lambda(1.), mu(1.); - // Then again, we need to have the - // same for the right hand - // side. This is exactly as before - // in previous examples. However, - // we now have a vector-valued - // right hand side, which is why - // the data type of the - // rhs_values array is - // changed. We initialize it by - // n_q_points elements, each of - // which is a Vector@ - // with dim elements. + // Then again, we need to have the + // same for the right hand + // side. This is exactly as before + // in previous examples. However, + // we now have a vector-valued + // right hand side, which is why + // the data type of the + // rhs_values array is + // changed. We initialize it by + // n_q_points elements, each of + // which is a Vector@ + // with dim elements. RightHandSide right_hand_side; std::vector > rhs_values (n_q_points, Vector(dim)); - // Now we can begin with the loop - // over all cells: + // Now we can begin with the loop + // over all cells: typename DoFHandler::active_cell_iterator cell = dof_handler.begin_active(), endc = dof_handler.end(); for (; cell!=endc; ++cell) @@ -606,111 +606,111 @@ namespace Step8 fe_values.reinit (cell); - // Next we get the values of - // the coefficients at the - // quadrature points. Likewise - // for the right hand side: + // Next we get the values of + // the coefficients at the + // quadrature points. Likewise + // for the right hand side: lambda.value_list (fe_values.get_quadrature_points(), lambda_values); mu.value_list (fe_values.get_quadrature_points(), mu_values); right_hand_side.vector_value_list (fe_values.get_quadrature_points(), rhs_values); - // Then assemble the entries of - // the local stiffness matrix - // and right hand side - // vector. This follows almost - // one-to-one the pattern - // described in the - // introduction of this - // example. One of the few - // comments in place is that we - // can compute the number - // comp(i), i.e. the index - // of the only nonzero vector - // component of shape function - // i using the - // fe.system_to_component_index(i).first - // function call below. - // - // (By accessing the - // first variable of - // the return value of the - // system_to_component_index - // function, you might - // already have guessed - // that there is more in - // it. In fact, the - // function returns a - // std::pair@, of - // which the first element - // is comp(i) and the - // second is the value - // base(i) also noted - // in the introduction, i.e. - // the index - // of this shape function - // within all the shape - // functions that are nonzero - // in this component, - // i.e. base(i) in the - // diction of the - // introduction. This is not a - // number that we are usually - // interested in, however.) - // - // With this knowledge, we can - // assemble the local matrix - // contributions: + // Then assemble the entries of + // the local stiffness matrix + // and right hand side + // vector. This follows almost + // one-to-one the pattern + // described in the + // introduction of this + // example. One of the few + // comments in place is that we + // can compute the number + // comp(i), i.e. the index + // of the only nonzero vector + // component of shape function + // i using the + // fe.system_to_component_index(i).first + // function call below. + // + // (By accessing the + // first variable of + // the return value of the + // system_to_component_index + // function, you might + // already have guessed + // that there is more in + // it. In fact, the + // function returns a + // std::pair@, of + // which the first element + // is comp(i) and the + // second is the value + // base(i) also noted + // in the introduction, i.e. + // the index + // of this shape function + // within all the shape + // functions that are nonzero + // in this component, + // i.e. base(i) in the + // diction of the + // introduction. This is not a + // number that we are usually + // interested in, however.) + // + // With this knowledge, we can + // assemble the local matrix + // contributions: for (unsigned int i=0; ishape_grad(i,q_point) - // returns the - // gradient of - // the only - // nonzero - // component of - // the i-th shape - // function at - // quadrature - // point - // q_point. The - // component - // comp(i) of - // the gradient, - // which is the - // derivative of - // this only - // nonzero vector - // component of - // the i-th shape - // function with - // respect to the - // comp(i)th - // coordinate is - // accessed by - // the appended - // brackets. + += + // The first term + // is (lambda d_i + // u_i, d_j v_j) + // + (mu d_i u_j, + // d_j v_i). + // Note that + // shape_grad(i,q_point) + // returns the + // gradient of + // the only + // nonzero + // component of + // the i-th shape + // function at + // quadrature + // point + // q_point. The + // component + // comp(i) of + // the gradient, + // which is the + // derivative of + // this only + // nonzero vector + // component of + // the i-th shape + // function with + // respect to the + // comp(i)th + // coordinate is + // accessed by + // the appended + // brackets. ( (fe_values.shape_grad(i,q_point)[component_i] * fe_values.shape_grad(j,q_point)[component_j] * @@ -720,45 +720,45 @@ namespace Step8 fe_values.shape_grad(j,q_point)[component_i] * mu_values[q_point]) + - // The second term is - // (mu nabla u_i, nabla v_j). - // We need not - // access a - // specific - // component of - // the - // gradient, - // since we - // only have to - // compute the - // scalar - // product of - // the two - // gradients, - // of which an - // overloaded - // version of - // the - // operator* - // takes care, - // as in - // previous - // examples. - // - // Note that by - // using the ?: - // operator, we - // only do this - // if comp(i) - // equals - // comp(j), - // otherwise a - // zero is - // added (which - // will be - // optimized - // away by the - // compiler). + // The second term is + // (mu nabla u_i, nabla v_j). + // We need not + // access a + // specific + // component of + // the + // gradient, + // since we + // only have to + // compute the + // scalar + // product of + // the two + // gradients, + // of which an + // overloaded + // version of + // the + // operator* + // takes care, + // as in + // previous + // examples. + // + // Note that by + // using the ?: + // operator, we + // only do this + // if comp(i) + // equals + // comp(j), + // otherwise a + // zero is + // added (which + // will be + // optimized + // away by the + // compiler). ((component_i == component_j) ? (fe_values.shape_grad(i,q_point) * fe_values.shape_grad(j,q_point) * @@ -771,14 +771,14 @@ namespace Step8 } } - // Assembling the right hand - // side is also just as - // discussed in the - // introduction: + // Assembling the right hand + // side is also just as + // discussed in the + // introduction: for (unsigned int i=0; iget_dof_indices (local_dof_indices); for (unsigned int i=0; iZeroFunction - // constructor accepts a parameter - // that tells it that it shall - // represent a vector valued, - // constant zero function with that - // many components. By default, - // this parameter is equal to one, - // in which case the - // ZeroFunction object would - // represent a scalar - // function. Since the solution - // vector has dim components, - // we need to pass dim as - // number of components to the zero - // function as well. + // The interpolation of the + // boundary values needs a small + // modification: since the solution + // function is vector-valued, so + // need to be the boundary + // values. The ZeroFunction + // constructor accepts a parameter + // that tells it that it shall + // represent a vector valued, + // constant zero function with that + // many components. By default, + // this parameter is equal to one, + // in which case the + // ZeroFunction object would + // represent a scalar + // function. Since the solution + // vector has dim components, + // we need to pass dim as + // number of components to the zero + // function as well. std::map boundary_values; VectorTools::interpolate_boundary_values (dof_handler, 0, @@ -847,16 +847,16 @@ namespace Step8 - // @sect4{ElasticProblem::solve} + // @sect4{ElasticProblem::solve} - // The solver does not care about - // where the system of equations - // comes, as long as it stays - // positive definite and symmetric - // (which are the requirements for - // the use of the CG solver), which - // the system indeed is. Therefore, - // we need not change anything. + // The solver does not care about + // where the system of equations + // comes, as long as it stays + // positive definite and symmetric + // (which are the requirements for + // the use of the CG solver), which + // the system indeed is. Therefore, + // we need not change anything. template void ElasticProblem::solve () { @@ -873,32 +873,32 @@ namespace Step8 } - // @sect4{ElasticProblem::refine_grid} - - // The function that does the - // refinement of the grid is the same - // as in the step-6 example. The - // quadrature formula is adapted to - // the linear elements again. Note - // that the error estimator by - // default adds up the estimated - // obtained from all components of - // the finite element solution, i.e., - // it uses the displacement in all - // directions with the same - // weight. If we would like the grid - // to be adapted to the - // x-displacement only, we could pass - // the function an additional - // parameter which tells it to do so - // and do not consider the - // displacements in all other - // directions for the error - // indicators. However, for the - // current problem, it seems - // appropriate to consider all - // displacement components with equal - // weight. + // @sect4{ElasticProblem::refine_grid} + + // The function that does the + // refinement of the grid is the same + // as in the step-6 example. The + // quadrature formula is adapted to + // the linear elements again. Note + // that the error estimator by + // default adds up the estimated + // obtained from all components of + // the finite element solution, i.e., + // it uses the displacement in all + // directions with the same + // weight. If we would like the grid + // to be adapted to the + // x-displacement only, we could pass + // the function an additional + // parameter which tells it to do so + // and do not consider the + // displacements in all other + // directions for the error + // indicators. However, for the + // current problem, it seems + // appropriate to consider all + // displacement components with equal + // weight. template void ElasticProblem::refine_grid () { @@ -919,17 +919,17 @@ namespace Step8 } - // @sect4{ElasticProblem::output_results} + // @sect4{ElasticProblem::output_results} - // The output happens mostly as has - // been shown in previous examples - // already. The only difference is - // that the solution function is - // vector valued. The DataOut - // class takes care of this - // automatically, but we have to give - // each component of the solution - // vector a different name. + // The output happens mostly as has + // been shown in previous examples + // already. The only difference is + // that the solution function is + // vector valued. The DataOut + // class takes care of this + // automatically, but we have to give + // each component of the solution + // vector a different name. template void ElasticProblem::output_results (const unsigned int cycle) const { @@ -945,83 +945,83 @@ namespace Step8 - // As said above, we need a - // different name for each - // component of the solution - // function. To pass one name for - // each component, a vector of - // strings is used. Since the - // number of components is the same - // as the number of dimensions we - // are working in, the following - // switch statement is used. - // - // We note that some graphics - // programs have restriction as to - // what characters are allowed in - // the names of variables. The - // library therefore supports only - // the minimal subset of these - // characters that is supported by - // all programs. Basically, these - // are letters, numbers, - // underscores, and some other - // characters, but in particular no - // whitespace and minus/hyphen. The - // library will throw an exception - // otherwise, at least if in debug - // mode. - // - // After listing the 1d, 2d, and 3d - // case, it is good style to let - // the program die if we run upon a - // case which we did not - // consider. Remember that the - // Assert macro generates an - // exception if the condition in - // the first parameter is not - // satisfied. Of course, the - // condition false can never be - // satisfied, so the program will - // always abort whenever it gets to - // the default statement: + // As said above, we need a + // different name for each + // component of the solution + // function. To pass one name for + // each component, a vector of + // strings is used. Since the + // number of components is the same + // as the number of dimensions we + // are working in, the following + // switch statement is used. + // + // We note that some graphics + // programs have restriction as to + // what characters are allowed in + // the names of variables. The + // library therefore supports only + // the minimal subset of these + // characters that is supported by + // all programs. Basically, these + // are letters, numbers, + // underscores, and some other + // characters, but in particular no + // whitespace and minus/hyphen. The + // library will throw an exception + // otherwise, at least if in debug + // mode. + // + // After listing the 1d, 2d, and 3d + // case, it is good style to let + // the program die if we run upon a + // case which we did not + // consider. Remember that the + // Assert macro generates an + // exception if the condition in + // the first parameter is not + // satisfied. Of course, the + // condition false can never be + // satisfied, so the program will + // always abort whenever it gets to + // the default statement: std::vector solution_names; switch (dim) { - case 1: - solution_names.push_back ("displacement"); - break; - case 2: - solution_names.push_back ("x_displacement"); - solution_names.push_back ("y_displacement"); - break; - case 3: - solution_names.push_back ("x_displacement"); - solution_names.push_back ("y_displacement"); - solution_names.push_back ("z_displacement"); - break; - default: - Assert (false, ExcNotImplemented()); + case 1: + solution_names.push_back ("displacement"); + break; + case 2: + solution_names.push_back ("x_displacement"); + solution_names.push_back ("y_displacement"); + break; + case 3: + solution_names.push_back ("x_displacement"); + solution_names.push_back ("y_displacement"); + solution_names.push_back ("z_displacement"); + break; + default: + Assert (false, ExcNotImplemented()); } - // After setting up the names for - // the different components of the - // solution vector, we can add the - // solution vector to the list of - // data vectors scheduled for - // output. Note that the following - // function takes a vector of - // strings as second argument, - // whereas the one which we have - // used in all previous examples - // accepted a string there. In - // fact, the latter function is - // only a shortcut for the function - // which we call here: it puts the - // single string that is passed to - // it into a vector of strings with - // only one element and forwards - // that to the other function. + // After setting up the names for + // the different components of the + // solution vector, we can add the + // solution vector to the list of + // data vectors scheduled for + // output. Note that the following + // function takes a vector of + // strings as second argument, + // whereas the one which we have + // used in all previous examples + // accepted a string there. In + // fact, the latter function is + // only a shortcut for the function + // which we call here: it puts the + // single string that is passed to + // it into a vector of strings with + // only one element and forwards + // that to the other function. data_out.add_data_vector (solution, solution_names); data_out.build_patches (); data_out.write_vtk (output); @@ -1029,84 +1029,84 @@ namespace Step8 - // @sect4{ElasticProblem::run} - - // The run function does the same - // things as in step-6, for - // example. This time, we use the - // square [-1,1]^d as domain, and we - // refine it twice globally before - // starting the first iteration. - // - // The reason is the following: we - // use the Gauss quadrature - // formula with two points in each - // direction for integration of the - // right hand side; that means that - // there are four quadrature points - // on each cell (in 2D). If we only - // refine the initial grid once - // globally, then there will be only - // four quadrature points in each - // direction on the domain. However, - // the right hand side function was - // chosen to be rather localized and - // in that case all quadrature points - // lie outside the support of the - // right hand side function. The - // right hand side vector will then - // contain only zeroes and the - // solution of the system of - // equations is the zero vector, - // i.e. a finite element function - // that it zero everywhere. We should - // not be surprised about such things - // happening, since we have chosen an - // initial grid that is totally - // unsuitable for the problem at - // hand. - // - // The unfortunate thing is that if - // the discrete solution is constant, - // then the error indicators computed - // by the KellyErrorEstimator - // class are zero for each cell as - // well, and the call to - // refine_and_coarsen_fixed_number - // on the triangulation object - // will not flag any cells for - // refinement (why should it if the - // indicated error is zero for each - // cell?). The grid in the next - // iteration will therefore consist - // of four cells only as well, and - // the same problem occurs again. - // - // The conclusion needs to be: while - // of course we will not choose the - // initial grid to be well-suited for - // the accurate solution of the - // problem, we must at least choose - // it such that it has the chance to - // capture the most striking features - // of the solution. In this case, it - // needs to be able to see the right - // hand side. Thus, we refine twice - // globally. (Note that the - // refine_global function is not - // part of the GridRefinement - // class in which - // refine_and_coarsen_fixed_number - // is declared, for example. The - // reason is first that it is not an - // algorithm that computed refinement - // flags from indicators, but more - // importantly that it actually - // performs the refinement, in - // contrast to the functions in - // GridRefinement that only flag - // cells without actually refining - // the grid.) + // @sect4{ElasticProblem::run} + + // The run function does the same + // things as in step-6, for + // example. This time, we use the + // square [-1,1]^d as domain, and we + // refine it twice globally before + // starting the first iteration. + // + // The reason is the following: we + // use the Gauss quadrature + // formula with two points in each + // direction for integration of the + // right hand side; that means that + // there are four quadrature points + // on each cell (in 2D). If we only + // refine the initial grid once + // globally, then there will be only + // four quadrature points in each + // direction on the domain. However, + // the right hand side function was + // chosen to be rather localized and + // in that case all quadrature points + // lie outside the support of the + // right hand side function. The + // right hand side vector will then + // contain only zeroes and the + // solution of the system of + // equations is the zero vector, + // i.e. a finite element function + // that it zero everywhere. We should + // not be surprised about such things + // happening, since we have chosen an + // initial grid that is totally + // unsuitable for the problem at + // hand. + // + // The unfortunate thing is that if + // the discrete solution is constant, + // then the error indicators computed + // by the KellyErrorEstimator + // class are zero for each cell as + // well, and the call to + // refine_and_coarsen_fixed_number + // on the triangulation object + // will not flag any cells for + // refinement (why should it if the + // indicated error is zero for each + // cell?). The grid in the next + // iteration will therefore consist + // of four cells only as well, and + // the same problem occurs again. + // + // The conclusion needs to be: while + // of course we will not choose the + // initial grid to be well-suited for + // the accurate solution of the + // problem, we must at least choose + // it such that it has the chance to + // capture the most striking features + // of the solution. In this case, it + // needs to be able to see the right + // hand side. Thus, we refine twice + // globally. (Note that the + // refine_global function is not + // part of the GridRefinement + // class in which + // refine_and_coarsen_fixed_number + // is declared, for example. The + // reason is first that it is not an + // algorithm that computed refinement + // flags from indicators, but more + // importantly that it actually + // performs the refinement, in + // contrast to the functions in + // GridRefinement that only flag + // cells without actually refining + // the grid.) template void ElasticProblem::run () { @@ -1139,14 +1139,14 @@ namespace Step8 } } - // @sect3{The main function} +// @sect3{The main function} - // After closing the Step8 - // namespace in the last line above, the - // following is the main function of the - // program and is again exactly like in - // step-6 (apart from the changed class - // names, of course). +// After closing the Step8 +// namespace in the last line above, the +// following is the main function of the +// program and is again exactly like in +// step-6 (apart from the changed class +// names, of course). int main () { try diff --git a/deal.II/examples/step-9/step-9.cc b/deal.II/examples/step-9/step-9.cc index 40d12c9b88..3ae105a770 100644 --- a/deal.II/examples/step-9/step-9.cc +++ b/deal.II/examples/step-9/step-9.cc @@ -9,10 +9,10 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ - // Just as in previous examples, we - // have to include several files of - // which the meaning has already been - // discussed: +// Just as in previous examples, we +// have to include several files of +// which the meaning has already been +// discussed: #include #include #include @@ -38,302 +38,302 @@ #include #include - // The following two files provide classes - // and information for multi-threaded - // programs. In the first one, the classes - // and functions are declared which we need - // to start new threads and to wait for - // threads to return (i.e. the - // Thread class and the - // new_thread functions). The - // second file has a class - // MultithreadInfo (and a global - // object multithread_info of - // that type) which can be used to query the - // number of processors in your system, which - // is often useful when deciding how many - // threads to start in parallel. +// The following two files provide classes +// and information for multi-threaded +// programs. In the first one, the classes +// and functions are declared which we need +// to start new threads and to wait for +// threads to return (i.e. the +// Thread class and the +// new_thread functions). The +// second file has a class +// MultithreadInfo (and a global +// object multithread_info of +// that type) which can be used to query the +// number of processors in your system, which +// is often useful when deciding how many +// threads to start in parallel. #include #include - // The next new include file declares - // a base class TensorFunction - // not unlike the Function class, - // but with the difference that the - // return value is tensor-valued - // rather than scalar of - // vector-valued. +// The next new include file declares +// a base class TensorFunction +// not unlike the Function class, +// but with the difference that the +// return value is tensor-valued +// rather than scalar of +// vector-valued. #include #include - // This is C++, as we want to write - // some output to disk: +// This is C++, as we want to write +// some output to disk: #include #include - // The last step is as in previous - // programs: +// The last step is as in previous +// programs: namespace Step9 { using namespace dealii; - // @sect3{AdvectionProblem class declaration} + // @sect3{AdvectionProblem class declaration} - // Following we declare the main - // class of this program. It is very - // much alike the main classes of - // previous examples, so we again - // only comment on the differences. + // Following we declare the main + // class of this program. It is very + // much alike the main classes of + // previous examples, so we again + // only comment on the differences. template class AdvectionProblem { - public: - AdvectionProblem (); - ~AdvectionProblem (); - void run (); - - private: - void setup_system (); - // The next function will be used - // to assemble the - // matrix. However, unlike in the - // previous examples, the - // function will not do the work - // itself, but rather it will - // split the range of active - // cells into several chunks and - // then call the following - // function on each of these - // chunks. The rationale is that - // matrix assembly can be - // parallelized quite well, as - // the computation of the local - // contributions on each cell is - // entirely independent of other - // cells, and we only have to - // synchronize when we add the - // contribution of a cell to the - // global matrix. The second - // function, doing the actual - // work, accepts two parameters - // which denote the first cell on - // which it shall operate, and - // the one past the last. - // - // The strategy for parallelization we - // choose here is one of the - // possibilities mentioned in detail in - // the @ref threads module in the - // documentation. While it is a - // straightforward way to distribute the - // work for assembling the system onto - // multiple processor cores. As mentioned - // in the module, there are other, and - // possibly better suited, ways to - // achieve the same goal. - void assemble_system (); - void assemble_system_interval (const typename DoFHandler::active_cell_iterator &begin, - const typename DoFHandler::active_cell_iterator &end); - - // The following functions again - // are as in previous examples, - // as are the subsequent - // variables. - void solve (); - void refine_grid (); - void output_results (const unsigned int cycle) const; - - Triangulation triangulation; - DoFHandler dof_handler; - - FE_Q fe; - - ConstraintMatrix hanging_node_constraints; - - SparsityPattern sparsity_pattern; - SparseMatrix system_matrix; - - Vector solution; - Vector system_rhs; - - // When assembling the matrix in - // parallel, we have to - // synchronize when several - // threads attempt to write the - // local contributions of a cell - // to the global matrix at the - // same time. This is done using - // a Mutex, which is an - // object that can be owned by - // only one thread at a time. If - // a thread wants to write to the - // matrix, it has to acquire this - // lock (if it is presently owned - // by another thread, then it has - // to wait), then write to the - // matrix and finally release the - // lock. Note that if the library - // was not compiled to support - // multithreading (which you have - // to specify at the time you - // call the ./configure - // script in the top-level - // directory), then a dummy the - // actual data type of the - // typedef - // Threads::ThreadMutex is a - // class that provides all the - // functions needed for a mutex, - // but does nothing when they are - // called; this is reasonable, of - // course, since if only one - // thread is running at a time, - // there is no need to - // synchronize with other - // threads. - Threads::ThreadMutex assembler_lock; + public: + AdvectionProblem (); + ~AdvectionProblem (); + void run (); + + private: + void setup_system (); + // The next function will be used + // to assemble the + // matrix. However, unlike in the + // previous examples, the + // function will not do the work + // itself, but rather it will + // split the range of active + // cells into several chunks and + // then call the following + // function on each of these + // chunks. The rationale is that + // matrix assembly can be + // parallelized quite well, as + // the computation of the local + // contributions on each cell is + // entirely independent of other + // cells, and we only have to + // synchronize when we add the + // contribution of a cell to the + // global matrix. The second + // function, doing the actual + // work, accepts two parameters + // which denote the first cell on + // which it shall operate, and + // the one past the last. + // + // The strategy for parallelization we + // choose here is one of the + // possibilities mentioned in detail in + // the @ref threads module in the + // documentation. While it is a + // straightforward way to distribute the + // work for assembling the system onto + // multiple processor cores. As mentioned + // in the module, there are other, and + // possibly better suited, ways to + // achieve the same goal. + void assemble_system (); + void assemble_system_interval (const typename DoFHandler::active_cell_iterator &begin, + const typename DoFHandler::active_cell_iterator &end); + + // The following functions again + // are as in previous examples, + // as are the subsequent + // variables. + void solve (); + void refine_grid (); + void output_results (const unsigned int cycle) const; + + Triangulation triangulation; + DoFHandler dof_handler; + + FE_Q fe; + + ConstraintMatrix hanging_node_constraints; + + SparsityPattern sparsity_pattern; + SparseMatrix system_matrix; + + Vector solution; + Vector system_rhs; + + // When assembling the matrix in + // parallel, we have to + // synchronize when several + // threads attempt to write the + // local contributions of a cell + // to the global matrix at the + // same time. This is done using + // a Mutex, which is an + // object that can be owned by + // only one thread at a time. If + // a thread wants to write to the + // matrix, it has to acquire this + // lock (if it is presently owned + // by another thread, then it has + // to wait), then write to the + // matrix and finally release the + // lock. Note that if the library + // was not compiled to support + // multithreading (which you have + // to specify at the time you + // call the ./configure + // script in the top-level + // directory), then a dummy the + // actual data type of the + // typedef + // Threads::ThreadMutex is a + // class that provides all the + // functions needed for a mutex, + // but does nothing when they are + // called; this is reasonable, of + // course, since if only one + // thread is running at a time, + // there is no need to + // synchronize with other + // threads. + Threads::ThreadMutex assembler_lock; }; - // @sect3{Equation data declaration} - - // Next we declare a class that - // describes the advection - // field. This, of course, is a - // vector field with as many compents - // as there are space dimensions. One - // could now use a class derived from - // the Function base class, as we - // have done for boundary values and - // coefficients in previous examples, - // but there is another possibility - // in the library, namely a base - // class that describes tensor valued - // functions. In contrast to the - // usual Function objects, we - // provide the compiler with - // knowledge on the size of the - // objects of the return type. This - // enables the compiler to generate - // efficient code, which is not so - // simple for usual vector-valued - // functions where memory has to be - // allocated on the heap (thus, the - // Function::vector_value - // function has to be given the - // address of an object into which - // the result is to be written, in - // order to avoid copying and memory - // allocation and deallocation on the - // heap). In addition to the known - // size, it is possible not only to - // return vectors, but also tensors - // of higher rank; however, this is - // not very often requested by - // applications, to be honest... - // - // The interface of the - // TensorFunction class is - // relatively close to that of the - // Function class, so there is - // probably no need to comment in - // detail the following declaration: + // @sect3{Equation data declaration} + + // Next we declare a class that + // describes the advection + // field. This, of course, is a + // vector field with as many compents + // as there are space dimensions. One + // could now use a class derived from + // the Function base class, as we + // have done for boundary values and + // coefficients in previous examples, + // but there is another possibility + // in the library, namely a base + // class that describes tensor valued + // functions. In contrast to the + // usual Function objects, we + // provide the compiler with + // knowledge on the size of the + // objects of the return type. This + // enables the compiler to generate + // efficient code, which is not so + // simple for usual vector-valued + // functions where memory has to be + // allocated on the heap (thus, the + // Function::vector_value + // function has to be given the + // address of an object into which + // the result is to be written, in + // order to avoid copying and memory + // allocation and deallocation on the + // heap). In addition to the known + // size, it is possible not only to + // return vectors, but also tensors + // of higher rank; however, this is + // not very often requested by + // applications, to be honest... + // + // The interface of the + // TensorFunction class is + // relatively close to that of the + // Function class, so there is + // probably no need to comment in + // detail the following declaration: template class AdvectionField : public TensorFunction<1,dim> { - public: - AdvectionField () : TensorFunction<1,dim> () {} - - virtual Tensor<1,dim> value (const Point &p) const; - - virtual void value_list (const std::vector > &points, - std::vector > &values) const; - - // In previous examples, we have - // used assertions that throw - // exceptions in several - // places. However, we have never - // seen how such exceptions are - // declared. This can be done as - // follows: - DeclException2 (ExcDimensionMismatch, - unsigned int, unsigned int, - << "The vector has size " << arg1 << " but should have " - << arg2 << " elements."); - // The syntax may look a little - // strange, but is - // reasonable. The format is - // basically as follows: use the - // name of one of the macros - // DeclExceptionN, where - // N denotes the number of - // additional parameters which - // the exception object shall - // take. In this case, as we want - // to throw the exception when - // the sizes of two vectors - // differ, we need two arguments, - // so we use - // DeclException2. The first - // parameter then describes the - // name of the exception, while - // the following declare the data - // types of the parameters. The - // last argument is a sequence of - // output directives that will be - // piped into the std::cerr - // object, thus the strange - // format with the leading @<@< - // operator and the like. Note - // that we can access the - // parameters which are passed to - // the exception upon - // construction (i.e. within the - // Assert call) by using the - // names arg1 through - // argN, where N is the - // number of arguments as defined - // by the use of the respective - // macro DeclExceptionN. - // - // To learn how the preprocessor - // expands this macro into actual - // code, please refer to the - // documentation of the exception - // classes in the base - // library. Suffice it to say - // that by this macro call, the - // respective exception class is - // declared, which also has error - // output functions already - // implemented. + public: + AdvectionField () : TensorFunction<1,dim> () {} + + virtual Tensor<1,dim> value (const Point &p) const; + + virtual void value_list (const std::vector > &points, + std::vector > &values) const; + + // In previous examples, we have + // used assertions that throw + // exceptions in several + // places. However, we have never + // seen how such exceptions are + // declared. This can be done as + // follows: + DeclException2 (ExcDimensionMismatch, + unsigned int, unsigned int, + << "The vector has size " << arg1 << " but should have " + << arg2 << " elements."); + // The syntax may look a little + // strange, but is + // reasonable. The format is + // basically as follows: use the + // name of one of the macros + // DeclExceptionN, where + // N denotes the number of + // additional parameters which + // the exception object shall + // take. In this case, as we want + // to throw the exception when + // the sizes of two vectors + // differ, we need two arguments, + // so we use + // DeclException2. The first + // parameter then describes the + // name of the exception, while + // the following declare the data + // types of the parameters. The + // last argument is a sequence of + // output directives that will be + // piped into the std::cerr + // object, thus the strange + // format with the leading @<@< + // operator and the like. Note + // that we can access the + // parameters which are passed to + // the exception upon + // construction (i.e. within the + // Assert call) by using the + // names arg1 through + // argN, where N is the + // number of arguments as defined + // by the use of the respective + // macro DeclExceptionN. + // + // To learn how the preprocessor + // expands this macro into actual + // code, please refer to the + // documentation of the exception + // classes in the base + // library. Suffice it to say + // that by this macro call, the + // respective exception class is + // declared, which also has error + // output functions already + // implemented. }; - // The following two functions - // implement the interface described - // above. The first simply implements - // the function as described in the - // introduction, while the second - // uses the same trick to avoid - // calling a virtual function as has - // already been introduced in the - // previous example program. Note the - // check for the right sizes of the - // arguments in the second function, - // which should always be present in - // such functions; it is our - // experience that many if not most - // programming errors result from - // incorrectly initialized arrays, - // incompatible parameters to - // functions and the like; using - // assertion as in this case can - // eliminate many of these problems. + // The following two functions + // implement the interface described + // above. The first simply implements + // the function as described in the + // introduction, while the second + // uses the same trick to avoid + // calling a virtual function as has + // already been introduced in the + // previous example program. Note the + // check for the right sizes of the + // arguments in the second function, + // which should always be present in + // such functions; it is our + // experience that many if not most + // programming errors result from + // incorrectly initialized arrays, + // incompatible parameters to + // functions and the like; using + // assertion as in this case can + // eliminate many of these problems. template Tensor<1,dim> AdvectionField::value (const Point &p) const @@ -363,41 +363,41 @@ namespace Step9 - // Besides the advection field, we - // need two functions describing the - // source terms (right hand side) - // and the boundary values. First for - // the right hand side, which follows - // the same pattern as in previous - // examples. As described in the - // introduction, the source is a - // constant function in the vicinity - // of a source point, which we denote - // by the constant static variable - // center_point. We set the - // values of this center using the - // same template tricks as we have - // shown in the step-7 example - // program. The rest is simple and - // has been shown previously, - // including the way to avoid virtual - // function calls in the - // value_list function. + // Besides the advection field, we + // need two functions describing the + // source terms (right hand side) + // and the boundary values. First for + // the right hand side, which follows + // the same pattern as in previous + // examples. As described in the + // introduction, the source is a + // constant function in the vicinity + // of a source point, which we denote + // by the constant static variable + // center_point. We set the + // values of this center using the + // same template tricks as we have + // shown in the step-7 example + // program. The rest is simple and + // has been shown previously, + // including the way to avoid virtual + // function calls in the + // value_list function. template class RightHandSide : public Function { - public: - RightHandSide () : Function() {} + public: + RightHandSide () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; - private: - static const Point center_point; + private: + static const Point center_point; }; @@ -412,26 +412,26 @@ namespace Step9 - // The only new thing here is that we - // check for the value of the - // component parameter. As this - // is a scalar function, it is - // obvious that it only makes sense - // if the desired component has the - // index zero, so we assert that this - // is indeed the - // case. ExcIndexRange is a - // global predefined exception - // (probably the one most often used, - // we therefore made it global - // instead of local to some class), - // that takes three parameters: the - // index that is outside the allowed - // range, the first element of the - // valid range and the one past the - // last (i.e. again the half-open - // interval so often used in the C++ - // standard library): + // The only new thing here is that we + // check for the value of the + // component parameter. As this + // is a scalar function, it is + // obvious that it only makes sense + // if the desired component has the + // index zero, so we assert that this + // is indeed the + // case. ExcIndexRange is a + // global predefined exception + // (probably the one most often used, + // we therefore made it global + // instead of local to some class), + // that takes three parameters: the + // index that is outside the allowed + // range, the first element of the + // valid range and the one past the + // last (i.e. again the half-open + // interval so often used in the C++ + // standard library): template double RightHandSide::value (const Point &p, @@ -461,22 +461,22 @@ namespace Step9 - // Finally for the boundary values, - // which is just another class - // derived from the Function base - // class: + // Finally for the boundary values, + // which is just another class + // derived from the Function base + // class: template class BoundaryValues : public Function { - public: - BoundaryValues () : Function() {} + public: + BoundaryValues () : Function() {} - virtual double value (const Point &p, - const unsigned int component = 0) const; + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; @@ -510,147 +510,147 @@ namespace Step9 - // @sect3{GradientEstimation class declaration} - - // Now, finally, here comes the class - // that will compute the difference - // approximation of the gradient on - // each cell and weighs that with a - // power of the mesh size, as - // described in the introduction. - // This class is a simple version of - // the DerivativeApproximation - // class in the library, that uses - // similar techniques to obtain - // finite difference approximations - // of the gradient of a finite - // element field, or if higher - // derivatives. - // - // The - // class has one public static - // function estimate that is - // called to compute a vector of - // error indicators, and one private - // function that does the actual work - // on an interval of all active - // cells. The latter is called by the - // first one in order to be able to - // do the computations in parallel if - // your computer has more than one - // processor. While the first - // function accepts as parameter a - // vector into which the error - // indicator is written for each - // cell. This vector is passed on to - // the second function that actually - // computes the error indicators on - // some cells, and the respective - // elements of the vector are - // written. By the way, we made it - // somewhat of a convention to use - // vectors of floats for error - // indicators rather than the common - // vectors of doubles, as the - // additional accuracy is not - // necessary for estimated values. - // - // In addition to these two - // functions, the class declares to - // exceptions which are raised when a - // cell has no neighbors in each of - // the space directions (in which - // case the matrix described in the - // introduction would be singular and - // can't be inverted), while the - // other one is used in the more - // common case of invalid parameters - // to a function, namely a vector of - // wrong size. - // - // Two annotations to this class are - // still in order: the first is that - // the class has no non-static member - // functions or variables, so this is - // not really a class, but rather - // serves the purpose of a - // namespace in C++. The reason - // that we chose a class over a - // namespace is that this way we can - // declare functions that are - // private, i.e. visible to the - // outside world but not - // callable. This can be done with - // namespaces as well, if one - // declares some functions in header - // files in the namespace and - // implements these and other - // functions in the implementation - // file. The functions not declared - // in the header file are still in - // the namespace but are not callable - // from outside. However, as we have - // only one file here, it is not - // possible to hide functions in the - // present case. - // - // The second is that the dimension - // template parameter is attached to - // the function rather than to the - // class itself. This way, you don't - // have to specify the template - // parameter yourself as in most - // other cases, but the compiler can - // figure its value out itself from - // the dimension of the DoF handler - // object that one passes as first - // argument. - // - // Finally note that the - // IndexInterval typedef is - // introduced as a convenient - // abbreviation for an otherwise - // lengthy type name. + // @sect3{GradientEstimation class declaration} + + // Now, finally, here comes the class + // that will compute the difference + // approximation of the gradient on + // each cell and weighs that with a + // power of the mesh size, as + // described in the introduction. + // This class is a simple version of + // the DerivativeApproximation + // class in the library, that uses + // similar techniques to obtain + // finite difference approximations + // of the gradient of a finite + // element field, or if higher + // derivatives. + // + // The + // class has one public static + // function estimate that is + // called to compute a vector of + // error indicators, and one private + // function that does the actual work + // on an interval of all active + // cells. The latter is called by the + // first one in order to be able to + // do the computations in parallel if + // your computer has more than one + // processor. While the first + // function accepts as parameter a + // vector into which the error + // indicator is written for each + // cell. This vector is passed on to + // the second function that actually + // computes the error indicators on + // some cells, and the respective + // elements of the vector are + // written. By the way, we made it + // somewhat of a convention to use + // vectors of floats for error + // indicators rather than the common + // vectors of doubles, as the + // additional accuracy is not + // necessary for estimated values. + // + // In addition to these two + // functions, the class declares to + // exceptions which are raised when a + // cell has no neighbors in each of + // the space directions (in which + // case the matrix described in the + // introduction would be singular and + // can't be inverted), while the + // other one is used in the more + // common case of invalid parameters + // to a function, namely a vector of + // wrong size. + // + // Two annotations to this class are + // still in order: the first is that + // the class has no non-static member + // functions or variables, so this is + // not really a class, but rather + // serves the purpose of a + // namespace in C++. The reason + // that we chose a class over a + // namespace is that this way we can + // declare functions that are + // private, i.e. visible to the + // outside world but not + // callable. This can be done with + // namespaces as well, if one + // declares some functions in header + // files in the namespace and + // implements these and other + // functions in the implementation + // file. The functions not declared + // in the header file are still in + // the namespace but are not callable + // from outside. However, as we have + // only one file here, it is not + // possible to hide functions in the + // present case. + // + // The second is that the dimension + // template parameter is attached to + // the function rather than to the + // class itself. This way, you don't + // have to specify the template + // parameter yourself as in most + // other cases, but the compiler can + // figure its value out itself from + // the dimension of the DoF handler + // object that one passes as first + // argument. + // + // Finally note that the + // IndexInterval typedef is + // introduced as a convenient + // abbreviation for an otherwise + // lengthy type name. class GradientEstimation { - public: - template - static void estimate (const DoFHandler &dof, - const Vector &solution, - Vector &error_per_cell); - - DeclException2 (ExcInvalidVectorLength, - int, int, - << "Vector has length " << arg1 << ", but should have " - << arg2); - DeclException0 (ExcInsufficientDirections); - - private: - typedef std::pair IndexInterval; - - template - static void estimate_interval (const DoFHandler &dof, - const Vector &solution, - const IndexInterval &index_interval, - Vector &error_per_cell); + public: + template + static void estimate (const DoFHandler &dof, + const Vector &solution, + Vector &error_per_cell); + + DeclException2 (ExcInvalidVectorLength, + int, int, + << "Vector has length " << arg1 << ", but should have " + << arg2); + DeclException0 (ExcInsufficientDirections); + + private: + typedef std::pair IndexInterval; + + template + static void estimate_interval (const DoFHandler &dof, + const Vector &solution, + const IndexInterval &index_interval, + Vector &error_per_cell); }; - // @sect3{AdvectionProblem class implementation} + // @sect3{AdvectionProblem class implementation} - // Now for the implementation of the - // main class. Constructor, - // destructor and the function - // setup_system follow the same - // pattern that was used previously, - // so we need not comment on these - // three function: + // Now for the implementation of the + // main class. Constructor, + // destructor and the function + // setup_system follow the same + // pattern that was used previously, + // so we need not comment on these + // three function: template AdvectionProblem::AdvectionProblem () : - dof_handler (triangulation), - fe(1) + dof_handler (triangulation), + fe(1) {} @@ -690,323 +690,323 @@ namespace Step9 - // In the following function, the - // matrix and right hand side are - // assembled. As stated in the - // documentation of the main class - // above, it does not do this itself, - // but rather delegates to the - // function following next, by - // splitting up the range of cells - // into chunks of approximately the - // same size and assembling on each - // of these chunks in parallel. + // In the following function, the + // matrix and right hand side are + // assembled. As stated in the + // documentation of the main class + // above, it does not do this itself, + // but rather delegates to the + // function following next, by + // splitting up the range of cells + // into chunks of approximately the + // same size and assembling on each + // of these chunks in parallel. template void AdvectionProblem::assemble_system () { - // First, we want to find out how - // many threads shall assemble the - // matrix in parallel. A reasonable - // choice would be that each - // processor in your system - // processes one chunk of cells; if - // we were to use this information, - // we could use the value of the - // global variable - // multithread_info.n_cpus, - // which is determined at start-up - // time of your program - // automatically. (Note that if the - // library was not configured for - // multi-threading, then the number - // of CPUs is set to one.) However, - // sometimes there might be reasons - // to use another value. For - // example, you might want to use - // less processors than there are - // in your system in order not to - // use too many computational - // ressources. On the other hand, - // if there are several jobs - // running on a computer and you - // want to get a higher percentage - // of CPU time, it might be worth - // to start more threads than there - // are CPUs, as most operating - // systems assign roughly the same - // CPU ressources to all threads - // presently running. For this - // reason, the MultithreadInfo - // class contains a read-write - // variable n_default_threads - // which is set to n_cpus by - // default, but can be set to - // another value. This variable is - // also queried by functions inside - // the library to determine how - // many threads they shall create. + // First, we want to find out how + // many threads shall assemble the + // matrix in parallel. A reasonable + // choice would be that each + // processor in your system + // processes one chunk of cells; if + // we were to use this information, + // we could use the value of the + // global variable + // multithread_info.n_cpus, + // which is determined at start-up + // time of your program + // automatically. (Note that if the + // library was not configured for + // multi-threading, then the number + // of CPUs is set to one.) However, + // sometimes there might be reasons + // to use another value. For + // example, you might want to use + // less processors than there are + // in your system in order not to + // use too many computational + // ressources. On the other hand, + // if there are several jobs + // running on a computer and you + // want to get a higher percentage + // of CPU time, it might be worth + // to start more threads than there + // are CPUs, as most operating + // systems assign roughly the same + // CPU ressources to all threads + // presently running. For this + // reason, the MultithreadInfo + // class contains a read-write + // variable n_default_threads + // which is set to n_cpus by + // default, but can be set to + // another value. This variable is + // also queried by functions inside + // the library to determine how + // many threads they shall create. const unsigned int n_threads = multithread_info.n_default_threads; - // It is worth noting, however, that this - // setup determines the load distribution - // onto processor in a static way: it does - // not take into account that some other - // part of our program may also be running - // something in parallel at the same time - // as we get here (this is not the case in - // the current program, but may easily be - // the case in more complex - // applications). A discussion of how to - // deal with this case can be found in the - // @ref threads module. - // - // Next, we need an object which is - // capable of keeping track of the - // threads we created, and allows - // us to wait until they all have - // finished (to join them in - // the language of threads). The - // Threads::ThreadGroup class - // does this, which is basically - // just a container for objects of - // type Threads::Thread that - // represent a single thread; - // Threads::Thread is what the - // Threads::new_thread function below will - // return when we start a new - // thread. - // - // Note that both Threads::ThreadGroup - // and Threads::Thread have a template - // argument that represents the - // return type of the function - // being called on a separate - // thread. Since most of the - // functions that we will call on - // different threads have return - // type void, the template - // argument has a default value - // void, so that in that case - // it can be omitted. (However, you - // still need to write the angle - // brackets, even if they are - // empty.) - // - // If you did not configure for - // multi-threading, then the - // new_thread function that is - // supposed to start a new thread - // in parallel only executes the - // function which should be run in - // parallel, waits for it to return - // (i.e. the function is executed - // sequentially), and puts the - // return value into the Thread - // object. Likewise, the function - // join that is supposed to - // wait for all spawned threads to - // return, returns immediately, as - // there can't be any threads running. + // It is worth noting, however, that this + // setup determines the load distribution + // onto processor in a static way: it does + // not take into account that some other + // part of our program may also be running + // something in parallel at the same time + // as we get here (this is not the case in + // the current program, but may easily be + // the case in more complex + // applications). A discussion of how to + // deal with this case can be found in the + // @ref threads module. + // + // Next, we need an object which is + // capable of keeping track of the + // threads we created, and allows + // us to wait until they all have + // finished (to join them in + // the language of threads). The + // Threads::ThreadGroup class + // does this, which is basically + // just a container for objects of + // type Threads::Thread that + // represent a single thread; + // Threads::Thread is what the + // Threads::new_thread function below will + // return when we start a new + // thread. + // + // Note that both Threads::ThreadGroup + // and Threads::Thread have a template + // argument that represents the + // return type of the function + // being called on a separate + // thread. Since most of the + // functions that we will call on + // different threads have return + // type void, the template + // argument has a default value + // void, so that in that case + // it can be omitted. (However, you + // still need to write the angle + // brackets, even if they are + // empty.) + // + // If you did not configure for + // multi-threading, then the + // new_thread function that is + // supposed to start a new thread + // in parallel only executes the + // function which should be run in + // parallel, waits for it to return + // (i.e. the function is executed + // sequentially), and puts the + // return value into the Thread + // object. Likewise, the function + // join that is supposed to + // wait for all spawned threads to + // return, returns immediately, as + // there can't be any threads running. Threads::ThreadGroup<> threads; - // Now we have to split the range - // of cells into chunks of - // approximately the same - // size. Each thread will then - // assemble the local contributions - // of the cells within its chunk - // and transfer these contributions - // to the global matrix. As - // splitting a range of cells is a - // rather common task when using - // multi-threading, there is a - // function in the Threads - // namespace that does exactly - // this. In fact, it does this not - // only for a range of cell - // iterators, but for iterators in - // general, so you could use it for - // std::vector::iterator or - // usual pointers as well. - // - // The function returns a vector of - // pairs of iterators, where the - // first denotes the first cell of - // each chunk, while the second - // denotes the one past the last - // (this half-open interval is the - // usual convention in the C++ - // standard library, so we keep to - // it). Note that we have to - // specify the actual data type of - // the iterators in angle brackets - // to the function. This is - // necessary, since it is a - // template function which takes - // the data type of the iterators - // as template argument; in the - // present case, however, the data - // types of the two first - // parameters differ - // (begin_active returns an - // active_iterator, while - // end returns a - // raw_iterator), and in this - // case the C++ language requires - // us to specify the template type - // explicitely. For brevity, we - // first typedef this data type to - // an alias. + // Now we have to split the range + // of cells into chunks of + // approximately the same + // size. Each thread will then + // assemble the local contributions + // of the cells within its chunk + // and transfer these contributions + // to the global matrix. As + // splitting a range of cells is a + // rather common task when using + // multi-threading, there is a + // function in the Threads + // namespace that does exactly + // this. In fact, it does this not + // only for a range of cell + // iterators, but for iterators in + // general, so you could use it for + // std::vector::iterator or + // usual pointers as well. + // + // The function returns a vector of + // pairs of iterators, where the + // first denotes the first cell of + // each chunk, while the second + // denotes the one past the last + // (this half-open interval is the + // usual convention in the C++ + // standard library, so we keep to + // it). Note that we have to + // specify the actual data type of + // the iterators in angle brackets + // to the function. This is + // necessary, since it is a + // template function which takes + // the data type of the iterators + // as template argument; in the + // present case, however, the data + // types of the two first + // parameters differ + // (begin_active returns an + // active_iterator, while + // end returns a + // raw_iterator), and in this + // case the C++ language requires + // us to specify the template type + // explicitely. For brevity, we + // first typedef this data type to + // an alias. typedef typename DoFHandler::active_cell_iterator active_cell_iterator; std::vector > - thread_ranges + thread_ranges = Threads::split_range (dof_handler.begin_active (), dof_handler.end (), n_threads); - // Finally, for each of the chunks - // of iterators we have computed, - // start one thread (or if not in - // multi-thread mode: execute - // assembly on these chunks - // sequentially). This is done - // using the following sequence of - // function calls: + // Finally, for each of the chunks + // of iterators we have computed, + // start one thread (or if not in + // multi-thread mode: execute + // assembly on these chunks + // sequentially). This is done + // using the following sequence of + // function calls: for (unsigned int thread=0; thread::assemble_system_interval, *this, thread_ranges[thread].first, thread_ranges[thread].second); - // The reasons and internal - // workings of these functions can - // be found in the report on the - // subject of multi-threading, - // which is available online as - // well. Suffice it to say that we - // create a new thread that calls - // the assemble_system_interval - // function on the present object - // (the this pointer), with the - // arguments following in the - // second set of parentheses passed - // as parameters. The Threads::new_thread - // function returns an object of - // type Threads::Thread, which - // we put into the threads - // container. If a thread exits, - // the return value of the function - // being called is put into a place - // such that the thread objects can - // access it using their - // return_value function; since - // the function we call doesn't - // have a return value, this does - // not apply here. Note that you - // can copy around thread objects - // freely, and that of course they - // will still represent the same - // thread. - - // When all the threads are - // running, the only thing we have - // to do is wait for them to - // finish. This is necessary of - // course, as we can't proceed with - // our tasks before the matrix and - // right hand side are - // assemblesd. Waiting for all the - // threads to finish can be done - // using the joint_all function - // in the ThreadGroup - // container, which just calls - // join on each of the thread - // objects it stores. - // - // Again, if the library was not - // configured to use - // multi-threading, then no threads - // can run in parallel and the - // function returns immediately. + // The reasons and internal + // workings of these functions can + // be found in the report on the + // subject of multi-threading, + // which is available online as + // well. Suffice it to say that we + // create a new thread that calls + // the assemble_system_interval + // function on the present object + // (the this pointer), with the + // arguments following in the + // second set of parentheses passed + // as parameters. The Threads::new_thread + // function returns an object of + // type Threads::Thread, which + // we put into the threads + // container. If a thread exits, + // the return value of the function + // being called is put into a place + // such that the thread objects can + // access it using their + // return_value function; since + // the function we call doesn't + // have a return value, this does + // not apply here. Note that you + // can copy around thread objects + // freely, and that of course they + // will still represent the same + // thread. + + // When all the threads are + // running, the only thing we have + // to do is wait for them to + // finish. This is necessary of + // course, as we can't proceed with + // our tasks before the matrix and + // right hand side are + // assemblesd. Waiting for all the + // threads to finish can be done + // using the joint_all function + // in the ThreadGroup + // container, which just calls + // join on each of the thread + // objects it stores. + // + // Again, if the library was not + // configured to use + // multi-threading, then no threads + // can run in parallel and the + // function returns immediately. threads.join_all (); - // After the matrix has been - // assembled in parallel, we stil - // have to eliminate hanging node - // constraints. This is something - // that can't be done on each of - // the threads separately, so we - // have to do it now. + // After the matrix has been + // assembled in parallel, we stil + // have to eliminate hanging node + // constraints. This is something + // that can't be done on each of + // the threads separately, so we + // have to do it now. hanging_node_constraints.condense (system_matrix); hanging_node_constraints.condense (system_rhs); - // Note also, that unlike in - // previous examples, there are no - // boundary conditions to be - // applied to the system of - // equations. This, of course, is - // due to the fact that we have - // included them into the weak - // formulation of the problem. + // Note also, that unlike in + // previous examples, there are no + // boundary conditions to be + // applied to the system of + // equations. This, of course, is + // due to the fact that we have + // included them into the weak + // formulation of the problem. } - // Now, this is the function that - // does the actual work. It is not - // very different from the - // assemble_system functions of - // previous example programs, so we - // will again only comment on the - // differences. The mathematical - // stuff follows closely what we have - // said in the introduction. + // Now, this is the function that + // does the actual work. It is not + // very different from the + // assemble_system functions of + // previous example programs, so we + // will again only comment on the + // differences. The mathematical + // stuff follows closely what we have + // said in the introduction. template void AdvectionProblem:: assemble_system_interval (const typename DoFHandler::active_cell_iterator &begin, const typename DoFHandler::active_cell_iterator &end) { - // First of all, we will need some - // objects that describe boundary - // values, right hand side function - // and the advection field. As we - // will only perform actions on - // these objects that do not change - // them, we declare them as - // constant, which can enable the - // compiler in some cases to - // perform additional - // optimizations. + // First of all, we will need some + // objects that describe boundary + // values, right hand side function + // and the advection field. As we + // will only perform actions on + // these objects that do not change + // them, we declare them as + // constant, which can enable the + // compiler in some cases to + // perform additional + // optimizations. const AdvectionField advection_field; const RightHandSide right_hand_side; const BoundaryValues boundary_values; - // Next we need quadrature formula - // for the cell terms, but also for - // the integral over the inflow - // boundary, which will be a face - // integral. As we use bilinear - // elements, Gauss formulae with - // two points in each space - // direction are sufficient. + // Next we need quadrature formula + // for the cell terms, but also for + // the integral over the inflow + // boundary, which will be a face + // integral. As we use bilinear + // elements, Gauss formulae with + // two points in each space + // direction are sufficient. QGauss quadrature_formula(2); QGauss face_quadrature_formula(2); - // Finally, we need objects of type - // FEValues and - // FEFaceValues. For the cell - // terms we need the values and - // gradients of the shape - // functions, the quadrature points - // in order to determine the source - // density and the advection field - // at a given point, and the - // weights of the quadrature points - // times the determinant of the - // Jacobian at these points. In - // contrast, for the boundary - // integrals, we don't need the - // gradients, but rather the normal - // vectors to the cells. + // Finally, we need objects of type + // FEValues and + // FEFaceValues. For the cell + // terms we need the values and + // gradients of the shape + // functions, the quadrature points + // in order to determine the source + // density and the advection field + // at a given point, and the + // weights of the quadrature points + // times the determinant of the + // Jacobian at these points. In + // contrast, for the boundary + // integrals, we don't need the + // gradients, but rather the normal + // vectors to the cells. FEValues fe_values (fe, quadrature_formula, update_values | update_gradients | update_quadrature_points | update_JxW_values); @@ -1014,68 +1014,68 @@ namespace Step9 update_values | update_quadrature_points | update_JxW_values | update_normal_vectors); - // Then we define some - // abbreviations to avoid - // unnecessarily long lines: + // Then we define some + // abbreviations to avoid + // unnecessarily long lines: const unsigned int dofs_per_cell = fe.dofs_per_cell; const unsigned int n_q_points = quadrature_formula.size(); const unsigned int n_face_q_points = face_quadrature_formula.size(); - // We declare cell matrix and cell - // right hand side... + // We declare cell matrix and cell + // right hand side... FullMatrix cell_matrix (dofs_per_cell, dofs_per_cell); Vector cell_rhs (dofs_per_cell); - // ... an array to hold the global - // indices of the degrees of - // freedom of the cell on which we - // are presently working... + // ... an array to hold the global + // indices of the degrees of + // freedom of the cell on which we + // are presently working... std::vector local_dof_indices (dofs_per_cell); - // ... and array in which the - // values of right hand side, - // advection direction, and - // boundary values will be stored, - // for cell and face integrals - // respectively: + // ... and array in which the + // values of right hand side, + // advection direction, and + // boundary values will be stored, + // for cell and face integrals + // respectively: std::vector rhs_values (n_q_points); std::vector > advection_directions (n_q_points); std::vector face_boundary_values (n_face_q_points); std::vector > face_advection_directions (n_face_q_points); - // Then we start the main loop over - // the cells: + // Then we start the main loop over + // the cells: typename DoFHandler::active_cell_iterator cell; for (cell=begin; cell!=end; ++cell) { - // First clear old contents of - // the cell contributions... + // First clear old contents of + // the cell contributions... cell_matrix = 0; cell_rhs = 0; - // ... then initialize - // the FEValues object... + // ... then initialize + // the FEValues object... fe_values.reinit (cell); - // ... obtain the values of - // right hand side and - // advection directions at the - // quadrature points... + // ... obtain the values of + // right hand side and + // advection directions at the + // quadrature points... advection_field.value_list (fe_values.get_quadrature_points(), advection_directions); right_hand_side.value_list (fe_values.get_quadrature_points(), rhs_values); - // ... set the value of the - // streamline diffusion - // parameter as described in - // the introduction... + // ... set the value of the + // streamline diffusion + // parameter as described in + // the introduction... const double delta = 0.1 * cell->diameter (); - // ... and assemble the local - // contributions to the system - // matrix and right hand side - // as also discussed above: + // ... and assemble the local + // contributions to the system + // matrix and right hand side + // as also discussed above: for (unsigned int q_point=0; q_pointinflow part of the - // boundary, but to find out - // whether a certain part of a - // face of the present cell is - // part of the inflow boundary, - // we have to have information - // on the exact location of the - // quadrature points and on the - // direction of flow at this - // point; we obtain this - // information using the - // FEFaceValues object and only - // decide within the main loop - // whether a quadrature point - // is on the inflow boundary. + // Besides the cell terms which + // we have build up now, the + // bilinear form of the present + // problem also contains terms + // on the boundary of the + // domain. Therefore, we have + // to check whether any of the + // faces of this cell are on + // the boundary of the domain, + // and if so assemble the + // contributions of this face + // as well. Of course, the + // bilinear form only contains + // contributions from the + // inflow part of the + // boundary, but to find out + // whether a certain part of a + // face of the present cell is + // part of the inflow boundary, + // we have to have information + // on the exact location of the + // quadrature points and on the + // direction of flow at this + // point; we obtain this + // information using the + // FEFaceValues object and only + // decide within the main loop + // whether a quadrature point + // is on the inflow boundary. for (unsigned int face=0; face::faces_per_cell; ++face) if (cell->face(face)->at_boundary()) { - // Ok, this face of the - // present cell is on the - // boundary of the - // domain. Just as for - // the usual FEValues - // object which we have - // used in previous - // examples and also - // above, we have to - // reinitialize the - // FEFaceValues object - // for the present face: + // Ok, this face of the + // present cell is on the + // boundary of the + // domain. Just as for + // the usual FEValues + // object which we have + // used in previous + // examples and also + // above, we have to + // reinitialize the + // FEFaceValues object + // for the present face: fe_face_values.reinit (cell, face); - // For the quadrature - // points at hand, we ask - // for the values of the - // inflow function and - // for the direction of - // flow: + // For the quadrature + // points at hand, we ask + // for the values of the + // inflow function and + // for the direction of + // flow: boundary_values.value_list (fe_face_values.get_quadrature_points(), face_boundary_values); advection_field.value_list (fe_face_values.get_quadrature_points(), face_advection_directions); - // Now loop over all - // quadrature points and - // see whether it is on - // the inflow or outflow - // part of the - // boundary. This is - // determined by a test - // whether the advection - // direction points - // inwards or outwards of - // the domain (note that - // the normal vector - // points outwards of the - // cell, and since the - // cell is at the - // boundary, the normal - // vector points outward - // of the domain, so if - // the advection - // direction points into - // the domain, its scalar - // product with the - // normal vector must be - // negative): + // Now loop over all + // quadrature points and + // see whether it is on + // the inflow or outflow + // part of the + // boundary. This is + // determined by a test + // whether the advection + // direction points + // inwards or outwards of + // the domain (note that + // the normal vector + // points outwards of the + // cell, and since the + // cell is at the + // boundary, the normal + // vector points outward + // of the domain, so if + // the advection + // direction points into + // the domain, its scalar + // product with the + // normal vector must be + // negative): for (unsigned int q_point=0; q_pointget_dof_indices (local_dof_indices); - // Up until now we have not - // taken care of the fact that - // this function might run more - // than once in parallel, as - // the operations above only - // work on variables that are - // local to this function, or - // if they are global (such as - // the information on the grid, - // the DoF handler, or the DoF - // numbers) they are only - // read. Thus, the different - // threads do not disturb each - // other. - // - // On the other hand, we would - // now like to write the local - // contributions to the global - // system of equations into the - // global objects. This needs - // some kind of - // synchronisation, as if we - // would not take care of the - // fact that multiple threads - // write into the matrix at the - // same time, we might be - // surprised that one threads - // reads data from the matrix - // that another thread is - // presently overwriting, or - // similar things. Thus, to - // make sure that only one - // thread operates on these - // objects at a time, we have - // to lock it. This is done - // using a Mutex, which is - // short for mutually - // exclusive: a thread that - // wants to write to the global - // objects acquires this lock, - // but has to wait if it is - // presently owned by another - // thread. If it has acquired - // the lock, it can be sure - // that no other thread is - // presently writing to the - // matrix, and can do so - // freely. When finished, we - // release the lock again so as - // to allow other threads to - // acquire it and write to the - // matrix. + // Up until now we have not + // taken care of the fact that + // this function might run more + // than once in parallel, as + // the operations above only + // work on variables that are + // local to this function, or + // if they are global (such as + // the information on the grid, + // the DoF handler, or the DoF + // numbers) they are only + // read. Thus, the different + // threads do not disturb each + // other. + // + // On the other hand, we would + // now like to write the local + // contributions to the global + // system of equations into the + // global objects. This needs + // some kind of + // synchronisation, as if we + // would not take care of the + // fact that multiple threads + // write into the matrix at the + // same time, we might be + // surprised that one threads + // reads data from the matrix + // that another thread is + // presently overwriting, or + // similar things. Thus, to + // make sure that only one + // thread operates on these + // objects at a time, we have + // to lock it. This is done + // using a Mutex, which is + // short for mutually + // exclusive: a thread that + // wants to write to the global + // objects acquires this lock, + // but has to wait if it is + // presently owned by another + // thread. If it has acquired + // the lock, it can be sure + // that no other thread is + // presently writing to the + // matrix, and can do so + // freely. When finished, we + // release the lock again so as + // to allow other threads to + // acquire it and write to the + // matrix. assembler_lock.acquire (); for (unsigned int i=0; ilock and release - // functions are no-ops, - // i.e. they return without - // doing anything. - // - // 2. In order to work - // properly, it is essential - // that all threads try to - // acquire the same lock. This, - // of course, can not be - // achieved if the lock is a - // local variable, as then each - // thread would acquire its own - // lock. Therefore, the lock - // variable is a member - // variable of the class; since - // all threads execute member - // functions of the same - // object, they have the same - // this pointer and - // therefore also operate on - // the same lock. + // At this point, the locked + // operations on the global + // matrix are done, i.e. other + // threads can now enter into + // the protected section by + // acquiring the lock. Two + // final notes are in place + // here, however: + // + // 1. If the library was not + // configured for + // multi-threading, then there + // can't be parallel threads + // and there is no need to + // synchronize. Thus, the + // lock and release + // functions are no-ops, + // i.e. they return without + // doing anything. + // + // 2. In order to work + // properly, it is essential + // that all threads try to + // acquire the same lock. This, + // of course, can not be + // achieved if the lock is a + // local variable, as then each + // thread would acquire its own + // lock. Therefore, the lock + // variable is a member + // variable of the class; since + // all threads execute member + // functions of the same + // object, they have the same + // this pointer and + // therefore also operate on + // the same lock. }; } - // Following is the function that - // solves the linear system of - // equations. As the system is no - // more symmetric positive definite - // as in all the previous examples, - // we can't use the Conjugate - // Gradients method anymore. Rather, - // we use a solver that is tailored - // to nonsymmetric systems like the - // one at hand, the BiCGStab - // method. As preconditioner, we use - // the Jacobi method. + // Following is the function that + // solves the linear system of + // equations. As the system is no + // more symmetric positive definite + // as in all the previous examples, + // we can't use the Conjugate + // Gradients method anymore. Rather, + // we use a solver that is tailored + // to nonsymmetric systems like the + // one at hand, the BiCGStab + // method. As preconditioner, we use + // the Jacobi method. template void AdvectionProblem::solve () { @@ -1357,16 +1357,16 @@ namespace Step9 } - // The following function refines the - // grid according to the quantity - // described in the introduction. The - // respective computations are made - // in the class - // GradientEstimation. The only - // difference to previous examples is - // that we refine a little more - // aggressively (0.5 instead of 0.3 - // of the number of cells). + // The following function refines the + // grid according to the quantity + // described in the introduction. The + // respective computations are made + // in the class + // GradientEstimation. The only + // difference to previous examples is + // that we refine a little more + // aggressively (0.5 instead of 0.3 + // of the number of cells). template void AdvectionProblem::refine_grid () { @@ -1385,9 +1385,9 @@ namespace Step9 - // Writing output to disk is done in - // the same way as in the previous - // examples... + // Writing output to disk is done in + // the same way as in the previous + // examples... template void AdvectionProblem::output_results (const unsigned int cycle) const { @@ -1403,8 +1403,8 @@ namespace Step9 } - // ... as is the main loop (setup -- - // solve -- refine) + // ... as is the main loop (setup -- + // solve -- refine) template void AdvectionProblem::run () { @@ -1449,82 +1449,82 @@ namespace Step9 - // @sect3{GradientEstimation class implementation} + // @sect3{GradientEstimation class implementation} - // Now for the implementation of the - // GradientEstimation class. The - // first function does not much - // except for delegating work to the - // other function: + // Now for the implementation of the + // GradientEstimation class. The + // first function does not much + // except for delegating work to the + // other function: template void GradientEstimation::estimate (const DoFHandler &dof_handler, const Vector &solution, Vector &error_per_cell) { - // Before starting with the work, - // we check that the vector into - // which the results are written, - // has the right size. It is a - // common error that such - // parameters have the wrong size, - // but the resulting damage by not - // catching these errors are very - // subtle as they are usually - // corruption of data somewhere in - // memory. Often, the problems - // emerging from this are not - // reproducible, and we found that - // it is well worth the effort to - // check for such things. + // Before starting with the work, + // we check that the vector into + // which the results are written, + // has the right size. It is a + // common error that such + // parameters have the wrong size, + // but the resulting damage by not + // catching these errors are very + // subtle as they are usually + // corruption of data somewhere in + // memory. Often, the problems + // emerging from this are not + // reproducible, and we found that + // it is well worth the effort to + // check for such things. Assert (error_per_cell.size() == dof_handler.get_tria().n_active_cells(), ExcInvalidVectorLength (error_per_cell.size(), dof_handler.get_tria().n_active_cells())); - // Next, we subdivide the range of - // cells into chunks of equal - // size. Just as we have used the - // function - // Threads::split_range when - // assembling above, there is a - // function that computes intervals - // of roughly equal size from a - // larger interval. This is used - // here: + // Next, we subdivide the range of + // cells into chunks of equal + // size. Just as we have used the + // function + // Threads::split_range when + // assembling above, there is a + // function that computes intervals + // of roughly equal size from a + // larger interval. This is used + // here: const unsigned int n_threads = multithread_info.n_default_threads; std::vector index_intervals = Threads::split_interval (0, dof_handler.get_tria().n_active_cells(), n_threads); - // In the same way as before, we use a - // Threads::ThreadGroup object - // to collect the descriptor objects of - // different threads. Note that as the - // function called is not a member - // function, but rather a static function, - // we need not (and can not) pass a - // this pointer to the - // new_thread function in this - // case. - // - // Taking pointers to templated - // functions seems to be - // notoriously difficult for many - // compilers (since there are - // several functions with the same - // name -- just as with overloaded - // functions). It therefore happens - // quite frequently that we can't - // directly insert taking the - // address of a function in the - // call to encapsulate for one - // or the other compiler, but have - // to take a temporary variable for - // that purpose. Here, in this - // case, Compaq's cxx compiler - // choked on the code so we use - // this workaround with the - // function pointer: + // In the same way as before, we use a + // Threads::ThreadGroup object + // to collect the descriptor objects of + // different threads. Note that as the + // function called is not a member + // function, but rather a static function, + // we need not (and can not) pass a + // this pointer to the + // new_thread function in this + // case. + // + // Taking pointers to templated + // functions seems to be + // notoriously difficult for many + // compilers (since there are + // several functions with the same + // name -- just as with overloaded + // functions). It therefore happens + // quite frequently that we can't + // directly insert taking the + // address of a function in the + // call to encapsulate for one + // or the other compiler, but have + // to take a temporary variable for + // that purpose. Here, in this + // case, Compaq's cxx compiler + // choked on the code so we use + // this workaround with the + // function pointer: Threads::ThreadGroup<> threads; void (*estimate_interval_ptr) (const DoFHandler &, const Vector &, @@ -1536,57 +1536,57 @@ namespace Step9 dof_handler, solution, index_intervals[i], error_per_cell); - // Ok, now the threads are at work, - // and we only have to wait for - // them to finish their work: + // Ok, now the threads are at work, + // and we only have to wait for + // them to finish their work: threads.join_all (); - // Note that if the value of the - // variable - // multithread_info.n_default_threads - // was one, or if the library was - // not configured to use threads, - // then the sequence of commands - // above reduced to a complicated - // way to simply call the - // estimate_interval function - // with the whole range of cells to - // work on. However, using the way - // above, we are able to write the - // program such that it makes no - // difference whether we presently - // work with multiple threads or in - // single-threaded mode, thus - // eliminating the need to write - // code included in conditional - // preprocessor sections. + // Note that if the value of the + // variable + // multithread_info.n_default_threads + // was one, or if the library was + // not configured to use threads, + // then the sequence of commands + // above reduced to a complicated + // way to simply call the + // estimate_interval function + // with the whole range of cells to + // work on. However, using the way + // above, we are able to write the + // program such that it makes no + // difference whether we presently + // work with multiple threads or in + // single-threaded mode, thus + // eliminating the need to write + // code included in conditional + // preprocessor sections. } - // Following now the function that - // actually computes the finite - // difference approximation to the - // gradient. The general outline of - // the function is to loop over all - // the cells in the range of - // iterators designated by the third - // argument, and on each cell first - // compute the list of active - // neighbors of the present cell and - // then compute the quantities - // described in the introduction for - // each of the neighbors. The reason - // for this order is that it is not a - // one-liner to find a given neighbor - // with locally refined meshes. In - // principle, an optimized - // implementation would find - // neighbors and the quantities - // depending on them in one step, - // rather than first building a list - // of neighbors and in a second step - // their contributions. - // - // Now for the details: + // Following now the function that + // actually computes the finite + // difference approximation to the + // gradient. The general outline of + // the function is to loop over all + // the cells in the range of + // iterators designated by the third + // argument, and on each cell first + // compute the list of active + // neighbors of the present cell and + // then compute the quantities + // described in the introduction for + // each of the neighbors. The reason + // for this order is that it is not a + // one-liner to find a given neighbor + // with locally refined meshes. In + // principle, an optimized + // implementation would find + // neighbors and the quantities + // depending on them in one step, + // rather than first building a list + // of neighbors and in a second step + // their contributions. + // + // Now for the details: template void GradientEstimation::estimate_interval (const DoFHandler &dof_handler, @@ -1594,51 +1594,51 @@ namespace Step9 const IndexInterval &index_interval, Vector &error_per_cell) { - // First we need a way to extract - // the values of the given finite - // element function at the center - // of the cells. As usual with - // values of finite element - // functions, we use an object of - // type FEValues, and we use - // (or mis-use in this case) the - // midpoint quadrature rule to get - // at the values at the - // center. Note that the - // FEValues object only needs - // to compute the values at the - // centers, and the location of the - // quadrature points in real space - // in order to get at the vectors - // y. + // First we need a way to extract + // the values of the given finite + // element function at the center + // of the cells. As usual with + // values of finite element + // functions, we use an object of + // type FEValues, and we use + // (or mis-use in this case) the + // midpoint quadrature rule to get + // at the values at the + // center. Note that the + // FEValues object only needs + // to compute the values at the + // centers, and the location of the + // quadrature points in real space + // in order to get at the vectors + // y. QMidpoint midpoint_rule; FEValues fe_midpoint_value (dof_handler.get_fe(), midpoint_rule, update_values | update_quadrature_points); - // Then we need space foe the - // tensor Y, which is the sum - // of outer products of the - // y-vectors. + // Then we need space foe the + // tensor Y, which is the sum + // of outer products of the + // y-vectors. Tensor<2,dim> Y; - // Then define iterators into the - // cells and into the output - // vector, which are to be looped - // over by the present instance of - // this function. We get start and - // end iterators over cells by - // setting them to the first active - // cell and advancing them using - // the given start and end - // index. Note that we can use the - // advance function of the - // standard C++ library, but that - // we have to cast the distance by - // which the iterator is to be - // moved forward to a signed - // quantity in order to avoid - // warnings by the compiler. + // Then define iterators into the + // cells and into the output + // vector, which are to be looped + // over by the present instance of + // this function. We get start and + // end iterators over cells by + // setting them to the first active + // cell and advancing them using + // the given start and end + // index. Note that we can use the + // advance function of the + // standard C++ library, but that + // we have to cast the distance by + // which the iterator is to be + // moved forward to a signed + // quantity in order to avoid + // warnings by the compiler. typename DoFHandler::active_cell_iterator cell, endc; cell = dof_handler.begin_active(); @@ -1647,415 +1647,415 @@ namespace Step9 endc = dof_handler.begin_active(); advance (endc, static_cast(index_interval.second)); - // Getting an iterator into the - // output array is simpler. We - // don't need an end iterator, as - // we always move this iterator - // forward by one element for each - // cell we are on, but stop the - // loop when we hit the end cell, - // so we need not have an end - // element for this iterator. + // Getting an iterator into the + // output array is simpler. We + // don't need an end iterator, as + // we always move this iterator + // forward by one element for each + // cell we are on, but stop the + // loop when we hit the end cell, + // so we need not have an end + // element for this iterator. Vector::iterator - error_on_this_cell = error_per_cell.begin() + index_interval.first; + error_on_this_cell = error_per_cell.begin() + index_interval.first; - // Then we allocate a vector to - // hold iterators to all active - // neighbors of a cell. We reserve - // the maximal number of active - // neighbors in order to avoid - // later reallocations. Note how - // this maximal number of active - // neighbors is computed here. + // Then we allocate a vector to + // hold iterators to all active + // neighbors of a cell. We reserve + // the maximal number of active + // neighbors in order to avoid + // later reallocations. Note how + // this maximal number of active + // neighbors is computed here. std::vector::active_cell_iterator> active_neighbors; active_neighbors.reserve (GeometryInfo::faces_per_cell * GeometryInfo::max_children_per_face); - // Well then, after all these - // preliminaries, lets start the - // computations: + // Well then, after all these + // preliminaries, lets start the + // computations: for (; cell!=endc; ++cell, ++error_on_this_cell) { - // First initialize the - // FEValues object, as well - // as the Y tensor: + // First initialize the + // FEValues object, as well + // as the Y tensor: fe_midpoint_value.reinit (cell); Y.clear (); - // Then allocate the vector - // that will be the sum over - // the y-vectors times the - // approximate directional - // derivative: + // Then allocate the vector + // that will be the sum over + // the y-vectors times the + // approximate directional + // derivative: Tensor<1,dim> projected_gradient; - // Now before going on first - // compute a list of all active - // neighbors of the present - // cell. We do so by first - // looping over all faces and - // see whether the neighbor - // there is active, which would - // be the case if it is on the - // same level as the present - // cell or one level coarser - // (note that a neighbor can - // only be once coarser than - // the present cell, as we only - // allow a maximal difference - // of one refinement over a - // face in - // deal.II). Alternatively, the - // neighbor could be on the - // same level and be further - // refined; then we have to - // find which of its children - // are next to the present cell - // and select these (note that - // if a child of of neighbor of - // an active cell that is next - // to this active cell, needs - // necessarily be active - // itself, due to the - // one-refinement rule cited - // above). - // - // Things are slightly - // different in one space - // dimension, as there the - // one-refinement rule does not - // exist: neighboring active - // cells may differ in as many - // refinement levels as they - // like. In this case, the - // computation becomes a little - // more difficult, but we will - // explain this below. - // - // Before starting the loop - // over all neighbors of the - // present cell, we have to - // clear the array storing the - // iterators to the active - // neighbors, of course. + // Now before going on first + // compute a list of all active + // neighbors of the present + // cell. We do so by first + // looping over all faces and + // see whether the neighbor + // there is active, which would + // be the case if it is on the + // same level as the present + // cell or one level coarser + // (note that a neighbor can + // only be once coarser than + // the present cell, as we only + // allow a maximal difference + // of one refinement over a + // face in + // deal.II). Alternatively, the + // neighbor could be on the + // same level and be further + // refined; then we have to + // find which of its children + // are next to the present cell + // and select these (note that + // if a child of of neighbor of + // an active cell that is next + // to this active cell, needs + // necessarily be active + // itself, due to the + // one-refinement rule cited + // above). + // + // Things are slightly + // different in one space + // dimension, as there the + // one-refinement rule does not + // exist: neighboring active + // cells may differ in as many + // refinement levels as they + // like. In this case, the + // computation becomes a little + // more difficult, but we will + // explain this below. + // + // Before starting the loop + // over all neighbors of the + // present cell, we have to + // clear the array storing the + // iterators to the active + // neighbors, of course. active_neighbors.clear (); for (unsigned int face_no=0; face_no::faces_per_cell; ++face_no) if (! cell->at_boundary(face_no)) { - // First define an - // abbreviation for the - // iterator to the face - // and the neighbor + // First define an + // abbreviation for the + // iterator to the face + // and the neighbor const typename DoFHandler::face_iterator - face = cell->face(face_no); + face = cell->face(face_no); const typename DoFHandler::cell_iterator - neighbor = cell->neighbor(face_no); - - // Then check whether the - // neighbor is active. If - // it is, then it is on - // the same level or one - // level coarser (if we - // are not in 1D), and we - // are interested in it - // in any case. + neighbor = cell->neighbor(face_no); + + // Then check whether the + // neighbor is active. If + // it is, then it is on + // the same level or one + // level coarser (if we + // are not in 1D), and we + // are interested in it + // in any case. if (neighbor->active()) active_neighbors.push_back (neighbor); else { - // If the neighbor is - // not active, then - // check its - // children. + // If the neighbor is + // not active, then + // check its + // children. if (dim == 1) { - // To find the - // child of the - // neighbor which - // bounds to the - // present cell, - // successively - // go to its - // right child if - // we are left of - // the present - // cell (n==0), - // or go to the - // left child if - // we are on the - // right (n==1), - // until we find - // an active - // cell. + // To find the + // child of the + // neighbor which + // bounds to the + // present cell, + // successively + // go to its + // right child if + // we are left of + // the present + // cell (n==0), + // or go to the + // left child if + // we are on the + // right (n==1), + // until we find + // an active + // cell. typename DoFHandler::cell_iterator - neighbor_child = neighbor; + neighbor_child = neighbor; while (neighbor_child->has_children()) neighbor_child = neighbor_child->child (face_no==0 ? 1 : 0); - // As this used - // some - // non-trivial - // geometrical - // intuition, we - // might want to - // check whether - // we did it - // right, - // i.e. check - // whether the - // neighbor of - // the cell we - // found is - // indeed the - // cell we are - // presently - // working - // on. Checks - // like this are - // often useful - // and have - // frequently - // uncovered - // errors both in - // algorithms - // like the line - // above (where - // it is simple - // to - // involuntarily - // exchange - // n==1 for - // n==0 or - // the like) and - // in the library - // (the - // assumptions - // underlying the - // algorithm - // above could - // either be - // wrong, wrongly - // documented, or - // are violated - // due to an - // error in the - // library). One - // could in - // principle - // remove such - // checks after - // the program - // works for some - // time, but it - // might be a - // good things to - // leave it in - // anyway to - // check for - // changes in the - // library or in - // the algorithm - // above. - // - // Note that if - // this check - // fails, then - // this is - // certainly an - // error that is - // irrecoverable - // and probably - // qualifies as - // an internal - // error. We - // therefore use - // a predefined - // exception - // class to throw - // here. + // As this used + // some + // non-trivial + // geometrical + // intuition, we + // might want to + // check whether + // we did it + // right, + // i.e. check + // whether the + // neighbor of + // the cell we + // found is + // indeed the + // cell we are + // presently + // working + // on. Checks + // like this are + // often useful + // and have + // frequently + // uncovered + // errors both in + // algorithms + // like the line + // above (where + // it is simple + // to + // involuntarily + // exchange + // n==1 for + // n==0 or + // the like) and + // in the library + // (the + // assumptions + // underlying the + // algorithm + // above could + // either be + // wrong, wrongly + // documented, or + // are violated + // due to an + // error in the + // library). One + // could in + // principle + // remove such + // checks after + // the program + // works for some + // time, but it + // might be a + // good things to + // leave it in + // anyway to + // check for + // changes in the + // library or in + // the algorithm + // above. + // + // Note that if + // this check + // fails, then + // this is + // certainly an + // error that is + // irrecoverable + // and probably + // qualifies as + // an internal + // error. We + // therefore use + // a predefined + // exception + // class to throw + // here. Assert (neighbor_child->neighbor(face_no==0 ? 1 : 0)==cell, ExcInternalError()); - // If the check - // succeeded, we - // push the - // active - // neighbor we - // just found to - // the stack we - // keep: + // If the check + // succeeded, we + // push the + // active + // neighbor we + // just found to + // the stack we + // keep: active_neighbors.push_back (neighbor_child); } else - // If we are not in - // 1d, we collect - // all neighbor - // children - // `behind' the - // subfaces of the - // current face + // If we are not in + // 1d, we collect + // all neighbor + // children + // `behind' the + // subfaces of the + // current face for (unsigned int subface_no=0; subface_non_children(); ++subface_no) active_neighbors.push_back ( cell->neighbor_child_on_subface(face_no, subface_no)); }; }; - // OK, now that we have all the - // neighbors, lets start the - // computation on each of - // them. First we do some - // preliminaries: find out - // about the center of the - // present cell and the - // solution at this point. The - // latter is obtained as a - // vector of function values at - // the quadrature points, of - // which there are only one, of - // course. Likewise, the - // position of the center is - // the position of the first - // (and only) quadrature point - // in real space. + // OK, now that we have all the + // neighbors, lets start the + // computation on each of + // them. First we do some + // preliminaries: find out + // about the center of the + // present cell and the + // solution at this point. The + // latter is obtained as a + // vector of function values at + // the quadrature points, of + // which there are only one, of + // course. Likewise, the + // position of the center is + // the position of the first + // (and only) quadrature point + // in real space. const Point this_center = fe_midpoint_value.quadrature_point(0); std::vector this_midpoint_value(1); fe_midpoint_value.get_function_values (solution, this_midpoint_value); - // Now loop over all active neighbors - // and collect the data we - // need. Allocate a vector just like - // this_midpoint_value which we - // will use to store the value of the - // solution in the midpoint of the - // neighbor cell. We allocate it here - // already, since that way we don't - // have to allocate memory repeatedly - // in each iteration of this inner loop - // (memory allocation is a rather - // expensive operation): + // Now loop over all active neighbors + // and collect the data we + // need. Allocate a vector just like + // this_midpoint_value which we + // will use to store the value of the + // solution in the midpoint of the + // neighbor cell. We allocate it here + // already, since that way we don't + // have to allocate memory repeatedly + // in each iteration of this inner loop + // (memory allocation is a rather + // expensive operation): std::vector neighbor_midpoint_value(1); typename std::vector::active_cell_iterator>::const_iterator - neighbor_ptr = active_neighbors.begin(); + neighbor_ptr = active_neighbors.begin(); for (; neighbor_ptr!=active_neighbors.end(); ++neighbor_ptr) { - // First define an - // abbreviation for the - // iterator to the active - // neighbor cell: + // First define an + // abbreviation for the + // iterator to the active + // neighbor cell: const typename DoFHandler::active_cell_iterator - neighbor = *neighbor_ptr; - - // Then get the center of - // the neighbor cell and - // the value of the finite - // element function - // thereon. Note that for - // this information we - // have to reinitialize the - // FEValues object for - // the neighbor cell. + neighbor = *neighbor_ptr; + + // Then get the center of + // the neighbor cell and + // the value of the finite + // element function + // thereon. Note that for + // this information we + // have to reinitialize the + // FEValues object for + // the neighbor cell. fe_midpoint_value.reinit (neighbor); const Point neighbor_center = fe_midpoint_value.quadrature_point(0); fe_midpoint_value.get_function_values (solution, neighbor_midpoint_value); - // Compute the vector y - // connecting the centers - // of the two cells. Note - // that as opposed to the - // introduction, we denote - // by y the normalized - // difference vector, as - // this is the quantity - // used everywhere in the - // computations. + // Compute the vector y + // connecting the centers + // of the two cells. Note + // that as opposed to the + // introduction, we denote + // by y the normalized + // difference vector, as + // this is the quantity + // used everywhere in the + // computations. Point y = neighbor_center - this_center; const double distance = std::sqrt(y.square()); y /= distance; - // Then add up the - // contribution of this - // cell to the Y matrix... + // Then add up the + // contribution of this + // cell to the Y matrix... for (unsigned int i=0; iy - // which span the whole space, - // otherwise we would not have - // all components of the - // gradient. This is indicated - // by the invertability of the - // matrix. - // - // If the matrix should not be - // invertible, this means that - // the present cell had an - // insufficient number of - // active neighbors. In - // contrast to all previous - // cases, where we raised - // exceptions, this is, - // however, not a programming - // error: it is a runtime error - // that can happen in optimized - // mode even if it ran well in - // debug mode, so it is - // reasonable to try to catch - // this error also in optimized - // mode. For this case, there - // is the AssertThrow - // macro: it checks the - // condition like the - // Assert macro, but not - // only in debug mode; it then - // outputs an error message, - // but instead of terminating - // the program as in the case - // of the Assert macro, the - // exception is thrown using - // the throw command of - // C++. This way, one has the - // possibility to catch this - // error and take reasonable - // counter actions. One such - // measure would be to refine - // the grid globally, as the - // case of insufficient - // directions can not occur if - // every cell of the initial - // grid has been refined at - // least once. + // If now, after collecting all + // the information from the + // neighbors, we can determine + // an approximation of the + // gradient for the present + // cell, then we need to have + // passed over vectors y + // which span the whole space, + // otherwise we would not have + // all components of the + // gradient. This is indicated + // by the invertability of the + // matrix. + // + // If the matrix should not be + // invertible, this means that + // the present cell had an + // insufficient number of + // active neighbors. In + // contrast to all previous + // cases, where we raised + // exceptions, this is, + // however, not a programming + // error: it is a runtime error + // that can happen in optimized + // mode even if it ran well in + // debug mode, so it is + // reasonable to try to catch + // this error also in optimized + // mode. For this case, there + // is the AssertThrow + // macro: it checks the + // condition like the + // Assert macro, but not + // only in debug mode; it then + // outputs an error message, + // but instead of terminating + // the program as in the case + // of the Assert macro, the + // exception is thrown using + // the throw command of + // C++. This way, one has the + // possibility to catch this + // error and take reasonable + // counter actions. One such + // measure would be to refine + // the grid globally, as the + // case of insufficient + // directions can not occur if + // every cell of the initial + // grid has been refined at + // least once. AssertThrow (determinant(Y) != 0, ExcInsufficientDirections()); - // If, on the other hand the - // matrix is invertible, then - // invert it, multiply the - // other quantity with it and - // compute the estimated error - // using this quantity and the - // right powers of the mesh - // width: + // If, on the other hand the + // matrix is invertible, then + // invert it, multiply the + // other quantity with it and + // compute the estimated error + // using this quantity and the + // right powers of the mesh + // width: const Tensor<2,dim> Y_inverse = invert(Y); Point gradient; @@ -2069,13 +2069,13 @@ namespace Step9 } - // @sect3{Main function} +// @sect3{Main function} - // The main function is exactly - // like in previous examples, with - // the only difference in the name of - // the main class that actually does - // the computation. +// The main function is exactly +// like in previous examples, with +// the only difference in the name of +// the main class that actually does +// the computation. int main () { try diff --git a/deal.II/include/deal.II/algorithms/newton.h b/deal.II/include/deal.II/algorithms/newton.h index 23a6600900..46810d0beb 100644 --- a/deal.II/include/deal.II/algorithms/newton.h +++ b/deal.II/include/deal.II/algorithms/newton.h @@ -23,177 +23,177 @@ class ParameterHandler; namespace Algorithms { -/** - * Operator class performing Newton's iteration with standard step - * size control and adaptive matrix generation. - * - * This class performs a Newton iteration up to convergence - * determined by #control. If after an update the norm of the residual - * has become larger, then step size control is activated and the - * update is subsequently divided by two until the residual actually - * becomes smaller (or the minimal scaling factor determined by - * #n_stepsize_iterations is reached). - * - * Since assembling matrices, depending on the implementation, tends - * to be costly, this method applies an adaptive reassembling - * strategy. Only if the reduction factor for the residual is more - * than #threshold, the event Algorithms::bad_derivative is submitted to - * #inverse_derivative. It is up to this object to implement - * reassembling accordingly. - * - *

Contents of the NamedData objects

- * - * The only value used by the Newton method is the first vector in the - * parameter out of operator()(). It serves as the start - * vector of Newton's method and in the end contains the solution. All - * other vectors of out are ignored by Newton's method and - * its inner Operator objects. All vectors of in are forwarded to - * the inner Operator objects, with additional information added as follows. - * - * When calling (*#residual)(), the NamedData in given to the - * Newton iteration is prepended by a vector "Newton iterate", - * the current value of the Newton iterate, which can be used to - * evaluate the residual at this point. - * - * For the call to (*#inverse_derivative), the vector "Newton - * residual" is inserted before "Newton iterate". - * - * @author Guido Kanschat, 2006, 2010 - */ + /** + * Operator class performing Newton's iteration with standard step + * size control and adaptive matrix generation. + * + * This class performs a Newton iteration up to convergence + * determined by #control. If after an update the norm of the residual + * has become larger, then step size control is activated and the + * update is subsequently divided by two until the residual actually + * becomes smaller (or the minimal scaling factor determined by + * #n_stepsize_iterations is reached). + * + * Since assembling matrices, depending on the implementation, tends + * to be costly, this method applies an adaptive reassembling + * strategy. Only if the reduction factor for the residual is more + * than #threshold, the event Algorithms::bad_derivative is submitted to + * #inverse_derivative. It is up to this object to implement + * reassembling accordingly. + * + *

Contents of the NamedData objects

+ * + * The only value used by the Newton method is the first vector in the + * parameter out of operator()(). It serves as the start + * vector of Newton's method and in the end contains the solution. All + * other vectors of out are ignored by Newton's method and + * its inner Operator objects. All vectors of in are forwarded to + * the inner Operator objects, with additional information added as follows. + * + * When calling (*#residual)(), the NamedData in given to the + * Newton iteration is prepended by a vector "Newton iterate", + * the current value of the Newton iterate, which can be used to + * evaluate the residual at this point. + * + * For the call to (*#inverse_derivative), the vector "Newton + * residual" is inserted before "Newton iterate". + * + * @author Guido Kanschat, 2006, 2010 + */ template class Newton : public Operator { - public: - /** - * Constructor, receiving the - * applications computing the - * residual and solving the - * linear problem, respectively. - */ - Newton (Operator& residual, Operator& inverse_derivative); - - /** - * Declare the parameters - * applicable to Newton's method. - */ - void declare_parameters (ParameterHandler& param); - - /** - * Read the parameters. - */ - void initialize (ParameterHandler& param); - - /** - * Initialize the pointer - * data_out for debugging. - */ - void initialize (OutputOperator& output); - - /** - * The actual Newton - * iteration. The initial value - * is in out(0), which - * also contains the result - * after convergence. Values in - * in are not used by - * Newton, but will be handed - * down to the objects - * #residual and #inverse_derivative. - */ - virtual void operator() (NamedData& out, const NamedData& in); - - virtual void notify(const Event&); - - /** - * Set the maximal residual - * reduction allowed without - * triggering assembling in the - * next step. Return the - * previous value. - */ - double threshold(double new_value); - - /** - * Control object for the - * Newton iteration. - */ - ReductionControl control; - private: - /** - * The operator computing the residual. - */ - SmartPointer, Newton > residual; - - /** - * The operator applying the - * inverse derivative to the residual. - */ - SmartPointer, Newton > inverse_derivative; - - /** - * The operator handling the output - * in case the debug_vectors is true. - * Call the initialize function first. - */ - SmartPointer, Newton > data_out; - - /** - * This flag is set by the - * function assemble(), - * indicating that the matrix - * must be assembled anew upon - * start. - */ - bool assemble_now; - - /** - * A flag used to decide how - * many stepsize iteration - * should be made. Default is - * the original value of 21. - * - * Enter zero here to turn of - * stepsize control. - * - * @note Controlled by - * Stepsize iterations in - * parameter file - */ - unsigned int n_stepsize_iterations; - - /** - * Threshold for re-assembling matrix. - * - * If the quotient of two - * consecutive residuals is - * smaller than this threshold, - * the system matrix is not - * assembled in this step. - * - * @note This parameter should be - * adjusted to the residual gain - * of the inner solver. - * - * The default values is zero, - * resulting in reassembling in - * every Newton step. - */ - double assemble_threshold; - - public: - /** - * Print residual, update and - * updated solution after each - * step into file - * Newton_NNN? - */ - bool debug_vectors; - /** - * Write debug output to - * @p deallog; the higher the - * number, the more output. - */ - unsigned int debug; + public: + /** + * Constructor, receiving the + * applications computing the + * residual and solving the + * linear problem, respectively. + */ + Newton (Operator &residual, Operator &inverse_derivative); + + /** + * Declare the parameters + * applicable to Newton's method. + */ + void declare_parameters (ParameterHandler ¶m); + + /** + * Read the parameters. + */ + void initialize (ParameterHandler ¶m); + + /** + * Initialize the pointer + * data_out for debugging. + */ + void initialize (OutputOperator &output); + + /** + * The actual Newton + * iteration. The initial value + * is in out(0), which + * also contains the result + * after convergence. Values in + * in are not used by + * Newton, but will be handed + * down to the objects + * #residual and #inverse_derivative. + */ + virtual void operator() (NamedData &out, const NamedData &in); + + virtual void notify(const Event &); + + /** + * Set the maximal residual + * reduction allowed without + * triggering assembling in the + * next step. Return the + * previous value. + */ + double threshold(double new_value); + + /** + * Control object for the + * Newton iteration. + */ + ReductionControl control; + private: + /** + * The operator computing the residual. + */ + SmartPointer, Newton > residual; + + /** + * The operator applying the + * inverse derivative to the residual. + */ + SmartPointer, Newton > inverse_derivative; + + /** + * The operator handling the output + * in case the debug_vectors is true. + * Call the initialize function first. + */ + SmartPointer, Newton > data_out; + + /** + * This flag is set by the + * function assemble(), + * indicating that the matrix + * must be assembled anew upon + * start. + */ + bool assemble_now; + + /** + * A flag used to decide how + * many stepsize iteration + * should be made. Default is + * the original value of 21. + * + * Enter zero here to turn of + * stepsize control. + * + * @note Controlled by + * Stepsize iterations in + * parameter file + */ + unsigned int n_stepsize_iterations; + + /** + * Threshold for re-assembling matrix. + * + * If the quotient of two + * consecutive residuals is + * smaller than this threshold, + * the system matrix is not + * assembled in this step. + * + * @note This parameter should be + * adjusted to the residual gain + * of the inner solver. + * + * The default values is zero, + * resulting in reassembling in + * every Newton step. + */ + double assemble_threshold; + + public: + /** + * Print residual, update and + * updated solution after each + * step into file + * Newton_NNN? + */ + bool debug_vectors; + /** + * Write debug output to + * @p deallog; the higher the + * number, the more output. + */ + unsigned int debug; }; } diff --git a/deal.II/include/deal.II/algorithms/newton.templates.h b/deal.II/include/deal.II/algorithms/newton.templates.h index 710fcf0b67..a7b81721bb 100644 --- a/deal.II/include/deal.II/algorithms/newton.templates.h +++ b/deal.II/include/deal.II/algorithms/newton.templates.h @@ -24,20 +24,20 @@ DEAL_II_NAMESPACE_OPEN namespace Algorithms { template - Newton::Newton(Operator& residual, Operator& inverse_derivative) - : - residual(&residual), inverse_derivative(&inverse_derivative), - assemble_now(false), - n_stepsize_iterations(21), - assemble_threshold(0.), - debug_vectors(false), - debug(0) + Newton::Newton(Operator &residual, Operator &inverse_derivative) + : + residual(&residual), inverse_derivative(&inverse_derivative), + assemble_now(false), + n_stepsize_iterations(21), + assemble_threshold(0.), + debug_vectors(false), + debug(0) {} template void - Newton::declare_parameters(ParameterHandler& param) + Newton::declare_parameters(ParameterHandler ¶m) { param.enter_subsection("Newton"); ReductionControl::declare_parameters (param); @@ -50,7 +50,7 @@ namespace Algorithms template void - Newton::initialize (ParameterHandler& param) + Newton::initialize (ParameterHandler ¶m) { param.enter_subsection("Newton"); control.parse_parameters (param); @@ -62,14 +62,14 @@ namespace Algorithms template void - Newton::initialize (OutputOperator& output) + Newton::initialize (OutputOperator &output) { data_out = &output; } template void - Newton::notify(const Event& e) + Newton::notify(const Event &e) { residual->notify(e); inverse_derivative->notify(e); @@ -78,12 +78,12 @@ namespace Algorithms template void - Newton::operator() (NamedData& out, const NamedData& in) + Newton::operator() (NamedData &out, const NamedData &in) { Assert (out.size() == 1, ExcNotImplemented()); deallog.push ("Newton"); - VECTOR& u = *out(0); + VECTOR &u = *out(0); if (debug>2) deallog << "u: " << u.l2_norm() << std::endl; @@ -93,29 +93,29 @@ namespace Algorithms typename VectorMemory::Pointer res(mem); res->reinit(u); - NamedData src1; - NamedData src2; - VECTOR* p = &u; + NamedData src1; + NamedData src2; + VECTOR *p = &u; src1.add(p, "Newton iterate"); src1.merge(in); p = res; src2.add(p, "Newton residual"); src2.merge(src1); - NamedData out1; + NamedData out1; out1.add(p, "Residual"); p = Du; - NamedData out2; + NamedData out2; out2.add(p, "Update"); unsigned int step = 0; - // fill res with (f(u), v) + // fill res with (f(u), v) (*residual)(out1, src1); double resnorm = res->l2_norm(); double old_residual = resnorm / assemble_threshold + 1; while (control.check(step++, resnorm) == SolverControl::iterate) { - // assemble (Df(u), v) + // assemble (Df(u), v) if (resnorm/old_residual >= assemble_threshold) inverse_derivative->notify (Events::bad_derivative); @@ -124,7 +124,7 @@ namespace Algorithms { (*inverse_derivative)(out2, src2); } - catch (SolverControl::NoConvergence& e) + catch (SolverControl::NoConvergence &e) { deallog << "Inner iteration failed after " << e.last_step << " steps with residual " @@ -133,10 +133,13 @@ namespace Algorithms if (debug_vectors) { - NamedData out; - VECTOR* p = &u; out.add(p, "solution"); - p = Du; out.add(p, "update"); - p = res; out.add(p, "residual"); + NamedData out; + VECTOR *p = &u; + out.add(p, "solution"); + p = Du; + out.add(p, "update"); + p = res; + out.add(p, "residual"); *data_out << step; *data_out << out; } @@ -146,7 +149,7 @@ namespace Algorithms (*residual)(out1, src1); resnorm = res->l2_norm(); - // Step size control + // Step size control unsigned int step_size = 0; while (resnorm >= old_residual) { @@ -166,12 +169,12 @@ namespace Algorithms } deallog.pop(); - // in case of failure: throw - // exception + // in case of failure: throw + // exception if (control.last_check() != SolverControl::success) throw SolverControl::NoConvergence (control.last_step(), control.last_value()); - // otherwise exit as normal + // otherwise exit as normal } } diff --git a/deal.II/include/deal.II/algorithms/operator.h b/deal.II/include/deal.II/algorithms/operator.h index ca9b9ca98a..f4e407d1e1 100644 --- a/deal.II/include/deal.II/algorithms/operator.h +++ b/deal.II/include/deal.II/algorithms/operator.h @@ -23,112 +23,112 @@ DEAL_II_NAMESPACE_OPEN namespace Algorithms { -/** - * The abstract base class of all algorithms in this library. An - * operator is an object with an operator(), which transforms a set - * of named vectors into another set of named vectors. - * - * Furthermore, an operator can be notified of parameter changes by - * the calling routine. The outer iteration can notify() the Operator - * of an Event, which could be for instance a change of mesh, a - * different time step size or too slow convergence of Newton's - * method, which would then trigger reassembling of a matrix or - * similar things. - * - *

Usage for nested iterations

- * - * This is probably the most prominent use for Operator, where an - * outer iterative method calls an inner solver and so on. Typically, - * the innermost method in such a nested system will have to compute a - * residual using values from all outer iterations. Since the depth - * and order of such a nesting is hardly predictable when designing a - * general tool, we use NamedData to access these vectors. Typically, - * the first vector in out contains the start vector when - * operator()() is called, and the solution when the function - * returns. The object in is providing additional information - * and forwarded to the inner Operator objects of the nested - * iteration. - * - * @author Guido Kanschat, 2010 - */ + /** + * The abstract base class of all algorithms in this library. An + * operator is an object with an operator(), which transforms a set + * of named vectors into another set of named vectors. + * + * Furthermore, an operator can be notified of parameter changes by + * the calling routine. The outer iteration can notify() the Operator + * of an Event, which could be for instance a change of mesh, a + * different time step size or too slow convergence of Newton's + * method, which would then trigger reassembling of a matrix or + * similar things. + * + *

Usage for nested iterations

+ * + * This is probably the most prominent use for Operator, where an + * outer iterative method calls an inner solver and so on. Typically, + * the innermost method in such a nested system will have to compute a + * residual using values from all outer iterations. Since the depth + * and order of such a nesting is hardly predictable when designing a + * general tool, we use NamedData to access these vectors. Typically, + * the first vector in out contains the start vector when + * operator()() is called, and the solution when the function + * returns. The object in is providing additional information + * and forwarded to the inner Operator objects of the nested + * iteration. + * + * @author Guido Kanschat, 2010 + */ template class Operator : public Subscriptor { - public: - /** - * The virtual destructor. - */ - ~Operator(); + public: + /** + * The virtual destructor. + */ + ~Operator(); - /** - * The actual operation, which - * is implemented in a derived class. - */ - virtual void operator() (NamedData& out, const NamedData& in) = 0; + /** + * The actual operation, which + * is implemented in a derived class. + */ + virtual void operator() (NamedData &out, const NamedData &in) = 0; - /** - * Register an event triggered - * by an outer iteration. - */ - virtual void notify(const Event&); - /** - * Clear all #notifications. - */ - void clear_events(); - protected: - /** - * Accumulate reasons for - * reassembling here. If any of - * those is set, the function - * solve() of a terminal - * application must take care - * of reassembling the matrix. - */ - Event notifications; + /** + * Register an event triggered + * by an outer iteration. + */ + virtual void notify(const Event &); + /** + * Clear all #notifications. + */ + void clear_events(); + protected: + /** + * Accumulate reasons for + * reassembling here. If any of + * those is set, the function + * solve() of a terminal + * application must take care + * of reassembling the matrix. + */ + Event notifications; }; -/** - * An unary operator base class, intended to output the vectors in - * NamedData in each step of an iteration. - * - * @author Guido Kanschat, 2010 - */ + /** + * An unary operator base class, intended to output the vectors in + * NamedData in each step of an iteration. + * + * @author Guido Kanschat, 2010 + */ template class OutputOperator : public Subscriptor { - OutputOperator(const OutputOperator&); - public: + OutputOperator(const OutputOperator &); + public: OutputOperator (); - /** - * Empty virtual destructor. - */ - virtual ~OutputOperator(); + /** + * Empty virtual destructor. + */ + virtual ~OutputOperator(); - /** - * Set the stream @p os to - * which data is written. If - * no stream is selected with - * this function, data goes - * to @p deallog. - */ - void initialize_stream(std::ostream& stream); - /** - * Set the current step. - */ - OutputOperator& operator<< (unsigned int step); + /** + * Set the stream @p os to + * which data is written. If + * no stream is selected with + * this function, data goes + * to @p deallog. + */ + void initialize_stream(std::ostream &stream); + /** + * Set the current step. + */ + OutputOperator &operator<< (unsigned int step); - /** - * Output all the vectors in NamedData. - */ - virtual OutputOperator& operator<< (const NamedData& vectors); - protected: - unsigned int step; - private: - std::ostream* os; + /** + * Output all the vectors in NamedData. + */ + virtual OutputOperator &operator<< (const NamedData &vectors); + protected: + unsigned int step; + private: + std::ostream *os; }; template - OutputOperator& + OutputOperator & OutputOperator::operator<< (unsigned int s) { step = s; diff --git a/deal.II/include/deal.II/algorithms/operator.templates.h b/deal.II/include/deal.II/algorithms/operator.templates.h index b852ceab3c..bfab37b2d9 100644 --- a/deal.II/include/deal.II/algorithms/operator.templates.h +++ b/deal.II/include/deal.II/algorithms/operator.templates.h @@ -23,7 +23,7 @@ namespace Algorithms template - void Operator::notify(const Event& e) + void Operator::notify(const Event &e) { notifications += e; } @@ -43,36 +43,36 @@ namespace Algorithms template OutputOperator::OutputOperator() - : + : os(0) {} template - void OutputOperator::initialize_stream(std::ostream& stream) + void OutputOperator::initialize_stream(std::ostream &stream) { os =&stream; } template - OutputOperator& - OutputOperator::operator<< (const NamedData& vectors) + OutputOperator & + OutputOperator::operator<< (const NamedData &vectors) { - if(os == 0) - { - //TODO: make this possible - //deallog << ' ' << step; - //for (unsigned int i=0;iprint(deallog); - //deallog << std::endl; - } + if (os == 0) + { + //TODO: make this possible + //deallog << ' ' << step; + //for (unsigned int i=0;iprint(deallog); + //deallog << std::endl; + } else - { - (*os) << ' ' << step; - for (unsigned int i=0;isize();++j) - (*os) << ' ' << (*vectors(i))(j); - (*os) << std::endl; - } + { + (*os) << ' ' << step; + for (unsigned int i=0; isize(); ++j) + (*os) << ' ' << (*vectors(i))(j); + (*os) << std::endl; + } return *this; } } diff --git a/deal.II/include/deal.II/algorithms/theta_timestepping.h b/deal.II/include/deal.II/algorithms/theta_timestepping.h index 17632007e6..3dc9fa5d82 100644 --- a/deal.II/include/deal.II/algorithms/theta_timestepping.h +++ b/deal.II/include/deal.II/algorithms/theta_timestepping.h @@ -23,319 +23,319 @@ class ParameterHandler; namespace Algorithms { -/** - * A little structure, gathering the size of a timestep and the - * current time. Time stepping schemes can use this to provide time - * step information to the classes actually performing a single step. - * - * The definition of what is considered "current time" depends on the - * scheme. For an explicit scheme, this is the time at the beginning - * of the step. For an implicit scheme, it is usually the time at the - * end. - */ + /** + * A little structure, gathering the size of a timestep and the + * current time. Time stepping schemes can use this to provide time + * step information to the classes actually performing a single step. + * + * The definition of what is considered "current time" depends on the + * scheme. For an explicit scheme, this is the time at the beginning + * of the step. For an implicit scheme, it is usually the time at the + * end. + */ struct TimestepData { /// The current time - double time; + double time; /// The current step size times something - double step; + double step; }; -/** - * Application class performing the theta timestepping scheme. - * - * The theta scheme is an abstraction of implicit and explicit Euler - * schemes, the Crank-Nicholson scheme and linear combinations of - * those. The choice of the actual scheme is controlled by the - * parameter #theta as follows. - *
    - *
  • #theta=0: explicit Euler scheme - *
  • #theta=1: implicit Euler scheme - *
  • #theta=½: Crank-Nicholson scheme - *
- * - * For fixed #theta, the Crank-Nicholson scheme is the only second - * order scheme. Nevertheless, further stability may be achieved by - * choosing #theta larger than ½, thereby introducing a first order - * error term. In order to avoid a loss of convergence order, the - * adaptive theta scheme can be used, where #theta=½+c dt. - * - * Assume that we want to solve the equation u' + Au = 0 with a - * step size k. A step of the theta scheme can be written as - * - * @f[ - * (M + \theta k A) u_{n+1} = (M - (1-\theta)k A) u_n. - * @f] - * - * Here, M is the mass matrix. We see, that the right hand side - * amounts to an explicit Euler step with modified step size in weak - * form (up to inversion of M). The left hand side corresponds to an - * implicit Euler step with modified step size (right hand side - * given). Thus, the implementation of the theta scheme will use two - * Operator objects, one for the explicit, one for the implicit - * part. Each of these will use its own TimestepData to account for - * the modified step sizes (and different times if the problem is not - * autonomous). - * - *

Usage of vectors in NamedData

- * - * ThetaTimestepping uses NamedData for communicating vectors. With - * outer or inner Operator objects. It does not use itself the input - * vectors provided, but forwards them to the explicit and implicit - * operators. - * - * The explicit Operator #op_explicit receives in its input in first - * place the vector "Previous iterate", which is the solution - * value after the previous timestep. It is followed by all vectors - * provided to ThetaTimestepping::operator() as input - * argument. #op_explicit is supposed to write its result into the - * first position of its output argument, labeled "Result". - * - * The implicit Operator #op_implicit receives the result of - * #op_explicit in its first input vector labeled "Previous - * time". It is followed by all vectors provided to - * ThetaTimestepping::operator() as input argument. The output of - * #op_implicit is directly written into the output argument given to - * ThetaTimestepping. - * - *

Usage of ThetaTimestepping

- * - * The use ThetaTimestepping is more complicated than for instance - * Newton, since the inner operators will usually need to access the - * TimeStepData. Thus, we have a circular dependency of information, - * and we include the following example for its use. It can be found - * in examples/doxygen/theta_timestepping.cc - * - * @dontinclude theta_timestepping.cc - * - * First, we define the two operators used by ThetaTimestepping and - * call them Implicit and Explicit. They - * both share the public interface of Operator, and additionally - * provide storage for the matrices to be used and a pointer to - * TimestepData. Note that we do not use a SmartPointer here, since - * the TimestepData will be destroyed before the operator. - * - * @skip class Explicit - * @until End of declarations - * - * These operators will be implemented after the main program. But let - * us look at how they get used. First, let us define a matrix to be - * used for our system and also an OutputOperator in order to write - * the data of each timestep to a file. - * - * @skipline main - * @until out.initialize - * - * Now we create objects for the implicit and explicit parts of the - * steps as well as the ThetaTimestepping itself. Notice how the - * TimestepData of ThetaTimestepping gets forwarded to the inner - * operators. There are two different data objects, because the - * timestep size is modified by #theta. - * - * @until set_output - * - * The next step is providing the vectors to be used. value - * is filled with the initial value and is also the vector where the - * solution at each timestep will be. Because the interface of - * Operator has to be able to handle several vectors, we need to store - * it in a NamedData object. Notice, that we need to create the - * intermediate pointer p. If we would use - * &value directly in the add function, the - * resulting object would be constant. - * - * @until add - * - * Finally, we are ready to tell the solver, that we are looknig at - * the initial timestep and run it. - * - * @until outdata - * @skip Explicit::initialize - * - * Now we need to study the application of the implicit and explicit - * operator. We assume that the pointer matrix points to - * the matrix created in the main program, and that - * timestep_data points to the correct data object of - * ThetaTimestepping. - * - * @skipline void - * @until vmult - * @until } - * - * @author Guido Kanschat - * @date 2010 - */ + /** + * Application class performing the theta timestepping scheme. + * + * The theta scheme is an abstraction of implicit and explicit Euler + * schemes, the Crank-Nicholson scheme and linear combinations of + * those. The choice of the actual scheme is controlled by the + * parameter #theta as follows. + *
    + *
  • #theta=0: explicit Euler scheme + *
  • #theta=1: implicit Euler scheme + *
  • #theta=½: Crank-Nicholson scheme + *
+ * + * For fixed #theta, the Crank-Nicholson scheme is the only second + * order scheme. Nevertheless, further stability may be achieved by + * choosing #theta larger than ½, thereby introducing a first order + * error term. In order to avoid a loss of convergence order, the + * adaptive theta scheme can be used, where #theta=½+c dt. + * + * Assume that we want to solve the equation u' + Au = 0 with a + * step size k. A step of the theta scheme can be written as + * + * @f[ + * (M + \theta k A) u_{n+1} = (M - (1-\theta)k A) u_n. + * @f] + * + * Here, M is the mass matrix. We see, that the right hand side + * amounts to an explicit Euler step with modified step size in weak + * form (up to inversion of M). The left hand side corresponds to an + * implicit Euler step with modified step size (right hand side + * given). Thus, the implementation of the theta scheme will use two + * Operator objects, one for the explicit, one for the implicit + * part. Each of these will use its own TimestepData to account for + * the modified step sizes (and different times if the problem is not + * autonomous). + * + *

Usage of vectors in NamedData

+ * + * ThetaTimestepping uses NamedData for communicating vectors. With + * outer or inner Operator objects. It does not use itself the input + * vectors provided, but forwards them to the explicit and implicit + * operators. + * + * The explicit Operator #op_explicit receives in its input in first + * place the vector "Previous iterate", which is the solution + * value after the previous timestep. It is followed by all vectors + * provided to ThetaTimestepping::operator() as input + * argument. #op_explicit is supposed to write its result into the + * first position of its output argument, labeled "Result". + * + * The implicit Operator #op_implicit receives the result of + * #op_explicit in its first input vector labeled "Previous + * time". It is followed by all vectors provided to + * ThetaTimestepping::operator() as input argument. The output of + * #op_implicit is directly written into the output argument given to + * ThetaTimestepping. + * + *

Usage of ThetaTimestepping

+ * + * The use ThetaTimestepping is more complicated than for instance + * Newton, since the inner operators will usually need to access the + * TimeStepData. Thus, we have a circular dependency of information, + * and we include the following example for its use. It can be found + * in examples/doxygen/theta_timestepping.cc + * + * @dontinclude theta_timestepping.cc + * + * First, we define the two operators used by ThetaTimestepping and + * call them Implicit and Explicit. They + * both share the public interface of Operator, and additionally + * provide storage for the matrices to be used and a pointer to + * TimestepData. Note that we do not use a SmartPointer here, since + * the TimestepData will be destroyed before the operator. + * + * @skip class Explicit + * @until End of declarations + * + * These operators will be implemented after the main program. But let + * us look at how they get used. First, let us define a matrix to be + * used for our system and also an OutputOperator in order to write + * the data of each timestep to a file. + * + * @skipline main + * @until out.initialize + * + * Now we create objects for the implicit and explicit parts of the + * steps as well as the ThetaTimestepping itself. Notice how the + * TimestepData of ThetaTimestepping gets forwarded to the inner + * operators. There are two different data objects, because the + * timestep size is modified by #theta. + * + * @until set_output + * + * The next step is providing the vectors to be used. value + * is filled with the initial value and is also the vector where the + * solution at each timestep will be. Because the interface of + * Operator has to be able to handle several vectors, we need to store + * it in a NamedData object. Notice, that we need to create the + * intermediate pointer p. If we would use + * &value directly in the add function, the + * resulting object would be constant. + * + * @until add + * + * Finally, we are ready to tell the solver, that we are looknig at + * the initial timestep and run it. + * + * @until outdata + * @skip Explicit::initialize + * + * Now we need to study the application of the implicit and explicit + * operator. We assume that the pointer matrix points to + * the matrix created in the main program, and that + * timestep_data points to the correct data object of + * ThetaTimestepping. + * + * @skipline void + * @until vmult + * @until } + * + * @author Guido Kanschat + * @date 2010 + */ template class ThetaTimestepping : public Operator { - public: - /** - * Constructor, receiving the - * two operators stored in - * #op_explicit and #op_implicit. For - * their meening, see the - * description of those variables. - */ - ThetaTimestepping (Operator& op_explicit, - Operator& op_implicit); + public: + /** + * Constructor, receiving the + * two operators stored in + * #op_explicit and #op_implicit. For + * their meening, see the + * description of those variables. + */ + ThetaTimestepping (Operator &op_explicit, + Operator &op_implicit); - /** - * The timestepping scheme. in - * should contain the initial - * value in first position. out - */ - virtual void operator() (NamedData& out, const NamedData& in); + /** + * The timestepping scheme. in + * should contain the initial + * value in first position. out + */ + virtual void operator() (NamedData &out, const NamedData &in); - /** - * Register an event triggered - * by an outer iteration. - */ - virtual void notify(const Event&); + /** + * Register an event triggered + * by an outer iteration. + */ + virtual void notify(const Event &); - /** - * Define an operator which - * will output the result in - * each step. Note that no - * output will be generated - * without this. - */ - void set_output(OutputOperator& output); + /** + * Define an operator which + * will output the result in + * each step. Note that no + * output will be generated + * without this. + */ + void set_output(OutputOperator &output); - void declare_parameters (ParameterHandler& param); - void initialize (ParameterHandler& param); - /** - * The current time in the - * timestepping scheme. - */ - const double& current_time() const; - /** - * The current step size. - */ - const double& step_size() const; - /** - * The weight between implicit - * and explicit part. - */ - const double& theta() const; + void declare_parameters (ParameterHandler ¶m); + void initialize (ParameterHandler ¶m); + /** + * The current time in the + * timestepping scheme. + */ + const double ¤t_time() const; + /** + * The current step size. + */ + const double &step_size() const; + /** + * The weight between implicit + * and explicit part. + */ + const double &theta() const; - /** - * The data handed to the - * #op_explicit time stepping - * operator. - * - * The time in here is the time - * at the beginning of the - * current step, the time step - * is (1-#theta) times the - * actual time step. - */ - const TimestepData& explicit_data() const; + /** + * The data handed to the + * #op_explicit time stepping + * operator. + * + * The time in here is the time + * at the beginning of the + * current step, the time step + * is (1-#theta) times the + * actual time step. + */ + const TimestepData &explicit_data() const; - /** - * The data handed to the - * #op_implicit time stepping - * operator. - * - * The time in here is the time - * at the beginning of the - * current step, the time step - * is #theta times the - * actual time step. - */ - const TimestepData& implicit_data() const; + /** + * The data handed to the + * #op_implicit time stepping + * operator. + * + * The time in here is the time + * at the beginning of the + * current step, the time step + * is #theta times the + * actual time step. + */ + const TimestepData &implicit_data() const; - /** - * Allow access to the control object. - */ - TimestepControl& timestep_control(); + /** + * Allow access to the control object. + */ + TimestepControl ×tep_control(); - private: - /** - * The object controlling the - * time step size and computing - * the new time in each step. - */ - TimestepControl control; + private: + /** + * The object controlling the + * time step size and computing + * the new time in each step. + */ + TimestepControl control; - /** - * The control parameter theta in the - * range [0,1]. - */ - double vtheta; - /** - * Use adaptive #theta if - * true. - */ - bool adaptive; + /** + * The control parameter theta in the + * range [0,1]. + */ + double vtheta; + /** + * Use adaptive #theta if + * true. + */ + bool adaptive; - /** - * The data for the explicit - * part of the scheme. - */ - TimestepData d_explicit; + /** + * The data for the explicit + * part of the scheme. + */ + TimestepData d_explicit; - /** - * The data for the implicit - * part of the scheme. - */ - TimestepData d_implicit; + /** + * The data for the implicit + * part of the scheme. + */ + TimestepData d_implicit; - /** - * The operator computing the - * explicit part of the - * scheme. This will receive in - * its input data the value at - * the current time with name - * "Current time solution". It - * should obtain the current - * time and time step size from - * explicit_data(). - * - * Its return value is - * Mu+cAu, where - * u is the current - * state vector, M the - * mass matrix, A the - * operator in space and - * c is the time step - * size in explicit_data(). - */ - SmartPointer, ThetaTimestepping > op_explicit; + /** + * The operator computing the + * explicit part of the + * scheme. This will receive in + * its input data the value at + * the current time with name + * "Current time solution". It + * should obtain the current + * time and time step size from + * explicit_data(). + * + * Its return value is + * Mu+cAu, where + * u is the current + * state vector, M the + * mass matrix, A the + * operator in space and + * c is the time step + * size in explicit_data(). + */ + SmartPointer, ThetaTimestepping > op_explicit; - /** - * The operator solving the - * implicit part of the - * scheme. It will receive in - * its input data the vector - * "Previous time". Information on the - * timestep should be obtained - * from implicit_data(). - * - * Its return value is the - * solution u of - * Mu-cAu=f, where - * f is the dual space - * vector found in the - * "Previous time" entry of the - * input data, M the - * mass matrix, A the - * operator in space and - * c is the time step - * size in explicit_data(). - */ - SmartPointer, ThetaTimestepping > op_implicit; + /** + * The operator solving the + * implicit part of the + * scheme. It will receive in + * its input data the vector + * "Previous time". Information on the + * timestep should be obtained + * from implicit_data(). + * + * Its return value is the + * solution u of + * Mu-cAu=f, where + * f is the dual space + * vector found in the + * "Previous time" entry of the + * input data, M the + * mass matrix, A the + * operator in space and + * c is the time step + * size in explicit_data(). + */ + SmartPointer, ThetaTimestepping > op_implicit; - /** - * The operator writing the - * output in each time step - */ - SmartPointer, ThetaTimestepping > output; + /** + * The operator writing the + * output in each time step + */ + SmartPointer, ThetaTimestepping > output; }; template inline - const TimestepData& + const TimestepData & ThetaTimestepping::explicit_data () const { return d_explicit; @@ -344,7 +344,7 @@ namespace Algorithms template inline - const TimestepData& + const TimestepData & ThetaTimestepping::implicit_data () const { return d_implicit; @@ -352,7 +352,7 @@ namespace Algorithms template inline - void ThetaTimestepping::set_output (OutputOperator& out) + void ThetaTimestepping::set_output (OutputOperator &out) { output = &out; } diff --git a/deal.II/include/deal.II/algorithms/theta_timestepping.templates.h b/deal.II/include/deal.II/algorithms/theta_timestepping.templates.h index 288a969f00..5dd1d33e12 100644 --- a/deal.II/include/deal.II/algorithms/theta_timestepping.templates.h +++ b/deal.II/include/deal.II/algorithms/theta_timestepping.templates.h @@ -20,14 +20,14 @@ DEAL_II_NAMESPACE_OPEN namespace Algorithms { template - ThetaTimestepping::ThetaTimestepping (Operator& e, Operator& i) - : vtheta(0.5), adaptive(false), op_explicit(&e), op_implicit(&i) + ThetaTimestepping::ThetaTimestepping (Operator &e, Operator &i) + : vtheta(0.5), adaptive(false), op_explicit(&e), op_implicit(&i) {} template void - ThetaTimestepping::notify(const Event& e) + ThetaTimestepping::notify(const Event &e) { op_explicit->notify(e); op_implicit->notify(e); @@ -35,7 +35,7 @@ namespace Algorithms template void - ThetaTimestepping::declare_parameters(ParameterHandler& param) + ThetaTimestepping::declare_parameters(ParameterHandler ¶m) { param.enter_subsection("ThetaTimestepping"); TimestepControl::declare_parameters (param); @@ -46,7 +46,7 @@ namespace Algorithms template void - ThetaTimestepping::initialize (ParameterHandler& param) + ThetaTimestepping::initialize (ParameterHandler ¶m) { param.enter_subsection("ThetaTimestepping"); control.parse_parameters (param); @@ -58,7 +58,7 @@ namespace Algorithms template void - ThetaTimestepping::operator() (NamedData& out, const NamedData& in) + ThetaTimestepping::operator() (NamedData &out, const NamedData &in) { Assert(!adaptive, ExcNotImplemented()); @@ -71,22 +71,22 @@ namespace Algorithms d_explicit.time = control.now(); - // The data used to compute the - // vector associated with the old - // timestep - VECTOR* p = out(0); - NamedData src1; + // The data used to compute the + // vector associated with the old + // timestep + VECTOR *p = out(0); + NamedData src1; src1.add(p, "Previous iterate"); src1.merge(in); - NamedData src2; + NamedData src2; src2.add(p, "Previous iterate"); p = aux; - NamedData out1; + NamedData out1; out1.add(p, "Result"); - // The data provided to the inner - // solver + // The data provided to the inner + // solver src2.add(p, "Previous time"); src2.merge(in); @@ -109,8 +109,8 @@ namespace Algorithms op_implicit->notify(Events::new_timestep_size); } - // Compute - // (I + (1-theta)dt A) u + // Compute + // (I + (1-theta)dt A) u (*op_explicit)(out1, src1); (*op_implicit)(out, src2); diff --git a/deal.II/include/deal.II/algorithms/timestep_control.h b/deal.II/include/deal.II/algorithms/timestep_control.h index 2c7f3ea94b..4a2b28a874 100644 --- a/deal.II/include/deal.II/algorithms/timestep_control.h +++ b/deal.II/include/deal.II/algorithms/timestep_control.h @@ -24,193 +24,193 @@ class ParameterHandler; namespace Algorithms { -/** - * Control class for timestepping schemes. Its main task is - * determining the size of the next time step and the according point - * in the time interval. Additionally, it controls writing the - * solution to a file. - * - * The size of the next time step is determined as follows: - *
    - *
  1. According to the strategy, the step size is tentatively added to the - * current time. - *
  2. If the resulting time exceeds the final time of the interval, - * the step size is reduced in order to meet this time. - *
  3. If the resulting time is below the final time by just a - * fraction of the step size, the step size is increased in order to - * meet this time. - *
  4. The resulting step size is used from the current time. - *
- * - * The variable @p print_step can be used to control the amount of - * output generated by the timestepping scheme. - */ + /** + * Control class for timestepping schemes. Its main task is + * determining the size of the next time step and the according point + * in the time interval. Additionally, it controls writing the + * solution to a file. + * + * The size of the next time step is determined as follows: + *
    + *
  1. According to the strategy, the step size is tentatively added to the + * current time. + *
  2. If the resulting time exceeds the final time of the interval, + * the step size is reduced in order to meet this time. + *
  3. If the resulting time is below the final time by just a + * fraction of the step size, the step size is increased in order to + * meet this time. + *
  4. The resulting step size is used from the current time. + *
+ * + * The variable @p print_step can be used to control the amount of + * output generated by the timestepping scheme. + */ class TimestepControl : public Subscriptor { - public: - /** - * The time stepping - * strategies. These are - * controlled by the value of - * tolerance() and start_step(). - */ - enum Strategy - { - /** - * Choose a uniform time - * step size. The step size - * is determined by - * start_step(), - * tolerance() is ignored. - */ - uniform, - /** - * Start with the time step - * size given by - * start_step() and double it - * in every - * step. tolerance() is - * ignored. - * - * This strategy is - * intended for - * pseudo-timestepping - * schemes computing a - * stationary limit. - */ - doubling - }; - - /** - * Constructor setting default - * values - */ - TimestepControl (double start = 0., - double final = 1., - double tolerance = 1.e-2, - double start_step = 1.e-2, - double print_step = -1., - double max_step = 1.); - - /** - * Declare the control - * parameters for parameter - * handler. - */ - static void declare_parameters (ParameterHandler& param); - /** - * Read the control parameters - * from a parameter handler. - */ - void parse_parameters (ParameterHandler& param); - - /** - * The left end of the time interval. - */ - double start () const; - /** - * The right end of the time - * interval. The control - * mechanism ensures that the - * final time step ends at this - * point. - */ - double final () const; - /** - * The tolerance value - * controlling the time steps. - */ - double tolerance () const; - /** - * The size of the current time - * step. - */ - double step () const; - - /** - * The current time. - */ - double now () const; - - /** - * Compute the size of the next - * step size and return true if - * it differs from the current - * step size. Advance the - * current time by the new step - * size. - */ - bool advance (); - - /** - * Set start value. - */ - void start (double); - /** - * Set final time value. - */ - void final (double); - /** - * Set tolerance - */ - void tolerance (double); - /** - * Set strategy. - */ - void strategy (Strategy); - - /** - * Set size of the first - * step. This may be overwritten - * by the time stepping strategy. - */ - void start_step (double); - - /** - * Set size of the maximum - * step size. - */ - void max_step (double); - - /** - * Set now() equal to - * start(). Initialize step() and - * print() to their initial - * values. - */ - void restart (); - /** - * Return true if this timestep - * should be written to disk. - */ - bool print (); - /** - * Set the output name template. - */ - void file_name_format (const char*); - const char* file_name_format (); - private: - - double start_val; - double final_val; - double tolerance_val; - Strategy strategy_val; - double start_step_val; - double max_step_val; - double min_step_val; - /** - * The size of the current time - * step. This may differ from - * @p step_val, if we aimed at @p final_val. - */ - double current_step_val; - double step_val; - - double now_val; - double print_step; - double next_print_val; - - char format[30]; + public: + /** + * The time stepping + * strategies. These are + * controlled by the value of + * tolerance() and start_step(). + */ + enum Strategy + { + /** + * Choose a uniform time + * step size. The step size + * is determined by + * start_step(), + * tolerance() is ignored. + */ + uniform, + /** + * Start with the time step + * size given by + * start_step() and double it + * in every + * step. tolerance() is + * ignored. + * + * This strategy is + * intended for + * pseudo-timestepping + * schemes computing a + * stationary limit. + */ + doubling + }; + + /** + * Constructor setting default + * values + */ + TimestepControl (double start = 0., + double final = 1., + double tolerance = 1.e-2, + double start_step = 1.e-2, + double print_step = -1., + double max_step = 1.); + + /** + * Declare the control + * parameters for parameter + * handler. + */ + static void declare_parameters (ParameterHandler ¶m); + /** + * Read the control parameters + * from a parameter handler. + */ + void parse_parameters (ParameterHandler ¶m); + + /** + * The left end of the time interval. + */ + double start () const; + /** + * The right end of the time + * interval. The control + * mechanism ensures that the + * final time step ends at this + * point. + */ + double final () const; + /** + * The tolerance value + * controlling the time steps. + */ + double tolerance () const; + /** + * The size of the current time + * step. + */ + double step () const; + + /** + * The current time. + */ + double now () const; + + /** + * Compute the size of the next + * step size and return true if + * it differs from the current + * step size. Advance the + * current time by the new step + * size. + */ + bool advance (); + + /** + * Set start value. + */ + void start (double); + /** + * Set final time value. + */ + void final (double); + /** + * Set tolerance + */ + void tolerance (double); + /** + * Set strategy. + */ + void strategy (Strategy); + + /** + * Set size of the first + * step. This may be overwritten + * by the time stepping strategy. + */ + void start_step (double); + + /** + * Set size of the maximum + * step size. + */ + void max_step (double); + + /** + * Set now() equal to + * start(). Initialize step() and + * print() to their initial + * values. + */ + void restart (); + /** + * Return true if this timestep + * should be written to disk. + */ + bool print (); + /** + * Set the output name template. + */ + void file_name_format (const char *); + const char *file_name_format (); + private: + + double start_val; + double final_val; + double tolerance_val; + Strategy strategy_val; + double start_step_val; + double max_step_val; + double min_step_val; + /** + * The size of the current time + * step. This may differ from + * @p step_val, if we aimed at @p final_val. + */ + double current_step_val; + double step_val; + + double now_val; + double print_step; + double next_print_val; + + char format[30]; }; @@ -305,13 +305,13 @@ namespace Algorithms inline void - TimestepControl::file_name_format (const char* fmt) + TimestepControl::file_name_format (const char *fmt) { strcpy(format, fmt); } - inline const char* + inline const char * TimestepControl::file_name_format () { return format; diff --git a/deal.II/include/deal.II/base/auto_derivative_function.h b/deal.II/include/deal.II/base/auto_derivative_function.h index 8f43db4ce4..c85d9ff928 100644 --- a/deal.II/include/deal.II/base/auto_derivative_function.h +++ b/deal.II/include/deal.II/base/auto_derivative_function.h @@ -73,201 +73,201 @@ DEAL_II_NAMESPACE_OPEN template class AutoDerivativeFunction : public Function { - public: +public: - /** - * Names of difference formulas. - */ - enum DifferenceFormula - { - /** - * The symmetric Euler - * formula of second order: - * @f[ - * u'(t) \approx - * \frac{u(t+h) - - * u(t-h)}{2h}. - * @f] - */ - Euler, - /** - * The upwind Euler - * formula of first order: - * @f[ - * u'(t) \approx - * \frac{u(t) - - * u(t-h)}{h}. - * @f] - */ - UpwindEuler, - /** - * The fourth order scheme - * @f[ - * u'(t) \approx - * \frac{u(t-2h) - 8u(t-h) - * + 8u(t+h) - u(t+2h)}{12h}. - * @f] - */ - FourthOrder - }; + /** + * Names of difference formulas. + */ + enum DifferenceFormula + { + /** + * The symmetric Euler + * formula of second order: + * @f[ + * u'(t) \approx + * \frac{u(t+h) - + * u(t-h)}{2h}. + * @f] + */ + Euler, + /** + * The upwind Euler + * formula of first order: + * @f[ + * u'(t) \approx + * \frac{u(t) - + * u(t-h)}{h}. + * @f] + */ + UpwindEuler, + /** + * The fourth order scheme + * @f[ + * u'(t) \approx + * \frac{u(t-2h) - 8u(t-h) + * + 8u(t+h) - u(t+2h)}{12h}. + * @f] + */ + FourthOrder + }; - /** - * Constructor. Takes the - * difference step size - * h. It's within the user's - * responsibility to choose an - * appropriate value here. h - * should be chosen taking into - * account the absolute value as - * well as the amount of local - * variation of the function. - * Setting h=1e-6 might be a - * good choice for functions with - * an absolute value of about 1, - * that furthermore does not vary - * to much. - * - * h can be changed later - * using the set_h() function. - * - * Sets DifferenceFormula - * formula to the default - * Euler formula of the - * set_formula() - * function. Change this preset - * formula by calling the - * set_formula() function. - */ - AutoDerivativeFunction (const double h, - const unsigned int n_components = 1, - const double initial_time = 0.0); + /** + * Constructor. Takes the + * difference step size + * h. It's within the user's + * responsibility to choose an + * appropriate value here. h + * should be chosen taking into + * account the absolute value as + * well as the amount of local + * variation of the function. + * Setting h=1e-6 might be a + * good choice for functions with + * an absolute value of about 1, + * that furthermore does not vary + * to much. + * + * h can be changed later + * using the set_h() function. + * + * Sets DifferenceFormula + * formula to the default + * Euler formula of the + * set_formula() + * function. Change this preset + * formula by calling the + * set_formula() function. + */ + AutoDerivativeFunction (const double h, + const unsigned int n_components = 1, + const double initial_time = 0.0); - /** - * Virtual destructor; absolutely - * necessary in this case. - */ - virtual ~AutoDerivativeFunction (); + /** + * Virtual destructor; absolutely + * necessary in this case. + */ + virtual ~AutoDerivativeFunction (); - /** - * Choose the difference formula. - * See the enum #DifferenceFormula - * for available choices. - */ - void set_formula (const DifferenceFormula formula = Euler); + /** + * Choose the difference formula. + * See the enum #DifferenceFormula + * for available choices. + */ + void set_formula (const DifferenceFormula formula = Euler); - /** - * Takes the difference step size - * h. It's within the user's - * responsibility to choose an - * appropriate value here. h - * should be chosen taking into - * account the absolute value of - * as well as the amount of local - * variation of the function. - * Setting h=1e-6 might be a - * good choice for functions with - * an absolute value of about 1, - * that furthermore does not vary - * to much. - */ - void set_h (const double h); + /** + * Takes the difference step size + * h. It's within the user's + * responsibility to choose an + * appropriate value here. h + * should be chosen taking into + * account the absolute value of + * as well as the amount of local + * variation of the function. + * Setting h=1e-6 might be a + * good choice for functions with + * an absolute value of about 1, + * that furthermore does not vary + * to much. + */ + void set_h (const double h); - /** - * Return the gradient of the - * specified component of the - * function at the given point. - * - * Computes numerical difference - * quotients using the preset - * #DifferenceFormula. - */ - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; + /** + * Return the gradient of the + * specified component of the + * function at the given point. + * + * Computes numerical difference + * quotients using the preset + * #DifferenceFormula. + */ + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; - /** - * Return the gradient of all - * components of the - * function at the given point. - * - * Computes numerical difference - * quotients using the preset - * #DifferenceFormula. - */ - virtual void vector_gradient (const Point &p, - std::vector > &gradients) const; + /** + * Return the gradient of all + * components of the + * function at the given point. + * + * Computes numerical difference + * quotients using the preset + * #DifferenceFormula. + */ + virtual void vector_gradient (const Point &p, + std::vector > &gradients) const; - /** - * Set gradients to the - * gradients of the specified - * component of the function at - * the points. It is assumed - * that gradients already has the - * right size, i.e. the same - * size as the points array. - * - * Computes numerical difference - * quotients using the preset - * #DifferenceFormula. - */ - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; + /** + * Set gradients to the + * gradients of the specified + * component of the function at + * the points. It is assumed + * that gradients already has the + * right size, i.e. the same + * size as the points array. + * + * Computes numerical difference + * quotients using the preset + * #DifferenceFormula. + */ + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; - /** - * Set gradients to the gradients of - * the function at the points, - * for all components. - * It is assumed that gradients - * already has the right size, i.e. - * the same size as the points array. - * - * The outer loop over - * gradients is over the points - * in the list, the inner loop - * over the different components - * of the function. - * - * Computes numerical difference - * quotients using the preset - * #DifferenceFormula. - */ - virtual void vector_gradient_list (const std::vector > &points, - std::vector > > &gradients) const; + /** + * Set gradients to the gradients of + * the function at the points, + * for all components. + * It is assumed that gradients + * already has the right size, i.e. + * the same size as the points array. + * + * The outer loop over + * gradients is over the points + * in the list, the inner loop + * over the different components + * of the function. + * + * Computes numerical difference + * quotients using the preset + * #DifferenceFormula. + */ + virtual void vector_gradient_list (const std::vector > &points, + std::vector > > &gradients) const; - /** - * Returns a - * #DifferenceFormula of the - * order ord at minimum. - */ - static - DifferenceFormula - get_formula_of_order (const unsigned int ord); + /** + * Returns a + * #DifferenceFormula of the + * order ord at minimum. + */ + static + DifferenceFormula + get_formula_of_order (const unsigned int ord); - /** - * Exception. - */ - DeclException0(ExcInvalidFormula); + /** + * Exception. + */ + DeclException0(ExcInvalidFormula); - private: +private: - /** - * Step size of the difference - * formula. Set by the set_h() - * function. - */ - double h; + /** + * Step size of the difference + * formula. Set by the set_h() + * function. + */ + double h; - /** - * Includes the unit vectors - * scaled by h. - */ - std::vector > ht; + /** + * Includes the unit vectors + * scaled by h. + */ + std::vector > ht; - /** - * Difference formula. Set by the - * set_formula() function. - */ - DifferenceFormula formula; + /** + * Difference formula. Set by the + * set_formula() function. + */ + DifferenceFormula formula; }; diff --git a/deal.II/include/deal.II/base/conditional_ostream.h b/deal.II/include/deal.II/base/conditional_ostream.h index a4c799f48b..753ade963e 100644 --- a/deal.II/include/deal.II/base/conditional_ostream.h +++ b/deal.II/include/deal.II/base/conditional_ostream.h @@ -80,81 +80,81 @@ DEAL_II_NAMESPACE_OPEN */ class ConditionalOStream { - public: - /** - * Constructor. Set the stream to which - * we want to write, and the condition - * based on which writes are actually - * forwarded. Per default the condition - * of an object is active. - */ - ConditionalOStream (std::ostream &stream, - const bool active = true); - - /** - * Depending on the - * active flag set the - * condition of this stream to - * active (true) or non-active - * (false). An object of this - * class prints to cout - * if and only if its condition - * is active. - */ - void set_condition (const bool active); - - /** - * Return the condition of the object. - */ - bool is_active() const; - - /** - * Return a reference to the stream - * currently in use. - */ - std::ostream & get_stream () const; - - /** - * Output a constant something through - * this stream. This function must be @p - * const so that member objects of this - * type can also be used from @p const - * member functions of the surrounding - * class. - */ - template - const ConditionalOStream & - operator << (const T &t) const; - - /** - * Treat ostream manipulators. This - * function must be @p const so that - * member objects of this type can also - * be used from @p const member functions - * of the surrounding class. - * - * Note that compilers want to see this - * treated differently from the general - * template above since functions like @p - * std::endl are actually overloaded and - * can't be bound directly to a template - * type. - */ - const ConditionalOStream & - operator<< (std::ostream& (*p) (std::ostream&)) const; - - private: - /** - * Reference to the stream we - * want to write to. - */ - std::ostream &output_stream; - - /** - * Stores the actual condition - * the object is in. - */ - bool active_flag; +public: + /** + * Constructor. Set the stream to which + * we want to write, and the condition + * based on which writes are actually + * forwarded. Per default the condition + * of an object is active. + */ + ConditionalOStream (std::ostream &stream, + const bool active = true); + + /** + * Depending on the + * active flag set the + * condition of this stream to + * active (true) or non-active + * (false). An object of this + * class prints to cout + * if and only if its condition + * is active. + */ + void set_condition (const bool active); + + /** + * Return the condition of the object. + */ + bool is_active() const; + + /** + * Return a reference to the stream + * currently in use. + */ + std::ostream &get_stream () const; + + /** + * Output a constant something through + * this stream. This function must be @p + * const so that member objects of this + * type can also be used from @p const + * member functions of the surrounding + * class. + */ + template + const ConditionalOStream & + operator << (const T &t) const; + + /** + * Treat ostream manipulators. This + * function must be @p const so that + * member objects of this type can also + * be used from @p const member functions + * of the surrounding class. + * + * Note that compilers want to see this + * treated differently from the general + * template above since functions like @p + * std::endl are actually overloaded and + * can't be bound directly to a template + * type. + */ + const ConditionalOStream & + operator<< (std::ostream& (*p) (std::ostream &)) const; + +private: + /** + * Reference to the stream we + * want to write to. + */ + std::ostream &output_stream; + + /** + * Stores the actual condition + * the object is in. + */ + bool active_flag; }; @@ -163,7 +163,7 @@ class ConditionalOStream template inline const ConditionalOStream & -ConditionalOStream::operator<< (const T& t) const +ConditionalOStream::operator<< (const T &t) const { if (active_flag == true) output_stream << t; @@ -174,7 +174,7 @@ ConditionalOStream::operator<< (const T& t) const inline const ConditionalOStream & -ConditionalOStream::operator<< (std::ostream& (*p) (std::ostream&)) const +ConditionalOStream::operator<< (std::ostream& (*p) (std::ostream &)) const { if (active_flag == true) output_stream << p; diff --git a/deal.II/include/deal.II/base/convergence_table.h b/deal.II/include/deal.II/base/convergence_table.h index 4d6f984cec..f7daef6008 100644 --- a/deal.II/include/deal.II/base/convergence_table.h +++ b/deal.II/include/deal.II/base/convergence_table.h @@ -57,209 +57,210 @@ DEAL_II_NAMESPACE_OPEN */ class ConvergenceTable: public TableHandler { - public: - /** - * Constructor. - */ - ConvergenceTable(); +public: + /** + * Constructor. + */ + ConvergenceTable(); - /** - * Rate in relation to the rows. - */ - enum RateMode { - /** - * Do not do anything. - */ - none, - /** - * Quotient of values in - * the previous row and in - * this row. - */ - reduction_rate, - /** - * Logarithm of - * #reduction_rate to the - * base 2 representing the - * order of convergence - * when halving the grid - * size, e.g. from h to - * h/2. - */ - reduction_rate_log2 - }; + /** + * Rate in relation to the rows. + */ + enum RateMode + { + /** + * Do not do anything. + */ + none, + /** + * Quotient of values in + * the previous row and in + * this row. + */ + reduction_rate, + /** + * Logarithm of + * #reduction_rate to the + * base 2 representing the + * order of convergence + * when halving the grid + * size, e.g. from h to + * h/2. + */ + reduction_rate_log2 + }; - /** - * Evaluates the convergence rates of the - * data column data_column_key - * due to the #RateMode in relation to - * the reference column reference_column_key. - * Be sure that the value types of the - * table entries of the - * data column and the reference data column - * is a number, i.e. double, float, - * (unsigned) int, and so on. - * - * The new rate column and the data column - * will be merged to a supercolumn. The - * tex caption of the supercolumn will be - * (by default) the same as the one of the - * data column. This may be changed by using - * the set_tex_supercaption (...) function - * of the base class TableHandler. - * - * This method behaves in the following way: - * - * If RateMode is reduction_rate, then the computed - * output is - * $ \frac{e_{n-1}/k_{n-1}}{e_n/k_n} $. - * - * Where $k$ is the reference column. - * - * If RateMode is reduction_rate_log2, then the - * computed output is - * $ - * 2\frac{\log |e_{n-1}/e_{n}|}{\log |k_n/k_{n-1}|} - * $. - * - * This is useful, for example, if we use as - * reference key the number of degrees of freedom. - * Assuming that the error is proportional to - * $ C (1/\sqrt{k})^r $, then this method will - * produce the rate $r$ as a result. - * - * @note Since this function adds columns - * to the table after several rows have - * already been filled, it switches off - * the auto fill mode of the TableHandler - * base class. If you intend to add - * further data with auto fill, you will - * have to re-enable it after calling - * this function. - */ - void - evaluate_convergence_rates (const std::string &data_column_key, - const std::string &reference_column_key, - const RateMode rate_mode); + /** + * Evaluates the convergence rates of the + * data column data_column_key + * due to the #RateMode in relation to + * the reference column reference_column_key. + * Be sure that the value types of the + * table entries of the + * data column and the reference data column + * is a number, i.e. double, float, + * (unsigned) int, and so on. + * + * The new rate column and the data column + * will be merged to a supercolumn. The + * tex caption of the supercolumn will be + * (by default) the same as the one of the + * data column. This may be changed by using + * the set_tex_supercaption (...) function + * of the base class TableHandler. + * + * This method behaves in the following way: + * + * If RateMode is reduction_rate, then the computed + * output is + * $ \frac{e_{n-1}/k_{n-1}}{e_n/k_n} $. + * + * Where $k$ is the reference column. + * + * If RateMode is reduction_rate_log2, then the + * computed output is + * $ + * 2\frac{\log |e_{n-1}/e_{n}|}{\log |k_n/k_{n-1}|} + * $. + * + * This is useful, for example, if we use as + * reference key the number of degrees of freedom. + * Assuming that the error is proportional to + * $ C (1/\sqrt{k})^r $, then this method will + * produce the rate $r$ as a result. + * + * @note Since this function adds columns + * to the table after several rows have + * already been filled, it switches off + * the auto fill mode of the TableHandler + * base class. If you intend to add + * further data with auto fill, you will + * have to re-enable it after calling + * this function. + */ + void + evaluate_convergence_rates (const std::string &data_column_key, + const std::string &reference_column_key, + const RateMode rate_mode); - /** - * Evaluates the convergence rates of the - * data column data_column_key - * due to the #RateMode. - * Be sure that the value types of the - * table entries of the data column - * is a number, i.e. double, float, - * (unsigned) int, and so on. - * - * The new rate column and the data column - * will be merged to a supercolumn. The - * tex caption of the supercolumn will be - * (by default) the same as the one of the - * data column. This may be changed by using - * the set_tex_supercaption() function - * of the base class TableHandler. - * - * @note Since this function adds columns - * to the table after several rows have - * already been filled, it switches off - * the auto fill mode of the TableHandler - * base class. If you intend to add - * further data with auto fill, you will - * have to re-enable it after calling - * this function. - */ - void - evaluate_convergence_rates (const std::string &data_column_key, - const RateMode rate_mode); + /** + * Evaluates the convergence rates of the + * data column data_column_key + * due to the #RateMode. + * Be sure that the value types of the + * table entries of the data column + * is a number, i.e. double, float, + * (unsigned) int, and so on. + * + * The new rate column and the data column + * will be merged to a supercolumn. The + * tex caption of the supercolumn will be + * (by default) the same as the one of the + * data column. This may be changed by using + * the set_tex_supercaption() function + * of the base class TableHandler. + * + * @note Since this function adds columns + * to the table after several rows have + * already been filled, it switches off + * the auto fill mode of the TableHandler + * base class. If you intend to add + * further data with auto fill, you will + * have to re-enable it after calling + * this function. + */ + void + evaluate_convergence_rates (const std::string &data_column_key, + const RateMode rate_mode); - /** - * Omit this column key - * (not supercolumn!) from the - * evaluation of the convergence rates - * of `all' columns (see the following - * two functions). - * - * The Column::flag==1 is reserved for - * omitting the column from convergence - * rate evalution. - */ - void - omit_column_from_convergence_rate_evaluation(const std::string &key); + /** + * Omit this column key + * (not supercolumn!) from the + * evaluation of the convergence rates + * of `all' columns (see the following + * two functions). + * + * The Column::flag==1 is reserved for + * omitting the column from convergence + * rate evalution. + */ + void + omit_column_from_convergence_rate_evaluation(const std::string &key); - /** - * Evaluates convergence rates - * due to the rate_mode - * in relation to the reference - * column - * reference_column_key. This - * function evaluates the rates - * of ALL columns except of the - * columns that are to be omitted - * (see previous function) and - * execpt of the columns that are - * previously evaluated rate - * columns. This function allows - * to evaluate the convergence - * rate for almost all columns of - * a table without calling - * evaluate_convergence_rates() - * for each column separately. - * - * Example: - * Columns like n cells or - * n dofs columns may be wanted - * to be omitted in the evaluation - * of the convergence rates. Hence they - * should omitted by calling the - * omit_column_from_convergence_rate_evaluation(). - */ - void - evaluate_all_convergence_rates(const std::string &reference_column_key, - const RateMode rate_mode); + /** + * Evaluates convergence rates + * due to the rate_mode + * in relation to the reference + * column + * reference_column_key. This + * function evaluates the rates + * of ALL columns except of the + * columns that are to be omitted + * (see previous function) and + * execpt of the columns that are + * previously evaluated rate + * columns. This function allows + * to evaluate the convergence + * rate for almost all columns of + * a table without calling + * evaluate_convergence_rates() + * for each column separately. + * + * Example: + * Columns like n cells or + * n dofs columns may be wanted + * to be omitted in the evaluation + * of the convergence rates. Hence they + * should omitted by calling the + * omit_column_from_convergence_rate_evaluation(). + */ + void + evaluate_all_convergence_rates(const std::string &reference_column_key, + const RateMode rate_mode); - /** - * Evaluates convergence rates - * due to the rate_mode. This - * function evaluates the rates of - * ALL columns except of the - * columns that are to be omitted - * (see previous function) - * and execpt of the columns that are - * previously - * evaluated rate columns. - * This function allows to evaluate - * the convergence rate for almost all - * columns of a table without calling - * evaluate_convergence_rates() - * for each column seperately. - * - * Example: - * Columns like n cells or - * n dofs columns may be wanted - * to be omitted in the evaluation - * of the convergence rates. Hence they - * should omitted by calling the - * omit_column_from_convergence_rate_evaluation(). - */ - void - evaluate_all_convergence_rates(const RateMode rate_mode); + /** + * Evaluates convergence rates + * due to the rate_mode. This + * function evaluates the rates of + * ALL columns except of the + * columns that are to be omitted + * (see previous function) + * and execpt of the columns that are + * previously + * evaluated rate columns. + * This function allows to evaluate + * the convergence rate for almost all + * columns of a table without calling + * evaluate_convergence_rates() + * for each column seperately. + * + * Example: + * Columns like n cells or + * n dofs columns may be wanted + * to be omitted in the evaluation + * of the convergence rates. Hence they + * should omitted by calling the + * omit_column_from_convergence_rate_evaluation(). + */ + void + evaluate_all_convergence_rates(const RateMode rate_mode); - /** @addtogroup Exceptions - * @{ */ + /** @addtogroup Exceptions + * @{ */ - /** - * Exception - */ - DeclException0 (ExcWrongValueType); + /** + * Exception + */ + DeclException0 (ExcWrongValueType); - /** - * Exception - */ - DeclException1 (ExcRateColumnAlreadyExists, - std::string, - << "Rate column <" << arg1 << "> does already exist."); - //@} + /** + * Exception + */ + DeclException1 (ExcRateColumnAlreadyExists, + std::string, + << "Rate column <" << arg1 << "> does already exist."); + //@} }; diff --git a/deal.II/include/deal.II/base/data_out_base.h b/deal.II/include/deal.II/base/data_out_base.h index b42e689aa9..a8124528fc 100644 --- a/deal.II/include/deal.II/base/data_out_base.h +++ b/deal.II/include/deal.II/base/data_out_base.h @@ -211,1848 +211,1849 @@ class XDMFEntry; */ class DataOutBase { +public: + /** + * Data structure describing a patch of + * data in dim space + * dimensions. + * + * A patch consists of the following data: + *
    + *
  • the corner #vertices, + *
  • the number + * #n_subdivisions of the number + * of cells the Patch has in each + * space direction, + *
  • the #data attached to + * each vertex, in the usual + * lexicographic ordering, + *
  • Information on #neighbors. + *
+ * + * See the general + * documentation of the + * DataOutBase class for more + * information on its contents and + * purposes. In the case of two + * dimensions, the next picture ist an + * example of n_subdivision = 4 + * because the number of (sub)cells + * within each patch is equal to + * 2^dim. + * + * @ingroup output + * + * @author Wolfgang Bangerth, Guido Kanschat + */ + template + struct Patch + { + /** + * Make the spacedim template + * parameter available. + */ + static const unsigned int space_dim=spacedim; + + /** + * Corner points of a patch. + * Inner points are computed by + * a multilinear transform of + * the unit cell to the cell + * specified by these corner + * points. The order of points + * is the same as for cells + * in the triangulation. + */ + Point vertices[GeometryInfo::vertices_per_cell]; + + /** + * Numbers of neighbors of a patch. + * OpenDX format requires + * neighbor information for + * advanced output. Here the + * neighborship relationship + * of patches is + * stored. During output, + * this must be transformed + * into neighborship of + * sub-grid cells. + */ + unsigned int neighbors[dim > 0 + ? + GeometryInfo::faces_per_cell + : + 1]; + + /** + * Number of this + * patch. Since we are not + * sure patches are handled + * in the same order, always, + * we better store this. + */ + unsigned int patch_index; + + /** + * Number of subdivisions with + * which this patch is to be + * written. 1 means no + * subdivision, 2 means + * bisection, 3 trisection, + * etc. + */ + unsigned int n_subdivisions; + + /** + * Data vectors. The format is + * as follows: + * data(i,.) denotes the data + * belonging to the ith data + * vector. data.n() + * therefore equals the number + * of output points; this + * number is (subdivisions+1)^{dim}. + * data.m() equals the number of + * data vectors. + * + * Within each column, + * data(.,j) are the + * data values at the output + * point j, where + * j denotes the + * usual lexicographic + * ordering in deal.II. This + * is also the order of + * points as provided by the + * QIterated class + * when used with the + * QTrapez class as + * subquadrature. + * + * Since the number of data vectors + * is usually the same for all + * patches to be printed, + * data.size() should yield + * the same value for all patches + * provided. The exception are + * patches for which + * points_are_available are set, + * where the actual coordinates of + * the point are appended to the + * 'data' field, see the + * documentation of the + * points_are_available flag. + */ + Table<2,float> data; + + /** + * Bool flag indicating, whether the + * coordinates of the inner patch + * points are appended to the @p data + * table (@ true) or not (@ false), + * where the second is the standard and + * can be found for all cells in the + * interior of a domain. + * + * On the boundary of a domain, patch + * points are evaluated using a + * Mapping and therefore have to be + * stored inside the patch, as the + * Mapping and the corresponding + * boundary information are no longer + * available later on when we + * actually write the patch out to an + * output stream. + */ + bool points_are_available; + + /** + * Default constructor. Sets + * n_subdivisions to one. + */ + Patch (); + + /** + * Compare the present patch + * for equality with another + * one. This is used in a few + * of the automated tests in + * our testsuite. + */ + bool operator == (const Patch &patch) const; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + + /** + * Value to be used if this + * patch has no neighbor on + * one side. + */ + static const unsigned int no_neighbor = numbers::invalid_unsigned_int; + /** @addtogroup Exceptions + * @{ */ + + /** + * Exception + */ + DeclException2 (ExcInvalidCombinationOfDimensions, + int, int, + << "It is not possible to have a structural dimension of " << arg1 + << " to be larger than the space dimension of the surrounding" + << " space " << arg2); + //@} + }; + + /** + * Flags controlling the details of + * output in OpenDX format. + * + * @ingroup output + */ + struct DXFlags + { + /** + * Write neighbor + * information. This + * information is necessary + * for instance, if OpenDX is + * supposed to compute + * integral curves + * (streamlines). If it is + * not present, streamlines + * end at cell boundaries. + */ + bool write_neighbors; + /** + * Write integer values of + * the Triangulation in + * binary format. + */ + bool int_binary; + /** + * Write coordinate vectors in + * binary format. + */ + bool coordinates_binary; + + /** + * Write data vectors in + * binary format. + */ + bool data_binary; + + /** + * Write binary coordinate + * vectors as double (64 bit) + * numbers instead of float (32 bit). + */ + bool data_double; + + /** + * Constructor. + */ + DXFlags (const bool write_neighbors = false, + const bool int_binary = false, + const bool coordinates_binary = false, + const bool data_binary = false); + + /** + * Declare all flags with name + * and type as offered by this + * class, for use in input files. + */ + static void declare_parameters (ParameterHandler &prm); + + /** + * Read the parameters declared in + * declare_parameters and set the + * flags for this output format + * accordingly. + * + * The flags thus obtained overwrite + * all previous contents of this object. + */ + void parse_parameters (const ParameterHandler &prm); + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. + */ + std::size_t memory_consumption () const; + }; + + /** + * Flags controlling the details + * of output in UCD format for + * AVS. + * + * @ingroup output + */ + struct UcdFlags + { + /** + * Write a comment at the + * beginning of the file + * stating the date of + * creation and some other + * data. While this is + * supported by the UCD + * format and + * AVS, some other + * programs get confused by + * this, so the default is to + * not write a + * preamble. However, a + * preamble can be written + * using this flag. + * + * Default: false. + */ + bool write_preamble; + + /** + * Constructor. + */ + UcdFlags (const bool write_preamble = false); + + /** + * Declare all flags with name + * and type as offered by this + * class, for use in input files. + */ + static void declare_parameters (ParameterHandler &prm); + + /** + * Read the parameters declared in + * declare_parameters and set the + * flags for this output format + * accordingly. + * + * The flags thus obtained overwrite + * all previous contents of this object. + */ + void parse_parameters (const ParameterHandler &prm); + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + }; + + /** + * Flags controlling the details of + * output in Gnuplot format. At + * present no flags are implemented. + * + * @ingroup output + */ + struct GnuplotFlags + { + private: + /** + * Dummy entry to suppress compiler + * warnings when copying an empty + * structure. Remove this member + * when adding the first flag to + * this structure (and remove the + * private as well). + */ + int dummy; + public: - /** - * Data structure describing a patch of - * data in dim space - * dimensions. - * - * A patch consists of the following data: - *
    - *
  • the corner #vertices, - *
  • the number - * #n_subdivisions of the number - * of cells the Patch has in each - * space direction, - *
  • the #data attached to - * each vertex, in the usual - * lexicographic ordering, - *
  • Information on #neighbors. - *
- * - * See the general - * documentation of the - * DataOutBase class for more - * information on its contents and - * purposes. In the case of two - * dimensions, the next picture ist an - * example of n_subdivision = 4 - * because the number of (sub)cells - * within each patch is equal to - * 2^dim. - * - * @ingroup output - * - * @author Wolfgang Bangerth, Guido Kanschat - */ - template - struct Patch - { - /** - * Make the spacedim template - * parameter available. - */ - static const unsigned int space_dim=spacedim; - - /** - * Corner points of a patch. - * Inner points are computed by - * a multilinear transform of - * the unit cell to the cell - * specified by these corner - * points. The order of points - * is the same as for cells - * in the triangulation. - */ - Point vertices[GeometryInfo::vertices_per_cell]; - - /** - * Numbers of neighbors of a patch. - * OpenDX format requires - * neighbor information for - * advanced output. Here the - * neighborship relationship - * of patches is - * stored. During output, - * this must be transformed - * into neighborship of - * sub-grid cells. - */ - unsigned int neighbors[dim > 0 - ? - GeometryInfo::faces_per_cell - : - 1]; - - /** - * Number of this - * patch. Since we are not - * sure patches are handled - * in the same order, always, - * we better store this. - */ - unsigned int patch_index; - - /** - * Number of subdivisions with - * which this patch is to be - * written. 1 means no - * subdivision, 2 means - * bisection, 3 trisection, - * etc. - */ - unsigned int n_subdivisions; - - /** - * Data vectors. The format is - * as follows: - * data(i,.) denotes the data - * belonging to the ith data - * vector. data.n() - * therefore equals the number - * of output points; this - * number is (subdivisions+1)^{dim}. - * data.m() equals the number of - * data vectors. - * - * Within each column, - * data(.,j) are the - * data values at the output - * point j, where - * j denotes the - * usual lexicographic - * ordering in deal.II. This - * is also the order of - * points as provided by the - * QIterated class - * when used with the - * QTrapez class as - * subquadrature. - * - * Since the number of data vectors - * is usually the same for all - * patches to be printed, - * data.size() should yield - * the same value for all patches - * provided. The exception are - * patches for which - * points_are_available are set, - * where the actual coordinates of - * the point are appended to the - * 'data' field, see the - * documentation of the - * points_are_available flag. - */ - Table<2,float> data; - - /** - * Bool flag indicating, whether the - * coordinates of the inner patch - * points are appended to the @p data - * table (@ true) or not (@ false), - * where the second is the standard and - * can be found for all cells in the - * interior of a domain. - * - * On the boundary of a domain, patch - * points are evaluated using a - * Mapping and therefore have to be - * stored inside the patch, as the - * Mapping and the corresponding - * boundary information are no longer - * available later on when we - * actually write the patch out to an - * output stream. - */ - bool points_are_available; - - /** - * Default constructor. Sets - * n_subdivisions to one. - */ - Patch (); - - /** - * Compare the present patch - * for equality with another - * one. This is used in a few - * of the automated tests in - * our testsuite. - */ - bool operator == (const Patch &patch) const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - - /** - * Value to be used if this - * patch has no neighbor on - * one side. - */ - static const unsigned int no_neighbor = numbers::invalid_unsigned_int; - /** @addtogroup Exceptions - * @{ */ - - /** - * Exception - */ - DeclException2 (ExcInvalidCombinationOfDimensions, - int, int, - << "It is not possible to have a structural dimension of " << arg1 - << " to be larger than the space dimension of the surrounding" - << " space " << arg2); - //@} - }; + /** + * Constructor. + */ + GnuplotFlags (); - /** - * Flags controlling the details of - * output in OpenDX format. - * - * @ingroup output - */ - struct DXFlags - { - /** - * Write neighbor - * information. This - * information is necessary - * for instance, if OpenDX is - * supposed to compute - * integral curves - * (streamlines). If it is - * not present, streamlines - * end at cell boundaries. - */ - bool write_neighbors; - /** - * Write integer values of - * the Triangulation in - * binary format. - */ - bool int_binary; - /** - * Write coordinate vectors in - * binary format. - */ - bool coordinates_binary; - - /** - * Write data vectors in - * binary format. - */ - bool data_binary; - - /** - * Write binary coordinate - * vectors as double (64 bit) - * numbers instead of float (32 bit). - */ - bool data_double; - - /** - * Constructor. - */ - DXFlags (const bool write_neighbors = false, - const bool int_binary = false, - const bool coordinates_binary = false, - const bool data_binary = false); - - /** - * Declare all flags with name - * and type as offered by this - * class, for use in input files. - */ - static void declare_parameters (ParameterHandler &prm); - - /** - * Read the parameters declared in - * declare_parameters and set the - * flags for this output format - * accordingly. - * - * The flags thus obtained overwrite - * all previous contents of this object. - */ - void parse_parameters (const ParameterHandler &prm); - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. - */ - std::size_t memory_consumption () const; - }; + /** + * Declare all flags with name + * and type as offered by this + * class, for use in input files. + */ + static void declare_parameters (ParameterHandler &prm); - /** - * Flags controlling the details - * of output in UCD format for - * AVS. - * - * @ingroup output - */ - struct UcdFlags - { - /** - * Write a comment at the - * beginning of the file - * stating the date of - * creation and some other - * data. While this is - * supported by the UCD - * format and - * AVS, some other - * programs get confused by - * this, so the default is to - * not write a - * preamble. However, a - * preamble can be written - * using this flag. - * - * Default: false. - */ - bool write_preamble; - - /** - * Constructor. - */ - UcdFlags (const bool write_preamble = false); - - /** - * Declare all flags with name - * and type as offered by this - * class, for use in input files. - */ - static void declare_parameters (ParameterHandler &prm); - - /** - * Read the parameters declared in - * declare_parameters and set the - * flags for this output format - * accordingly. - * - * The flags thus obtained overwrite - * all previous contents of this object. - */ - void parse_parameters (const ParameterHandler &prm); - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - }; + /** + * Read the parameters declared in + * declare_parameters and set the + * flags for this output format + * accordingly. + * + * The flags thus obtained overwrite + * all previous contents of this object. + */ + void parse_parameters (const ParameterHandler &prm) const; - /** - * Flags controlling the details of - * output in Gnuplot format. At - * present no flags are implemented. - * - * @ingroup output - */ - struct GnuplotFlags - { - private: - /** - * Dummy entry to suppress compiler - * warnings when copying an empty - * structure. Remove this member - * when adding the first flag to - * this structure (and remove the - * private as well). - */ - int dummy; - - public: - /** - * Constructor. - */ - GnuplotFlags (); - - /** - * Declare all flags with name - * and type as offered by this - * class, for use in input files. - */ - static void declare_parameters (ParameterHandler &prm); - - /** - * Read the parameters declared in - * declare_parameters and set the - * flags for this output format - * accordingly. - * - * The flags thus obtained overwrite - * all previous contents of this object. - */ - void parse_parameters (const ParameterHandler &prm) const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - }; + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + }; + + /** + * Flags controlling the details + * of output in Povray + * format. Several flags are + * implemented, see their + * respective documentation. + * + * @ingroup output + */ + struct PovrayFlags + { + /** + * Normal vector interpolation, + * if set to true + * + * default = false + */ + bool smooth; - /** - * Flags controlling the details - * of output in Povray - * format. Several flags are - * implemented, see their - * respective documentation. - * - * @ingroup output - */ - struct PovrayFlags - { - /** - * Normal vector interpolation, - * if set to true - * - * default = false - */ - bool smooth; - - /** - * Use bicubic patches (b-splines) - * instead of triangles. - * - * default = false - */ - bool bicubic_patch; - - /** - * include external "data.inc" - * with camera, light and - * texture definition for the - * scene. - * - * default = false - */ - bool external_data; - - /** - * Constructor. - */ - PovrayFlags (const bool smooth = false, - const bool bicubic_patch = false, - const bool external_data = false); - - /** - * Declare all flags with name - * and type as offered by this - * class, for use in input files. - */ - static void declare_parameters (ParameterHandler &prm); - - /** - * Read the parameters declared in - * declare_parameters and set the - * flags for this output format - * accordingly. - * - * The flags thus obtained overwrite - * all previous contents of this object. - */ - void parse_parameters (const ParameterHandler &prm); - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - }; + /** + * Use bicubic patches (b-splines) + * instead of triangles. + * + * default = false + */ + bool bicubic_patch; + /** + * include external "data.inc" + * with camera, light and + * texture definition for the + * scene. + * + * default = false + */ + bool external_data; - /** - * Flags controlling the details of - * output in encapsulated postscript - * format. - * - * @ingroup output - */ - struct EpsFlags - { - /** - * This denotes the number of the - * data vector which shall be used - * for generating the height - * information. By default, the - * first data vector is taken, - * i.e. height_vector==0, if - * there is any data vector. If there - * is no data vector, no height - * information is generated. - */ - unsigned int height_vector; - - /** - * Number of the vector which is - * to be taken to colorize cells. - * The same applies as for - * height_vector. - */ - unsigned int color_vector; - - /** - * Enum denoting the possibilities - * whether the scaling should be done - * such that the given size equals - * the width or the height of - * the resulting picture. - */ - enum SizeType { - /// Scale to given width - width, - /// Scale to given height - height - }; - - /** - * See above. Default is width. - */ - SizeType size_type; - - /** - * Width or height of the output - * as given in postscript units - * This usually is given by the - * strange unit 1/72 inch. Whether - * this is height or width is - * specified by the flag - * size_type. - * - * Default is 300, which represents - * a size of roughly 10 cm. - */ - unsigned int size; - - /** - * Width of a line in postscript - * units. Default is 0.5. - */ - double line_width; - - /** - * Angle of the line origin-viewer - * against the z-axis in degrees. - * - * Default is the Gnuplot-default - * of 60. - */ - double azimut_angle; - - /** - * Angle by which the viewers - * position projected onto the - * x-y-plane is rotated around - * the z-axis, in positive sense - * when viewed from above. The - * unit are degrees, and zero - * equals a position above or below - * the negative y-axis. - * - * Default is the - * Gnuplot-default of 30. - * An example of a - * Gnuplot-default of 0 is - * the following: - * - * @verbatim - * - * 3________7 - * / /| - * / / | - * 2/______6/ | - * | | | | - * O--> | 0___|___4 - * | / | / - * | / | / - * 1|/______5/ - * - * @endverbatim - */ - double turn_angle; - - /** - * Factor by which the z-axis is to - * be stretched as compared to the - * x- and y-axes. This is to compensate - * for the different sizes that - * coordinate and solution values may - * have and to prevent that the plot - * looks to much out-of-place (no - * elevation at all if solution values - * are much smaller than coordinate - * values, or the common "extremely - * mountainous area" in the opposite - * case. - * - * Default is 1.0. - */ - double z_scaling; - - /** - * Flag the determines whether the - * lines bounding the cells (or the - * parts of each patch) are to be - * plotted. - * - * Default: true. - */ - bool draw_mesh; - - /** - * Flag whether to fill the regions - * between the lines bounding the cells - * or not. If not, no hidden line removal - * is performed, which in this crude - * implementation is done through - * writing the cells in a back-to-front - * order, thereby hiding the cells in - * the background by cells in the - * foreground. - * - * If this flag is false and draw_mesh - * is false as well, nothing will be - * printed. - * - * If this flag is true, then the cells - * will be drawn either colored by one - * of the data sets (if shade_cells is - * true), or pure white (if - * shade_cells is false or if there are - * no data sets). - * - * Default is true. - */ - bool draw_cells; - - /** - * Flag to determine whether the cells - * shall be colorized by the data - * set denoted by color_vector, or - * simply be painted in white. This - * flag only makes sense if - * draw_cells==true. Colorization is - * done through the color_function. - * - * Default is true. - */ - bool shade_cells; - - /** - * Structure keeping the three color - * values in the RGB system. - */ - struct RgbValues - { - float red; - float green; - float blue; - - /** - * Return true if the - * color represented by - * the three color values - * is a grey scale, - * i.e. all components - * are equal. - */ - bool is_grey () const; - }; - - /** - * Definition of a function pointer - * type taking a value and returning - * a triple of color values in RGB - * values. - * - * Besides the actual value by which - * the color is to be computed, min - * and max values of the data to - * be colorized are given as well. - */ - typedef RgbValues (*ColorFunction) (const double value, - const double min_value, - const double max_value); - - /** - * This is a pointer to the function - * which is used to colorize the cells. - * By default, it points to the - * static function default_color_function - * which is a member of this class. - */ - ColorFunction color_function; - - - /** - * Default colorization function. This - * one does what one usually wants: - * It shifts colors from black (lowest - * value) through blue, green and red - * to white (highest value). For the - * exact defition of the color scale - * refer to the implementation. - * - * This function was originally written - * by Stefan Nauber. - */ - static RgbValues - default_color_function (const double value, - const double min_value, - const double max_value); - - /** - * This is an alternative color - * function producing a grey scale - * between black (lowest values) - * and white (highest values). You - * may use it by setting the - * color_function variable to the - * address of this function. - */ - static RgbValues - grey_scale_color_function (const double value, - const double min_value, - const double max_value); - - /** - * This is one more - * alternative color function - * producing a grey scale - * between white (lowest - * values) and black (highest - * values), i.e. the scale is - * reversed to the previous - * one. You may use it by - * setting the - * color_function - * variable to the address of - * this function. - */ - static RgbValues - reverse_grey_scale_color_function (const double value, - const double min_value, - const double max_value); - - /** - * Constructor. - */ - EpsFlags (const unsigned int height_vector = 0, - const unsigned int color_vector = 0, - const SizeType size_type = width, - const unsigned int size = 300, - const double line_width = 0.5, - const double azimut_angle = 60, - const double turn_angle = 30, - const double z_scaling = 1.0, - const bool draw_mesh = true, - const bool draw_cells = true, - const bool shade_cells = true, - const ColorFunction color_function= &default_color_function); - - /** - * Declare all flags with name - * and type as offered by this - * class, for use in input files. - * - * For coloring, only the color - * functions declared in this - * class are offered. - */ - static void declare_parameters (ParameterHandler &prm); - - /** - * Read the parameters declared in - * declare_parameters and set the - * flags for this output format - * accordingly. - * - * The flags thus obtained overwrite - * all previous contents of this object. - */ - void parse_parameters (const ParameterHandler &prm); - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - }; + /** + * Constructor. + */ + PovrayFlags (const bool smooth = false, + const bool bicubic_patch = false, + const bool external_data = false); - /** - * Flags controlling the details - * of output in GMV - * format. At present no flags - * are implemented. - * - * @ingroup output - */ - struct GmvFlags - { - private: - /** - * Dummy entry to suppress compiler - * warnings when copying an empty - * structure. Remove this member - * when adding the first flag to - * this structure (and remove the - * private as well). - */ - int dummy; - - public: - /** - * Default constructor. - */ - GmvFlags (); - - /** - * Declare all flags with name - * and type as offered by this - * class, for use in input files. - */ - static void declare_parameters (ParameterHandler &prm); - - /** - * Read the parameters declared in - * declare_parameters and set the - * flags for this output format - * accordingly. - * - * The flags thus obtained overwrite - * all previous contents of this object. - */ - void parse_parameters (const ParameterHandler &prm) const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - }; + /** + * Declare all flags with name + * and type as offered by this + * class, for use in input files. + */ + static void declare_parameters (ParameterHandler &prm); - /** - * Flags controlling the details - * of output in - * Tecplot format. - * - * @ingroup output - */ - struct TecplotFlags - { + /** + * Read the parameters declared in + * declare_parameters and set the + * flags for this output format + * accordingly. + * + * The flags thus obtained overwrite + * all previous contents of this object. + */ + void parse_parameters (const ParameterHandler &prm); - public: - - /** - * This variable is needed to hold the - * output file name when using the - * Tecplot API to write binary files. - * If the user doesn't set the file - * name with this variable only - * ASCII Tecplot output will be - * produced. - */ - const char* tecplot_binary_file_name; - - /** - * Tecplot allows to assign - * names to zones. This - * variable stores this name. - */ - const char* zone_name; - - /** - * Constructor - **/ - TecplotFlags (const char* tecplot_binary_file_name = NULL, - const char* zone_name = NULL); - - /** - * Declare all flags with name - * and type as offered by this - * class, for use in input files. - */ - static void declare_parameters (ParameterHandler &prm); - - /** - * Read the parameters declared in - * declare_parameters and set the - * flags for this output format - * accordingly. - * - * The flags thus obtained overwrite - * all previous contents of this object. - */ - void parse_parameters (const ParameterHandler &prm) const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - }; + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + }; + + + /** + * Flags controlling the details of + * output in encapsulated postscript + * format. + * + * @ingroup output + */ + struct EpsFlags + { + /** + * This denotes the number of the + * data vector which shall be used + * for generating the height + * information. By default, the + * first data vector is taken, + * i.e. height_vector==0, if + * there is any data vector. If there + * is no data vector, no height + * information is generated. + */ + unsigned int height_vector; - /** - * Flags controlling the details - * of output in VTK - * format. At present no flags - * are implemented. - * - * @ingroup output - */ - struct VtkFlags - { - private: - /** - * Dummy entry to suppress compiler - * warnings when copying an empty - * structure. Remove this member - * when adding the first flag to - * this structure (and remove the - * private as well). - */ - int dummy; - - public: - /** - * Default constructor. - */ - VtkFlags (); - - /** - * Declare all flags with name - * and type as offered by this - * class, for use in input files. - */ - static void declare_parameters (ParameterHandler &prm); - - /** - * Read the parameters declared in - * declare_parameters and set the - * flags for this output format - * accordingly. - * - * The flags thus obtained overwrite - * all previous contents of this object. - */ - void parse_parameters (const ParameterHandler &prm) const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - }; - /** - * Flags controlling the details - * of output in deal.II - * intermediate format. At - * present no flags are - * implemented. - * - * @ingroup output - */ - struct Deal_II_IntermediateFlags + /** + * Number of the vector which is + * to be taken to colorize cells. + * The same applies as for + * height_vector. + */ + unsigned int color_vector; + + /** + * Enum denoting the possibilities + * whether the scaling should be done + * such that the given size equals + * the width or the height of + * the resulting picture. + */ + enum SizeType { - /** - * An indicator of the - * currect file format - * version used to write - * intermediate format. We do - * not attempt to be backward - * compatible, so this number - * is used only to verify - * that the format we are - * writing is what the - * current readers and - * writers understand. - */ - static const unsigned int format_version = 3; - private: - /** - * Dummy entry to suppress compiler - * warnings when copying an empty - * structure. Remove this member - * when adding the first flag to - * this structure (and remove the - * private as well). - */ - int dummy; - - public: - /** - * Constructor. - */ - Deal_II_IntermediateFlags (); - - /** - * Declare all flags with name - * and type as offered by this - * class, for use in input files. - */ - static void declare_parameters (ParameterHandler &prm); - - /** - * Read the parameters declared in - * declare_parameters and set the - * flags for this output format - * accordingly. - * - * The flags thus obtained overwrite - * all previous contents of this object. - */ - void parse_parameters (const ParameterHandler &prm) const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; + /// Scale to given width + width, + /// Scale to given height + height }; - /** - * Provide a data type specifying - * the presently supported output - * formats. - */ - enum OutputFormat + /** + * See above. Default is width. + */ + SizeType size_type; + + /** + * Width or height of the output + * as given in postscript units + * This usually is given by the + * strange unit 1/72 inch. Whether + * this is height or width is + * specified by the flag + * size_type. + * + * Default is 300, which represents + * a size of roughly 10 cm. + */ + unsigned int size; + + /** + * Width of a line in postscript + * units. Default is 0.5. + */ + double line_width; + + /** + * Angle of the line origin-viewer + * against the z-axis in degrees. + * + * Default is the Gnuplot-default + * of 60. + */ + double azimut_angle; + + /** + * Angle by which the viewers + * position projected onto the + * x-y-plane is rotated around + * the z-axis, in positive sense + * when viewed from above. The + * unit are degrees, and zero + * equals a position above or below + * the negative y-axis. + * + * Default is the + * Gnuplot-default of 30. + * An example of a + * Gnuplot-default of 0 is + * the following: + * + * @verbatim + * + * 3________7 + * / /| + * / / | + * 2/______6/ | + * | | | | + * O--> | 0___|___4 + * | / | / + * | / | / + * 1|/______5/ + * + * @endverbatim + */ + double turn_angle; + + /** + * Factor by which the z-axis is to + * be stretched as compared to the + * x- and y-axes. This is to compensate + * for the different sizes that + * coordinate and solution values may + * have and to prevent that the plot + * looks to much out-of-place (no + * elevation at all if solution values + * are much smaller than coordinate + * values, or the common "extremely + * mountainous area" in the opposite + * case. + * + * Default is 1.0. + */ + double z_scaling; + + /** + * Flag the determines whether the + * lines bounding the cells (or the + * parts of each patch) are to be + * plotted. + * + * Default: true. + */ + bool draw_mesh; + + /** + * Flag whether to fill the regions + * between the lines bounding the cells + * or not. If not, no hidden line removal + * is performed, which in this crude + * implementation is done through + * writing the cells in a back-to-front + * order, thereby hiding the cells in + * the background by cells in the + * foreground. + * + * If this flag is false and draw_mesh + * is false as well, nothing will be + * printed. + * + * If this flag is true, then the cells + * will be drawn either colored by one + * of the data sets (if shade_cells is + * true), or pure white (if + * shade_cells is false or if there are + * no data sets). + * + * Default is true. + */ + bool draw_cells; + + /** + * Flag to determine whether the cells + * shall be colorized by the data + * set denoted by color_vector, or + * simply be painted in white. This + * flag only makes sense if + * draw_cells==true. Colorization is + * done through the color_function. + * + * Default is true. + */ + bool shade_cells; + + /** + * Structure keeping the three color + * values in the RGB system. + */ + struct RgbValues { - /** - * Use the format already - * stored in the object. - */ - default_format, - /** - * Do not write any output. - */ - none, - /** - * Output for OpenDX. - */ - dx, - /** - * Output in the UCD format - * for AVS. - */ - ucd, - /** - * Output for the - * Gnuplot tool. - */ - gnuplot, - /** - * Output for the - * Povray - * raytracer. - */ - povray, - /** - * Output in encapsulated - * PostScript. - */ - eps, - /** - * Output for GMV. - */ - gmv, - /** - * Output for - * Tecplot in text - * format. - */ - - tecplot, - /** - * Output for - * Tecplot in - * binary format. Faster - * and smaller than text - * format. - */ - tecplot_binary, - - /** - * Output in - * VTK format. - */ - vtk, - - /** - * Output in - * VTK format. - */ - vtu, - - /** - * Output in deal.II - * intermediate format. - */ - deal_II_intermediate, - - /** - * Output in - * HDF5 format. - */ - hdf5 + float red; + float green; + float blue; + + /** + * Return true if the + * color represented by + * the three color values + * is a grey scale, + * i.e. all components + * are equal. + */ + bool is_grey () const; }; + /** + * Definition of a function pointer + * type taking a value and returning + * a triple of color values in RGB + * values. + * + * Besides the actual value by which + * the color is to be computed, min + * and max values of the data to + * be colorized are given as well. + */ + typedef RgbValues (*ColorFunction) (const double value, + const double min_value, + const double max_value); -/** - * Write the given list of patches to the output stream in - * OpenDX format. - * - * Since OpenDX uses some kind of visual data flow oriented - * programming language, some of these programs are provided in - * contrib/dx. - */ - template - static void write_dx (const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const DXFlags &flags, - std::ostream &out); + /** + * This is a pointer to the function + * which is used to colorize the cells. + * By default, it points to the + * static function default_color_function + * which is a member of this class. + */ + ColorFunction color_function; -/** - * Write the given list of patches to the output stream in eps format. - * - * Output in this format circumvents the use of auxiliary graphic - * programs converting some output format into a graphics format. This - * has the advantage that output is easy and fast, and the - * disadvantage that you have to give a whole bunch of parameters - * which determine the direction of sight, the mode of colorization, - * the scaling of the height axis, etc. (Of course, all these - * parameters have reasonable default values, which you may want to - * change from time to time.) At present, this format only supports - * output for two-dimensional data, with values in the third direction - * taken from a data vector. - * - * Basically, output consists of the mesh and the cells in between - * them. You can draw either of these, or both, or none if you are - * really interested in an empty picture. If written, the mesh uses - * black lines. The cells in between the mesh are either not printed - * (this will result in a loss of hidden line removal, i.e. you can - * "see through" the cells to lines behind), printed in white (which - * does nothing apart from the hidden line removal), or colorized - * using one of the data vectors (which need not be the same as the - * one used for computing the height information) and a customizable - * color function. The default color functions chooses the color - * between black, blue, green, red and white, with growing values of - * the data field chosen for colorization. At present, cells are - * displayed with one color per cell only, which is taken from the - * value of the data field at the center of the cell; bilinear - * interpolation of the color on a cell is not used. - * - * By default, the viewpoint is chosen like the default viewpoint in - * GNUPLOT, i.e. with an angle of 60 degrees with respect to the - * positive z-axis and rotated 30 degrees in positive sense (as seen - * from above) away from the negative y-axis. Of course you can - * change these settings. - * - * EPS output is written without a border around the picture, i.e. the - * bounding box is close to the output on all four sides. Coordinates - * are written using at most five digits, to keep picture size at a - * reasonable size. - * - * All parameters along with their default values are listed in the - * documentation of the EpsFlags member class of this - * class. See there for more and detailed information. - */ - template - static void write_eps (const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const EpsFlags &flags, - std::ostream &out); -/** - * Write the given list of patches to the output stream in - * GMV format. - * - * Data is written in the following format: nodes are considered the - * points of the patches. In spatial dimensions less than three, - * zeroes are inserted for the missing coordinates. The data vectors - * are written as node or cell data, where for the first the data - * space is interpolated to (bi-,tri-)linear elements. - */ - template - static void write_gmv (const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const GmvFlags &flags, - std::ostream &out); + /** + * Default colorization function. This + * one does what one usually wants: + * It shifts colors from black (lowest + * value) through blue, green and red + * to white (highest value). For the + * exact defition of the color scale + * refer to the implementation. + * + * This function was originally written + * by Stefan Nauber. + */ + static RgbValues + default_color_function (const double value, + const double min_value, + const double max_value); -/** - * Write the given list of patches to the output stream in gnuplot - * format. Visualization of two-dimensional data can then be achieved by - * starting gnuplot and endtering the commands - * - * @verbatim - * set data style lines - * splot "filename" using 1:2:n - * @endverbatim - * This example assumes that the number of the data vector displayed - * is n-2. - * - * The GNUPLOT format is not able to handle data on unstructured grids - * directly. Directly would mean that you only give the vertices and - * the solution values thereon and the program constructs its own grid - * to represent the data. This is only possible for a structured - * tensor product grid in two dimensions. However, it is possible to - * give several such patches within one file, which is exactly what - * the respective function of this class does: writing each cell's - * data as a patch of data, at least if the patches as passed from - * derived classes represent cells. Note that the functions on patches - * need not be continuous at interfaces between patches, so this - * method also works for discontinuous elements. Note also, that - * GNUPLOT can do hidden line removal for patched data. - * - * While this discussion applies to two spatial dimensions, it is more - * complicated in 3d. The reason is that we could still use patches, - * but it is difficult when trying to visualize them, since if we use - * a cut through the data (by, for example, using x- and - * z-coordinates, a fixed y-value and plot function values in - * z-direction, then the patched data is not a patch in the sense - * GNUPLOT wants it any more. Therefore, we use another approach, - * namely writing the data on the 3d grid as a sequence of lines, - * i.e. two points each associated with one or more data sets. There - * are therefore 12 lines for each subcells of a patch. - * - * Given the lines as described above, a cut through this data in - * Gnuplot can then be achieved like this (& stands for the dollar - * sign in the following): - * @verbatim - * set data style lines - * splot [:][:][0:] "T" using 1:2:(&3==.5 ? &4 : -1) - * @endverbatim - * - * This command plots data in x- and y-direction unbounded, but in - * z-direction only those data points which are above the x-y-plane - * (we assume here a positive solution, if it has negative values, you - * might want to decrease the lower bound). Furthermore, it only takes - * the data points with z-values (&3) equal to 0.5, i.e. a - * cut through the domain at z=0.5. For the data points on - * this plane, the data values of the first data set (&4) are - * raised in z-direction above the x-y-plane; all other points are - * denoted the value -1 instead of the value of the data - * vector and are not plotted due to the lower bound in z plotting - * direction, given in the third pair of brackets. - * - * More complex cuts are possible, including nonlinear ones. Note - * however, that only those points which are actually on the - * cut-surface are plotted. - */ - template - static void write_gnuplot (const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const GnuplotFlags &flags, - std::ostream &out); + /** + * This is an alternative color + * function producing a grey scale + * between black (lowest values) + * and white (highest values). You + * may use it by setting the + * color_function variable to the + * address of this function. + */ + static RgbValues + grey_scale_color_function (const double value, + const double min_value, + const double max_value); -/** - * Write the given list of patches to the output stream for the - * Povray raytracer. - * - * Output in this format creates a povray source file, include - * standard camera and light source definition for rendering with - * povray 3.1 At present, this format only supports output for - * two-dimensional data, with values in the third direction taken from - * a data vector. - * - * The output uses two different povray-objects: - * - *
    - *
  • BICUBIC_PATCH - * A bicubic_patch is a 3-dimensional Bezier patch. It consists of 16 Points - * describing the surface. The 4 corner points are touched by the object, - * while the other 12 points pull and stretch the patch into shape. - * One bicubic_patch is generated on each patch. Therefor the number of - * subdivisions has to be 3 to provide the patch with 16 points. - * A bicubic patch is not exact but generates very smooth images. - * - *
  • MESH - * The mesh object is used to store large number of triangles. - * Every square of the patch data is split into one upper-left and one - * lower-right triangle. If the number of subdivisions is three, 32 triangle - * are generated for every patch. - * - * Using the smooth flag povray interpolates the normals on the triangles, - * imitating a curved surface - *
- * - * All objects get one texture definition called Tex. This texture has to be - * declared somewhere before the object data. This may be in an external - * data file or at the beginning of the output file. - * Setting the external_data flag to false, an standard camera, light and - * texture (scaled to fit the scene) is added to the outputfile. Set to true - * an include file "data.inc" is included. This file is not generated by deal - * and has to include camera, light and the texture definition Tex. - * - * You need povray (>=3.0) to render the scene. The minimum options for povray - * are: - * @verbatim - * povray +I +W +H +L - * @endverbatim - * If the external file "data.inc" is used, the path to this file has to be - * included in the povray options. - */ - template - static void write_povray (const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const PovrayFlags &flags, - std::ostream &out); + /** + * This is one more + * alternative color function + * producing a grey scale + * between white (lowest + * values) and black (highest + * values), i.e. the scale is + * reversed to the previous + * one. You may use it by + * setting the + * color_function + * variable to the address of + * this function. + */ + static RgbValues + reverse_grey_scale_color_function (const double value, + const double min_value, + const double max_value); -/** - * Write the given list of patches to the output stream in - * Tecplot ASCII format (FEBLOCK). - * - * For more information consult the Tecplot Users and Reference - * manuals. - */ - template - static void write_tecplot (const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const TecplotFlags &flags, - std::ostream &out); + /** + * Constructor. + */ + EpsFlags (const unsigned int height_vector = 0, + const unsigned int color_vector = 0, + const SizeType size_type = width, + const unsigned int size = 300, + const double line_width = 0.5, + const double azimut_angle = 60, + const double turn_angle = 30, + const double z_scaling = 1.0, + const bool draw_mesh = true, + const bool draw_cells = true, + const bool shade_cells = true, + const ColorFunction color_function= &default_color_function); -/** - * Write the given list of patches to the output stream in - * Tecplot binary format. - * - * For this to work properly ./configure checks for the - * Tecplot API at build time. To write Tecplot binary files directly - * make sure that the TECHOME environment variable points to the - * Tecplot installation directory, and that the files - * \$TECHOME/include/TECIO.h and \$TECHOME/lib/tecio.a are readable. - * If these files are not available (or in the case of 1D) this - * function will simply call write_tecplot() and thus larger ASCII - * data files will be produced rather than more efficient Tecplot - * binary files. - * - * @warning TecplotFlags::tecplot_binary_file_name indicates the name - * of the file to be written. If the file name is not set ASCII - * output is produced. - * - * For more information consult the Tecplot Users and Reference - * manuals. - */ - template - static void write_tecplot_binary ( - const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const TecplotFlags &flags, - std::ostream &out); + /** + * Declare all flags with name + * and type as offered by this + * class, for use in input files. + * + * For coloring, only the color + * functions declared in this + * class are offered. + */ + static void declare_parameters (ParameterHandler &prm); -/** - * Write the given list of patches to the output stream in UCD format - * described in the AVS developer's guide (now AVS). Due - * to limitations in the present format, only node based data can be - * output, which in one reason why we invented the patch concept. In - * order to write higher order elements, you may split them up into - * several subdivisions of each cell. These subcells will then, - * however, also appear as different cells by programs which - * understand the UCD format. - * - * No use is made of the possibility to give model data since these - * are not supported by all UCD aware programs. You may give cell data - * in derived classes by setting all values of a given data set on a - * patch to the same value. - */ - template - static void write_ucd (const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const UcdFlags &flags, - std::ostream &out); + /** + * Read the parameters declared in + * declare_parameters and set the + * flags for this output format + * accordingly. + * + * The flags thus obtained overwrite + * all previous contents of this object. + */ + void parse_parameters (const ParameterHandler &prm); -/** - * Write the given list of patches to the output stream in VTK - * format. The data is written in the traditional VTK format as opposed to the - * XML-based format that write_vtu() produces. - * - * The vector_data_ranges argument denotes ranges of components in the - * output that are considered a vector, rather than simply a - * collection of scalar fields. The VTK output format has special - * provisions that allow these components to be output by a single - * name rather than having to group several scalar fields into a - * vector later on in the visualization program. - */ - template - static void write_vtk (const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const VtkFlags &flags, - std::ostream &out); + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + }; + + /** + * Flags controlling the details + * of output in GMV + * format. At present no flags + * are implemented. + * + * @ingroup output + */ + struct GmvFlags + { + private: + /** + * Dummy entry to suppress compiler + * warnings when copying an empty + * structure. Remove this member + * when adding the first flag to + * this structure (and remove the + * private as well). + */ + int dummy; + public: + /** + * Default constructor. + */ + GmvFlags (); -/** - * Write the given list of patches to the output stream in VTK - * format. The data is written in the XML-based VTK format as opposed to the - * traditional format that write_vtk() produces. - * - * The vector_data_ranges argument denotes ranges of components in the - * output that are considered a vector, rather than simply a - * collection of scalar fields. The VTK output format has special - * provisions that allow these components to be output by a single - * name rather than having to group several scalar fields into a - * vector later on in the visualization program. - * - * Some visualization programs, such as ParaView, can read several - * separate VTU files to parallelize visualization. In that case, you - * need a .pvtu file that describes which VTU files form - * a group. The DataOutInterface::write_pvtu_record() function can - * generate such a master record. Likewise, - * DataOutInterface::write_visit_record() does the same for VisIt. - * Finally, for time dependent problems, you may also want to look - * at DataOutInterface::write_pvd_record() - * - * The use of this function is explained in step-40. - */ - template - static void write_vtu (const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const VtkFlags &flags, - std::ostream &out); + /** + * Declare all flags with name + * and type as offered by this + * class, for use in input files. + */ + static void declare_parameters (ParameterHandler &prm); -/** - * This writes the header for the xml based vtu file format. This - * routine is used internally together with - * DataOutInterface::write_vtu_footer() and DataOutInterface::write_vtu_main() - * by DataOutBase::write_vtu(). - */ - static void write_vtu_header (std::ostream &out); + /** + * Read the parameters declared in + * declare_parameters and set the + * flags for this output format + * accordingly. + * + * The flags thus obtained overwrite + * all previous contents of this object. + */ + void parse_parameters (const ParameterHandler &prm) const; -/** - * This writes the footer for the xml based vtu file format. This - * routine is used internally together with - * DataOutInterface::write_vtu_header() and DataOutInterface::write_vtu_main() - * by DataOutBase::write_vtu(). - */ - static void write_vtu_footer (std::ostream &out); + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + }; + + /** + * Flags controlling the details + * of output in + * Tecplot format. + * + * @ingroup output + */ + struct TecplotFlags + { -/** - * This writes the main part for the xml based vtu file format. This - * routine is used internally together with - * DataOutInterface::write_vtu_header() and DataOutInterface::write_vtu_footer() - * by DataOutBase::write_vtu(). - */ - template - static void write_vtu_main (const std::vector > &patches, + public: + + /** + * This variable is needed to hold the + * output file name when using the + * Tecplot API to write binary files. + * If the user doesn't set the file + * name with this variable only + * ASCII Tecplot output will be + * produced. + */ + const char *tecplot_binary_file_name; + + /** + * Tecplot allows to assign + * names to zones. This + * variable stores this name. + */ + const char *zone_name; + + /** + * Constructor + **/ + TecplotFlags (const char *tecplot_binary_file_name = NULL, + const char *zone_name = NULL); + + /** + * Declare all flags with name + * and type as offered by this + * class, for use in input files. + */ + static void declare_parameters (ParameterHandler &prm); + + /** + * Read the parameters declared in + * declare_parameters and set the + * flags for this output format + * accordingly. + * + * The flags thus obtained overwrite + * all previous contents of this object. + */ + void parse_parameters (const ParameterHandler &prm) const; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + }; + + /** + * Flags controlling the details + * of output in VTK + * format. At present no flags + * are implemented. + * + * @ingroup output + */ + struct VtkFlags + { + private: + /** + * Dummy entry to suppress compiler + * warnings when copying an empty + * structure. Remove this member + * when adding the first flag to + * this structure (and remove the + * private as well). + */ + int dummy; + + public: + /** + * Default constructor. + */ + VtkFlags (); + + /** + * Declare all flags with name + * and type as offered by this + * class, for use in input files. + */ + static void declare_parameters (ParameterHandler &prm); + + /** + * Read the parameters declared in + * declare_parameters and set the + * flags for this output format + * accordingly. + * + * The flags thus obtained overwrite + * all previous contents of this object. + */ + void parse_parameters (const ParameterHandler &prm) const; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + }; + /** + * Flags controlling the details + * of output in deal.II + * intermediate format. At + * present no flags are + * implemented. + * + * @ingroup output + */ + struct Deal_II_IntermediateFlags + { + /** + * An indicator of the + * currect file format + * version used to write + * intermediate format. We do + * not attempt to be backward + * compatible, so this number + * is used only to verify + * that the format we are + * writing is what the + * current readers and + * writers understand. + */ + static const unsigned int format_version = 3; + private: + /** + * Dummy entry to suppress compiler + * warnings when copying an empty + * structure. Remove this member + * when adding the first flag to + * this structure (and remove the + * private as well). + */ + int dummy; + + public: + /** + * Constructor. + */ + Deal_II_IntermediateFlags (); + + /** + * Declare all flags with name + * and type as offered by this + * class, for use in input files. + */ + static void declare_parameters (ParameterHandler &prm); + + /** + * Read the parameters declared in + * declare_parameters and set the + * flags for this output format + * accordingly. + * + * The flags thus obtained overwrite + * all previous contents of this object. + */ + void parse_parameters (const ParameterHandler &prm) const; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + }; + + /** + * Provide a data type specifying + * the presently supported output + * formats. + */ + enum OutputFormat + { + /** + * Use the format already + * stored in the object. + */ + default_format, + /** + * Do not write any output. + */ + none, + /** + * Output for OpenDX. + */ + dx, + /** + * Output in the UCD format + * for AVS. + */ + ucd, + /** + * Output for the + * Gnuplot tool. + */ + gnuplot, + /** + * Output for the + * Povray + * raytracer. + */ + povray, + /** + * Output in encapsulated + * PostScript. + */ + eps, + /** + * Output for GMV. + */ + gmv, + /** + * Output for + * Tecplot in text + * format. + */ + + tecplot, + /** + * Output for + * Tecplot in + * binary format. Faster + * and smaller than text + * format. + */ + tecplot_binary, + + /** + * Output in + * VTK format. + */ + vtk, + + /** + * Output in + * VTK format. + */ + vtu, + + /** + * Output in deal.II + * intermediate format. + */ + deal_II_intermediate, + + /** + * Output in + * HDF5 format. + */ + hdf5 + }; + + + /** + * Write the given list of patches to the output stream in + * OpenDX format. + * + * Since OpenDX uses some kind of visual data flow oriented + * programming language, some of these programs are provided in + * contrib/dx. + */ + template + static void write_dx (const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const DXFlags &flags, + std::ostream &out); + + /** + * Write the given list of patches to the output stream in eps format. + * + * Output in this format circumvents the use of auxiliary graphic + * programs converting some output format into a graphics format. This + * has the advantage that output is easy and fast, and the + * disadvantage that you have to give a whole bunch of parameters + * which determine the direction of sight, the mode of colorization, + * the scaling of the height axis, etc. (Of course, all these + * parameters have reasonable default values, which you may want to + * change from time to time.) At present, this format only supports + * output for two-dimensional data, with values in the third direction + * taken from a data vector. + * + * Basically, output consists of the mesh and the cells in between + * them. You can draw either of these, or both, or none if you are + * really interested in an empty picture. If written, the mesh uses + * black lines. The cells in between the mesh are either not printed + * (this will result in a loss of hidden line removal, i.e. you can + * "see through" the cells to lines behind), printed in white (which + * does nothing apart from the hidden line removal), or colorized + * using one of the data vectors (which need not be the same as the + * one used for computing the height information) and a customizable + * color function. The default color functions chooses the color + * between black, blue, green, red and white, with growing values of + * the data field chosen for colorization. At present, cells are + * displayed with one color per cell only, which is taken from the + * value of the data field at the center of the cell; bilinear + * interpolation of the color on a cell is not used. + * + * By default, the viewpoint is chosen like the default viewpoint in + * GNUPLOT, i.e. with an angle of 60 degrees with respect to the + * positive z-axis and rotated 30 degrees in positive sense (as seen + * from above) away from the negative y-axis. Of course you can + * change these settings. + * + * EPS output is written without a border around the picture, i.e. the + * bounding box is close to the output on all four sides. Coordinates + * are written using at most five digits, to keep picture size at a + * reasonable size. + * + * All parameters along with their default values are listed in the + * documentation of the EpsFlags member class of this + * class. See there for more and detailed information. + */ + template + static void write_eps (const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const EpsFlags &flags, + std::ostream &out); + + /** + * Write the given list of patches to the output stream in + * GMV format. + * + * Data is written in the following format: nodes are considered the + * points of the patches. In spatial dimensions less than three, + * zeroes are inserted for the missing coordinates. The data vectors + * are written as node or cell data, where for the first the data + * space is interpolated to (bi-,tri-)linear elements. + */ + template + static void write_gmv (const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const GmvFlags &flags, + std::ostream &out); + + /** + * Write the given list of patches to the output stream in gnuplot + * format. Visualization of two-dimensional data can then be achieved by + * starting gnuplot and endtering the commands + * + * @verbatim + * set data style lines + * splot "filename" using 1:2:n + * @endverbatim + * This example assumes that the number of the data vector displayed + * is n-2. + * + * The GNUPLOT format is not able to handle data on unstructured grids + * directly. Directly would mean that you only give the vertices and + * the solution values thereon and the program constructs its own grid + * to represent the data. This is only possible for a structured + * tensor product grid in two dimensions. However, it is possible to + * give several such patches within one file, which is exactly what + * the respective function of this class does: writing each cell's + * data as a patch of data, at least if the patches as passed from + * derived classes represent cells. Note that the functions on patches + * need not be continuous at interfaces between patches, so this + * method also works for discontinuous elements. Note also, that + * GNUPLOT can do hidden line removal for patched data. + * + * While this discussion applies to two spatial dimensions, it is more + * complicated in 3d. The reason is that we could still use patches, + * but it is difficult when trying to visualize them, since if we use + * a cut through the data (by, for example, using x- and + * z-coordinates, a fixed y-value and plot function values in + * z-direction, then the patched data is not a patch in the sense + * GNUPLOT wants it any more. Therefore, we use another approach, + * namely writing the data on the 3d grid as a sequence of lines, + * i.e. two points each associated with one or more data sets. There + * are therefore 12 lines for each subcells of a patch. + * + * Given the lines as described above, a cut through this data in + * Gnuplot can then be achieved like this (& stands for the dollar + * sign in the following): + * @verbatim + * set data style lines + * splot [:][:][0:] "T" using 1:2:(&3==.5 ? &4 : -1) + * @endverbatim + * + * This command plots data in x- and y-direction unbounded, but in + * z-direction only those data points which are above the x-y-plane + * (we assume here a positive solution, if it has negative values, you + * might want to decrease the lower bound). Furthermore, it only takes + * the data points with z-values (&3) equal to 0.5, i.e. a + * cut through the domain at z=0.5. For the data points on + * this plane, the data values of the first data set (&4) are + * raised in z-direction above the x-y-plane; all other points are + * denoted the value -1 instead of the value of the data + * vector and are not plotted due to the lower bound in z plotting + * direction, given in the third pair of brackets. + * + * More complex cuts are possible, including nonlinear ones. Note + * however, that only those points which are actually on the + * cut-surface are plotted. + */ + template + static void write_gnuplot (const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const GnuplotFlags &flags, + std::ostream &out); + + /** + * Write the given list of patches to the output stream for the + * Povray raytracer. + * + * Output in this format creates a povray source file, include + * standard camera and light source definition for rendering with + * povray 3.1 At present, this format only supports output for + * two-dimensional data, with values in the third direction taken from + * a data vector. + * + * The output uses two different povray-objects: + * + *
    + *
  • BICUBIC_PATCH + * A bicubic_patch is a 3-dimensional Bezier patch. It consists of 16 Points + * describing the surface. The 4 corner points are touched by the object, + * while the other 12 points pull and stretch the patch into shape. + * One bicubic_patch is generated on each patch. Therefor the number of + * subdivisions has to be 3 to provide the patch with 16 points. + * A bicubic patch is not exact but generates very smooth images. + * + *
  • MESH + * The mesh object is used to store large number of triangles. + * Every square of the patch data is split into one upper-left and one + * lower-right triangle. If the number of subdivisions is three, 32 triangle + * are generated for every patch. + * + * Using the smooth flag povray interpolates the normals on the triangles, + * imitating a curved surface + *
+ * + * All objects get one texture definition called Tex. This texture has to be + * declared somewhere before the object data. This may be in an external + * data file or at the beginning of the output file. + * Setting the external_data flag to false, an standard camera, light and + * texture (scaled to fit the scene) is added to the outputfile. Set to true + * an include file "data.inc" is included. This file is not generated by deal + * and has to include camera, light and the texture definition Tex. + * + * You need povray (>=3.0) to render the scene. The minimum options for povray + * are: + * @verbatim + * povray +I +W +H +L + * @endverbatim + * If the external file "data.inc" is used, the path to this file has to be + * included in the povray options. + */ + template + static void write_povray (const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const PovrayFlags &flags, + std::ostream &out); + + /** + * Write the given list of patches to the output stream in + * Tecplot ASCII format (FEBLOCK). + * + * For more information consult the Tecplot Users and Reference + * manuals. + */ + template + static void write_tecplot (const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const TecplotFlags &flags, + std::ostream &out); + + /** + * Write the given list of patches to the output stream in + * Tecplot binary format. + * + * For this to work properly ./configure checks for the + * Tecplot API at build time. To write Tecplot binary files directly + * make sure that the TECHOME environment variable points to the + * Tecplot installation directory, and that the files + * \$TECHOME/include/TECIO.h and \$TECHOME/lib/tecio.a are readable. + * If these files are not available (or in the case of 1D) this + * function will simply call write_tecplot() and thus larger ASCII + * data files will be produced rather than more efficient Tecplot + * binary files. + * + * @warning TecplotFlags::tecplot_binary_file_name indicates the name + * of the file to be written. If the file name is not set ASCII + * output is produced. + * + * For more information consult the Tecplot Users and Reference + * manuals. + */ + template + static void write_tecplot_binary ( + const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const TecplotFlags &flags, + std::ostream &out); + + /** + * Write the given list of patches to the output stream in UCD format + * described in the AVS developer's guide (now AVS). Due + * to limitations in the present format, only node based data can be + * output, which in one reason why we invented the patch concept. In + * order to write higher order elements, you may split them up into + * several subdivisions of each cell. These subcells will then, + * however, also appear as different cells by programs which + * understand the UCD format. + * + * No use is made of the possibility to give model data since these + * are not supported by all UCD aware programs. You may give cell data + * in derived classes by setting all values of a given data set on a + * patch to the same value. + */ + template + static void write_ucd (const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const UcdFlags &flags, + std::ostream &out); + + /** + * Write the given list of patches to the output stream in VTK + * format. The data is written in the traditional VTK format as opposed to the + * XML-based format that write_vtu() produces. + * + * The vector_data_ranges argument denotes ranges of components in the + * output that are considered a vector, rather than simply a + * collection of scalar fields. The VTK output format has special + * provisions that allow these components to be output by a single + * name rather than having to group several scalar fields into a + * vector later on in the visualization program. + */ + template + static void write_vtk (const std::vector > &patches, const std::vector &data_names, const std::vector > &vector_data_ranges, const VtkFlags &flags, std::ostream &out); -/** - * Write the given list of patches to the output stream in deal.II - * intermediate format. This is not a format understood by any other - * graphics program, but is rather a direct dump of the intermediate - * internal format used by deal.II. This internal format is generated - * by the various classes that can generate output using the - * DataOutBase class, for example from a finite element solution, and - * is then converted in the present class to the final graphics - * format. - * - * Note that the intermediate format is what its name suggests: a - * direct representation of internal data. It isn't standardized and - * will change whenever we change our internal representation. You can - * only expect to process files written in this format using the same - * version of deal.II that was used for writing. - * - * The reason why we offer to write out this intermediate format is - * that it can be read back into a deal.II program using the - * DataOutReader class, which is helpful in at least two contexts: - * First, this can be used to later generate graphical output in any - * other graphics format presently understood; this way, it is not - * necessary to know at run-time which output format is requested, or - * if multiple output files in different formats are needed. Secondly, - * in contrast to almost all other graphics formats, it is possible to - * merge several files that contain intermediate format data, and - * generate a single output file from it, which may be again in - * intermediate format or any of the final formats. This latter option - * is most helpful for parallel programs: as demonstrated in the - * step-17 example program, it is possible to let only one processor - * generate the graphical output for the entire parallel program, but - * this can become vastly inefficient if many processors are involved, - * because the load is no longer balanced. The way out is to let each - * processor generate intermediate graphical output for its chunk of - * the domain, and the later merge the different files into one, which - * is an operation that is much cheaper than the generation of the - * intermediate data. - * - * Intermediate format deal.II data is usually stored in files with - * the ending .d2. - */ - template - static void write_deal_II_intermediate ( - const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const Deal_II_IntermediateFlags &flags, - std::ostream &out); - - template - static void write_hdf5_parallel ( - const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const char* filename, - MPI_Comm comm); - - template - static XDMFEntry create_xdmf_entry (const std::vector > &patches, - const std::vector &data_names, - const std::vector > &vector_data_ranges, - const char* h5_filename, - const double cur_time, - MPI_Comm comm); - - template - static void write_xdmf_file ( - const std::vector > &patches, - const std::vector &entries, - const char *filename, - MPI_Comm comm); - - /** - * Given an input stream that contains - * data written by - * write_deal_II_intermediate, determine - * the dim and spacedim - * template parameters with which that - * function was called, and return them - * as a pair of values. - * - * Note that this function eats a number - * of elements at the present position of - * the stream, and therefore alters - * it. In order to read from it using, - * for example, the DataOutReader class, - * you may wish to either reset the - * stream to its previous position, or - * close and reopen it. - */ - static - std::pair - determine_intermediate_format_dimensions (std::istream &input); - - /** - * Return the OutputFormat - * value corresponding to the - * given string. If the string - * does not match any known - * format, an exception is - * thrown. - * - * Since this function does not - * need data from this object, it - * is static and can thus be - * called without creating an - * object of this class. Its main - * purpose is to allow a program - * to use any implemented output - * format without the need to - * extend the program's parser - * each time a new format is - * implemented. - * - * To get a list of presently - * available format names, - * e.g. to give it to the - * ParameterHandler class, - * use the function - * get_output_format_names(). - */ - static OutputFormat parse_output_format (const std::string &format_name); - - /** - * Return a list of implemented - * output formats. The different - * names are separated by - * vertical bar signs (`|') - * as used by the - * ParameterHandler classes. - */ - static std::string get_output_format_names (); - - /** - * Provide a function which tells us which - * suffix a file with a given output format - * usually has. At present the following - * formats are defined: - *
    - *
  • dx: .dx - *
  • ucd: .inp - *
  • gnuplot: .gnuplot - *
  • povray: .pov - *
  • eps: .eps - *
  • gmv: .gmv - *
  • tecplot: .dat - *
  • tecplot_binary: .plt - *
  • vtk: .vtk - *
  • vtu: .vtu - *
  • deal_II_intermediate: .d2. - *
- */ - static std::string default_suffix (const OutputFormat output_format); - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - static std::size_t memory_consumption (); - - /** @addtogroup Exceptions - * @{ */ - - /** - * Exception - */ - DeclException2 (ExcInvalidDatasetSize, - int, int, - << "The number of points in this data set is " << arg1 - << ", but we expected " << arg2 << " in each space direction."); - /** - * An output function did not - * receive any patches for - * writing. - */ - DeclException0 (ExcNoPatches); - /** - * Exception - */ - DeclException0 (ExcTecplotAPIError); - /** - * Exception - */ - DeclException1 (ExcErrorOpeningTecplotFile, - char*, - << "There was an error opening Tecplot file " << arg1 - << " for output"); - - //@} - private: - /** - * Write the coordinates of nodes - * in the desired format. - */ - template - static void write_nodes (const std::vector >& patches, - STREAM& out); - - /** - * Write the node numbers of a - * cell in the desired format. - */ - template - static void write_cells (const std::vector >& patches, - STREAM& out); - - /** - * Write data in the desired - * format. - */ - template - static void write_data (const std::vector >& patches, - const unsigned int n_data_sets, - const bool double_precision, - STREAM& out); - - /** - * Class holding the data of one - * cell of a patch in two space - * dimensions for output. It is - * the projection of a cell in - * three-dimensional space (two - * coordinates, one height value) - * to the direction of sight. - */ - class EpsCell2d - { - public: - - /** - * Vector of vertices of this cell. - */ - Point<2> vertices[4]; - - /** - * Data value from which the actual - * colors will be computed by - * the colorization function stated - * in the EpsFlags class. - */ - float color_value; - - /** - * Depth into the picture, which - * is defined as the distance from - * an observer at an the origin in - * direction of the line of sight. - */ - float depth; - - /** - * Comparison operator for - * sorting. - */ - bool operator < (const EpsCell2d &) const; - }; + /** + * Write the given list of patches to the output stream in VTK + * format. The data is written in the XML-based VTK format as opposed to the + * traditional format that write_vtk() produces. + * + * The vector_data_ranges argument denotes ranges of components in the + * output that are considered a vector, rather than simply a + * collection of scalar fields. The VTK output format has special + * provisions that allow these components to be output by a single + * name rather than having to group several scalar fields into a + * vector later on in the visualization program. + * + * Some visualization programs, such as ParaView, can read several + * separate VTU files to parallelize visualization. In that case, you + * need a .pvtu file that describes which VTU files form + * a group. The DataOutInterface::write_pvtu_record() function can + * generate such a master record. Likewise, + * DataOutInterface::write_visit_record() does the same for VisIt. + * Finally, for time dependent problems, you may also want to look + * at DataOutInterface::write_pvd_record() + * + * The use of this function is explained in step-40. + */ + template + static void write_vtu (const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const VtkFlags &flags, + std::ostream &out); + + /** + * This writes the header for the xml based vtu file format. This + * routine is used internally together with + * DataOutInterface::write_vtu_footer() and DataOutInterface::write_vtu_main() + * by DataOutBase::write_vtu(). + */ + static void write_vtu_header (std::ostream &out); + + /** + * This writes the footer for the xml based vtu file format. This + * routine is used internally together with + * DataOutInterface::write_vtu_header() and DataOutInterface::write_vtu_main() + * by DataOutBase::write_vtu(). + */ + static void write_vtu_footer (std::ostream &out); + + /** + * This writes the main part for the xml based vtu file format. This + * routine is used internally together with + * DataOutInterface::write_vtu_header() and DataOutInterface::write_vtu_footer() + * by DataOutBase::write_vtu(). + */ + template + static void write_vtu_main (const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const VtkFlags &flags, + std::ostream &out); + + /** + * Write the given list of patches to the output stream in deal.II + * intermediate format. This is not a format understood by any other + * graphics program, but is rather a direct dump of the intermediate + * internal format used by deal.II. This internal format is generated + * by the various classes that can generate output using the + * DataOutBase class, for example from a finite element solution, and + * is then converted in the present class to the final graphics + * format. + * + * Note that the intermediate format is what its name suggests: a + * direct representation of internal data. It isn't standardized and + * will change whenever we change our internal representation. You can + * only expect to process files written in this format using the same + * version of deal.II that was used for writing. + * + * The reason why we offer to write out this intermediate format is + * that it can be read back into a deal.II program using the + * DataOutReader class, which is helpful in at least two contexts: + * First, this can be used to later generate graphical output in any + * other graphics format presently understood; this way, it is not + * necessary to know at run-time which output format is requested, or + * if multiple output files in different formats are needed. Secondly, + * in contrast to almost all other graphics formats, it is possible to + * merge several files that contain intermediate format data, and + * generate a single output file from it, which may be again in + * intermediate format or any of the final formats. This latter option + * is most helpful for parallel programs: as demonstrated in the + * step-17 example program, it is possible to let only one processor + * generate the graphical output for the entire parallel program, but + * this can become vastly inefficient if many processors are involved, + * because the load is no longer balanced. The way out is to let each + * processor generate intermediate graphical output for its chunk of + * the domain, and the later merge the different files into one, which + * is an operation that is much cheaper than the generation of the + * intermediate data. + * + * Intermediate format deal.II data is usually stored in files with + * the ending .d2. + */ + template + static void write_deal_II_intermediate ( + const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const Deal_II_IntermediateFlags &flags, + std::ostream &out); + + template + static void write_hdf5_parallel ( + const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const char *filename, + MPI_Comm comm); + + template + static XDMFEntry create_xdmf_entry (const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const char *h5_filename, + const double cur_time, + MPI_Comm comm); + + template + static void write_xdmf_file ( + const std::vector > &patches, + const std::vector &entries, + const char *filename, + MPI_Comm comm); + + /** + * Given an input stream that contains + * data written by + * write_deal_II_intermediate, determine + * the dim and spacedim + * template parameters with which that + * function was called, and return them + * as a pair of values. + * + * Note that this function eats a number + * of elements at the present position of + * the stream, and therefore alters + * it. In order to read from it using, + * for example, the DataOutReader class, + * you may wish to either reset the + * stream to its previous position, or + * close and reopen it. + */ + static + std::pair + determine_intermediate_format_dimensions (std::istream &input); + + /** + * Return the OutputFormat + * value corresponding to the + * given string. If the string + * does not match any known + * format, an exception is + * thrown. + * + * Since this function does not + * need data from this object, it + * is static and can thus be + * called without creating an + * object of this class. Its main + * purpose is to allow a program + * to use any implemented output + * format without the need to + * extend the program's parser + * each time a new format is + * implemented. + * + * To get a list of presently + * available format names, + * e.g. to give it to the + * ParameterHandler class, + * use the function + * get_output_format_names(). + */ + static OutputFormat parse_output_format (const std::string &format_name); + + /** + * Return a list of implemented + * output formats. The different + * names are separated by + * vertical bar signs (`|') + * as used by the + * ParameterHandler classes. + */ + static std::string get_output_format_names (); + + /** + * Provide a function which tells us which + * suffix a file with a given output format + * usually has. At present the following + * formats are defined: + *
    + *
  • dx: .dx + *
  • ucd: .inp + *
  • gnuplot: .gnuplot + *
  • povray: .pov + *
  • eps: .eps + *
  • gmv: .gmv + *
  • tecplot: .dat + *
  • tecplot_binary: .plt + *
  • vtk: .vtk + *
  • vtu: .vtu + *
  • deal_II_intermediate: .d2. + *
+ */ + static std::string default_suffix (const OutputFormat output_format); + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + static std::size_t memory_consumption (); + + /** @addtogroup Exceptions + * @{ */ + + /** + * Exception + */ + DeclException2 (ExcInvalidDatasetSize, + int, int, + << "The number of points in this data set is " << arg1 + << ", but we expected " << arg2 << " in each space direction."); + /** + * An output function did not + * receive any patches for + * writing. + */ + DeclException0 (ExcNoPatches); + /** + * Exception + */ + DeclException0 (ExcTecplotAPIError); + /** + * Exception + */ + DeclException1 (ExcErrorOpeningTecplotFile, + char *, + << "There was an error opening Tecplot file " << arg1 + << " for output"); + + //@} +private: + /** + * Write the coordinates of nodes + * in the desired format. + */ + template + static void write_nodes (const std::vector > &patches, + STREAM &out); + + /** + * Write the node numbers of a + * cell in the desired format. + */ + template + static void write_cells (const std::vector > &patches, + STREAM &out); + + /** + * Write data in the desired + * format. + */ + template + static void write_data (const std::vector > &patches, + const unsigned int n_data_sets, + const bool double_precision, + STREAM &out); + + /** + * Class holding the data of one + * cell of a patch in two space + * dimensions for output. It is + * the projection of a cell in + * three-dimensional space (two + * coordinates, one height value) + * to the direction of sight. + */ + class EpsCell2d + { + public: + + /** + * Vector of vertices of this cell. + */ + Point<2> vertices[4]; + + /** + * Data value from which the actual + * colors will be computed by + * the colorization function stated + * in the EpsFlags class. + */ + float color_value; + + /** + * Depth into the picture, which + * is defined as the distance from + * an observer at an the origin in + * direction of the line of sight. + */ + float depth; - /** - * This is a helper function for - * the write_gmv - * function. There, the data in - * the patches needs to be copied - * around as output is one - * variable globally at a time, - * rather than all data on each - * vertex at a time. This copying - * around can be done detached - * from the main thread, and is - * thus moved into this separate - * function. - * - * Note that because of the - * similarity of the formats, - * this function is also used by - * the Vtk and Tecplot output - * functions. - */ - template - static void - write_gmv_reorder_data_vectors (const std::vector > &patches, - Table<2,double> &data_vectors); + /** + * Comparison operator for + * sorting. + */ + bool operator < (const EpsCell2d &) const; + }; + + + /** + * This is a helper function for + * the write_gmv + * function. There, the data in + * the patches needs to be copied + * around as output is one + * variable globally at a time, + * rather than all data on each + * vertex at a time. This copying + * around can be done detached + * from the main thread, and is + * thus moved into this separate + * function. + * + * Note that because of the + * similarity of the formats, + * this function is also used by + * the Vtk and Tecplot output + * functions. + */ + template + static void + write_gmv_reorder_data_vectors (const std::vector > &patches, + Table<2,double> &data_vectors); @@ -2178,640 +2179,640 @@ class DataOutBase template class DataOutInterface : private DataOutBase { - public: - /* - * Import a few names that were - * previously in this class and have then - * moved to the base class. Since the - * base class is inherited from - * privately, we need to re-import these - * symbols to make sure that references - * to DataOutInterface::XXX - * remain valid. - */ - using DataOutBase::OutputFormat; - using DataOutBase::default_format; - using DataOutBase::dx; - using DataOutBase::gnuplot; - using DataOutBase::povray; - using DataOutBase::eps; - using DataOutBase::tecplot; - using DataOutBase::tecplot_binary; - using DataOutBase::vtk; - using DataOutBase::vtu; - using DataOutBase::deal_II_intermediate; - using DataOutBase::parse_output_format; - using DataOutBase::get_output_format_names; - using DataOutBase::determine_intermediate_format_dimensions; - - /** - * Constructor. - */ - DataOutInterface (); - - /** - * Destructor. Does nothing, but is - * declared virtual since this class has - * virtual functions. - */ - virtual ~DataOutInterface (); - - /** - * Obtain data through get_patches() - * and write it to out - * in OpenDX format. See - * DataOutBase::write_dx. - */ - void write_dx (std::ostream &out) const; - - /** - * Obtain data through get_patches() - * and write it to out - * in EPS format. See - * DataOutBase::write_eps. - */ - void write_eps (std::ostream &out) const; - - /** - * Obtain data through get_patches() - * and write it to out - * in GMV format. See - * DataOutBase::write_gmv. - */ - void write_gmv (std::ostream &out) const; - - /** - * Obtain data through get_patches() - * and write it to out - * in GNUPLOT format. See - * DataOutBase::write_gnuplot. - */ - void write_gnuplot (std::ostream &out) const; - - /** - * Obtain data through get_patches() - * and write it to out - * in POVRAY format. See - * DataOutBase::write_povray. - */ - void write_povray (std::ostream &out) const; - - /** - * Obtain data through get_patches() - * and write it to out - * in Tecplot format. See - * DataOutBase::write_tecplot. - */ - void write_tecplot (std::ostream &out) const; - - /** - * Obtain data through - * get_patches() and write it in - * the Tecplot binary output - * format. Note that the name of - * the output file must be - * specified through the - * TecplotFlags interface. - */ - void write_tecplot_binary (std::ostream &out) const; - - /** - * Obtain data through - * get_patches() and write it to - * out in UCD format for - * AVS. See - * DataOutBase::write_ucd. - */ - void write_ucd (std::ostream &out) const; - - /** - * Obtain data through get_patches() - * and write it to out - * in Vtk format. See - * DataOutBase::write_vtk. - */ - void write_vtk (std::ostream &out) const; - - /** - * Obtain data through get_patches() - * and write it to out - * in Vtu (VTK's XML) format. See - * DataOutBase::write_vtu. - * - * Some visualization programs, - * such as ParaView, can read - * several separate VTU files to - * parallelize visualization. In - * that case, you need a - * .pvtu file that - * describes which VTU files form - * a group. The - * DataOutInterface::write_pvtu_record() - * function can generate such a - * master record. Likewise, - * DataOutInterface::write_visit_record() - * does the same for VisIt. Finally, - * DataOutInterface::write_pvd_record() - * can be used to group together - * the files that jointly make up - * a time dependent simulation. - */ - void write_vtu (std::ostream &out) const; - - /** - * Collective MPI call to write the - * solution from all participating nodes - * (those in the given communicator) to a - * single compressed .vtu file on a - * shared file system. The communicator - * can be a sub communicator of the one - * used by the computation. This routine - * uses MPI I/O to achieve high - * performance on parallel filesystems. - * Also see - * DataOutInterface::write_vtu(). - */ - void write_vtu_in_parallel (const char* filename, MPI_Comm comm) const; - - /** - * Some visualization programs, such as - * ParaView, can read several separate - * VTU files to parallelize - * visualization. In that case, you need - * a .pvtu file that - * describes which VTU files (written, - * for example, through the write_vtu() - * function) form a group. The current - * function can generate such a master - * record. - * - * The file so written contains a list of - * (scalar or vector) fields whose values - * are described by the individual files - * that comprise the set of parallel VTU - * files along with the names of these - * files. This function gets the names - * and types of fields through the - * get_patches() function of this class - * like all the other write_xxx() - * functions. The second argument to this - * function specifies the names of the - * files that form the parallel set. - * - * @note See DataOutBase::write_vtu for - * writing each piece. Also note that - * only one parallel process needs to - * call the current function, listing the - * names of the files written by all - * parallel processes. - * - * @note The use of this function is - * explained in step-40. - * - * @note In order to tell Paraview to - * group together multiple pvtu - * files that each describe one time - * step of a time dependent simulation, - * see the - * DataOutInterface::write_pvd_record() - * function. - * - * @note At the time of writing, - * the other big VTK-based - * visualization program, VisIt, - * can not read pvtu - * records. However, it can read - * visit records as written by - * the write_visit_record() - * function. - */ - void write_pvtu_record (std::ostream &out, - const std::vector &piece_names) const; - - /** - * In ParaView it is possible to visualize time-dependent - * data tagged with the current - * integration time of a time dependent simulation. To use this - * feature you need a .pvd - * file that describes which VTU or PVTU file - * belongs to which timestep. This function writes a file that - * provides this mapping, i.e., it takes a list of pairs each of - * which indicates a particular time instant and the corresponding - * file that contains the graphical data for this time instant. - * - * A typical use case, in program that computes a time dependent - * solution, would be the following (time and - * time_step are member variables of the class with types - * double and unsigned int, respectively; - * the variable times_and_names is of type - * std::vector@ @>): - * - * @code - * template - * void MyEquation::output_results () const - * { - * DataOut data_out; - * - * data_out.attach_dof_handler (dof_handler); - * data_out.add_data_vector (solution, "U"); - * data_out.build_patches (); - * - * const std::string filename = "solution-" + - * Utilities::int_to_string (timestep_number, 3) + - * ".vtu"; - * std::ofstream output (filename.c_str()); - * data_out.write_vtu (output); - * - * times_and_names.push_back (std::pair (time, filename)); - * std::ofstream pvd_output ("solution.pvd"); - * data_out.write_pvd_record (pvd_output, times_and_names); - * } - * @endcode - * - * @note See DataOutBase::write_vtu or - * DataOutInterface::write_pvtu_record for - * writing solutions at each timestep. - * - * @note The second element of each pair, i.e., the file in which - * the graphical data for each time is stored, may itself be again - * a file that references other files. For example, it could be - * the name for a .pvtu file that references multiple - * parts of a parallel computation. - * - * @author Marco Engelhard, 2012 - */ - void write_pvd_record (std::ostream &out, - const std::vector > ×_and_names) const; - - /** - * This function is the exact - * equivalent of the - * write_pvtu_record() function - * but for the VisIt - * visualization program. See - * there for the purpose of this - * function. - * - * This function is documented - * in the "Creating a master file - * for parallel" section (section 5.7) - * of the "Getting data into VisIt" - * report that can be found here: - * https://wci.llnl.gov/codes/visit/2.0.0/GettingDataIntoVisIt2.0.0.pdf - */ - void write_visit_record (std::ostream &out, - const std::vector &piece_names) const; - - /** - * Obtain data through get_patches() - * and write it to out - * in deal.II intermediate - * format. See - * DataOutBase::write_deal_II_intermediate. - * - * Note that the intermediate - * format is what its name - * suggests: a direct - * representation of internal - * data. It isn't standardized - * and will change whenever we - * change our internal - * representation. You can only - * expect to process files - * written in this format using - * the same version of deal.II - * that was used for writing. - */ - void write_deal_II_intermediate (std::ostream &out) const; - - XDMFEntry create_xdmf_entry (const char *h5_filename, - const double cur_time, - MPI_Comm comm) const; - - void write_xdmf_file (const std::vector &entries, - const char *filename, - MPI_Comm comm) const; - - void write_hdf5_parallel (const char* filename, MPI_Comm comm) const; - /** - * Write data and grid to out - * according to the given data - * format. This function simply - * calls the appropriate - * write_* function. If no - * output format is requested, - * the default_format is - * written. - * - * An error occurs if no format - * is provided and the default - * format is default_format. - */ - void write (std::ostream &out, - const OutputFormat output_format = default_format) const; - - /** - * Set the default format. The - * value set here is used - * anytime, output for format - * default_format is - * requested. - */ - void set_default_format (const OutputFormat default_format); - - /** - * Set the flags to be used for - * output in OpenDX format. - */ - void set_flags (const DXFlags &dx_flags); - - /** - * Set the flags to be used for - * output in UCD format. - */ - void set_flags (const UcdFlags &ucd_flags); - - /** - * Set the flags to be used for - * output in GNUPLOT format. - */ - void set_flags (const GnuplotFlags &gnuplot_flags); - - /** - * Set the flags to be used for - * output in POVRAY format. - */ - void set_flags (const PovrayFlags &povray_flags); - - /** - * Set the flags to be used for - * output in EPS output. - */ - void set_flags (const EpsFlags &eps_flags); - - /** - * Set the flags to be used for - * output in GMV format. - */ - void set_flags (const GmvFlags &gmv_flags); - - /** - * Set the flags to be used for - * output in Tecplot format. - */ - void set_flags (const TecplotFlags &tecplot_flags); - - /** - * Set the flags to be used for - * output in VTK format. - */ - void set_flags (const VtkFlags &vtk_flags); - - /** - * Set the flags to be used for output in - * deal.II intermediate format. - */ - void set_flags (const Deal_II_IntermediateFlags &deal_II_intermediate_flags); - - /** - * A function that returns the same - * string as the respective function in - * the base class does; the only - * exception being that if the parameter - * is omitted, then the value for the - * present default format is returned, - * i.e. the correct suffix for the format - * that was set through - * set_default_format() or - * parse_parameters() before calling this - * function. - */ - std::string - default_suffix (const OutputFormat output_format = default_format) const; - - /** - * Declare parameters for all - * output formats by declaring - * subsections within the - * parameter file for each output - * format and call the respective - * declare_parameters - * functions of the flag classes - * for each output format. - * - * Some of the declared - * subsections may not contain - * entries, if the respective - * format does not export any - * flags. - * - * Note that the top-level - * parameters denoting the number - * of subdivisions per patch and - * the output format are not - * declared, since they are only - * passed to virtual functions - * and are not stored inside - * objects of this type. You have - * to declare them yourself. - */ - static void declare_parameters (ParameterHandler &prm); - - /** - * Read the parameters declared - * in declare_parameters and - * set the flags for the output - * formats accordingly. - * - * The flags thus obtained - * overwrite all previous - * contents of the flag objects - * as default-constructed or set - * by the set_flags() function. - */ - void parse_parameters (ParameterHandler &prm); - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - - protected: - /** - * This is the abstract function - * through which derived classes - * propagate preprocessed data in - * the form of Patch - * structures (declared in the - * base class DataOutBase) to - * the actual output - * function. You need to overload - * this function to allow the - * output functions to know what - * they shall print. - */ - virtual - const std::vector > & - get_patches () const = 0; - - /** - * Abstract virtual function - * through which the names of - * data sets are obtained by the - * output functions of the base - * class. - */ - virtual - std::vector - get_dataset_names () const = 0; - - /** - * This functions returns - * information about how the - * individual components of - * output files that consist of - * more than one data set are to - * be interpreted. - * - * It returns a list of index - * pairs and corresponding name - * indicating which components of - * the output are to be - * considered vector-valued - * rather than just a collection - * of scalar data. The index - * pairs are inclusive; for - * example, if we have a Stokes - * problem in 2d with components - * (u,v,p), then the - * corresponding vector data - * range should be (0,1), and the - * returned list would consist of - * only a single element with a - * tuple such as (0,1,"velocity"). - * - * Since some of the derived - * classes do not know about - * vector data, this function has - * a default implementation that - * simply returns an empty - * string, meaning that all data - * is to be considered a - * collection of scalar fields. - */ - virtual - std::vector > - get_vector_data_ranges () const; - - /** - * The default number of - * subdivisions for patches. This - * is filled by parse_parameters() - * and should be obeyed by - * build_patches() in derived - * classes. - */ - unsigned int default_subdivisions; +public: + /* + * Import a few names that were + * previously in this class and have then + * moved to the base class. Since the + * base class is inherited from + * privately, we need to re-import these + * symbols to make sure that references + * to DataOutInterface::XXX + * remain valid. + */ + using DataOutBase::OutputFormat; + using DataOutBase::default_format; + using DataOutBase::dx; + using DataOutBase::gnuplot; + using DataOutBase::povray; + using DataOutBase::eps; + using DataOutBase::tecplot; + using DataOutBase::tecplot_binary; + using DataOutBase::vtk; + using DataOutBase::vtu; + using DataOutBase::deal_II_intermediate; + using DataOutBase::parse_output_format; + using DataOutBase::get_output_format_names; + using DataOutBase::determine_intermediate_format_dimensions; + + /** + * Constructor. + */ + DataOutInterface (); + + /** + * Destructor. Does nothing, but is + * declared virtual since this class has + * virtual functions. + */ + virtual ~DataOutInterface (); + + /** + * Obtain data through get_patches() + * and write it to out + * in OpenDX format. See + * DataOutBase::write_dx. + */ + void write_dx (std::ostream &out) const; + + /** + * Obtain data through get_patches() + * and write it to out + * in EPS format. See + * DataOutBase::write_eps. + */ + void write_eps (std::ostream &out) const; + + /** + * Obtain data through get_patches() + * and write it to out + * in GMV format. See + * DataOutBase::write_gmv. + */ + void write_gmv (std::ostream &out) const; + + /** + * Obtain data through get_patches() + * and write it to out + * in GNUPLOT format. See + * DataOutBase::write_gnuplot. + */ + void write_gnuplot (std::ostream &out) const; + + /** + * Obtain data through get_patches() + * and write it to out + * in POVRAY format. See + * DataOutBase::write_povray. + */ + void write_povray (std::ostream &out) const; + + /** + * Obtain data through get_patches() + * and write it to out + * in Tecplot format. See + * DataOutBase::write_tecplot. + */ + void write_tecplot (std::ostream &out) const; + + /** + * Obtain data through + * get_patches() and write it in + * the Tecplot binary output + * format. Note that the name of + * the output file must be + * specified through the + * TecplotFlags interface. + */ + void write_tecplot_binary (std::ostream &out) const; + + /** + * Obtain data through + * get_patches() and write it to + * out in UCD format for + * AVS. See + * DataOutBase::write_ucd. + */ + void write_ucd (std::ostream &out) const; + + /** + * Obtain data through get_patches() + * and write it to out + * in Vtk format. See + * DataOutBase::write_vtk. + */ + void write_vtk (std::ostream &out) const; + + /** + * Obtain data through get_patches() + * and write it to out + * in Vtu (VTK's XML) format. See + * DataOutBase::write_vtu. + * + * Some visualization programs, + * such as ParaView, can read + * several separate VTU files to + * parallelize visualization. In + * that case, you need a + * .pvtu file that + * describes which VTU files form + * a group. The + * DataOutInterface::write_pvtu_record() + * function can generate such a + * master record. Likewise, + * DataOutInterface::write_visit_record() + * does the same for VisIt. Finally, + * DataOutInterface::write_pvd_record() + * can be used to group together + * the files that jointly make up + * a time dependent simulation. + */ + void write_vtu (std::ostream &out) const; + + /** + * Collective MPI call to write the + * solution from all participating nodes + * (those in the given communicator) to a + * single compressed .vtu file on a + * shared file system. The communicator + * can be a sub communicator of the one + * used by the computation. This routine + * uses MPI I/O to achieve high + * performance on parallel filesystems. + * Also see + * DataOutInterface::write_vtu(). + */ + void write_vtu_in_parallel (const char *filename, MPI_Comm comm) const; + + /** + * Some visualization programs, such as + * ParaView, can read several separate + * VTU files to parallelize + * visualization. In that case, you need + * a .pvtu file that + * describes which VTU files (written, + * for example, through the write_vtu() + * function) form a group. The current + * function can generate such a master + * record. + * + * The file so written contains a list of + * (scalar or vector) fields whose values + * are described by the individual files + * that comprise the set of parallel VTU + * files along with the names of these + * files. This function gets the names + * and types of fields through the + * get_patches() function of this class + * like all the other write_xxx() + * functions. The second argument to this + * function specifies the names of the + * files that form the parallel set. + * + * @note See DataOutBase::write_vtu for + * writing each piece. Also note that + * only one parallel process needs to + * call the current function, listing the + * names of the files written by all + * parallel processes. + * + * @note The use of this function is + * explained in step-40. + * + * @note In order to tell Paraview to + * group together multiple pvtu + * files that each describe one time + * step of a time dependent simulation, + * see the + * DataOutInterface::write_pvd_record() + * function. + * + * @note At the time of writing, + * the other big VTK-based + * visualization program, VisIt, + * can not read pvtu + * records. However, it can read + * visit records as written by + * the write_visit_record() + * function. + */ + void write_pvtu_record (std::ostream &out, + const std::vector &piece_names) const; + + /** + * In ParaView it is possible to visualize time-dependent + * data tagged with the current + * integration time of a time dependent simulation. To use this + * feature you need a .pvd + * file that describes which VTU or PVTU file + * belongs to which timestep. This function writes a file that + * provides this mapping, i.e., it takes a list of pairs each of + * which indicates a particular time instant and the corresponding + * file that contains the graphical data for this time instant. + * + * A typical use case, in program that computes a time dependent + * solution, would be the following (time and + * time_step are member variables of the class with types + * double and unsigned int, respectively; + * the variable times_and_names is of type + * std::vector@ @>): + * + * @code + * template + * void MyEquation::output_results () const + * { + * DataOut data_out; + * + * data_out.attach_dof_handler (dof_handler); + * data_out.add_data_vector (solution, "U"); + * data_out.build_patches (); + * + * const std::string filename = "solution-" + + * Utilities::int_to_string (timestep_number, 3) + + * ".vtu"; + * std::ofstream output (filename.c_str()); + * data_out.write_vtu (output); + * + * times_and_names.push_back (std::pair (time, filename)); + * std::ofstream pvd_output ("solution.pvd"); + * data_out.write_pvd_record (pvd_output, times_and_names); + * } + * @endcode + * + * @note See DataOutBase::write_vtu or + * DataOutInterface::write_pvtu_record for + * writing solutions at each timestep. + * + * @note The second element of each pair, i.e., the file in which + * the graphical data for each time is stored, may itself be again + * a file that references other files. For example, it could be + * the name for a .pvtu file that references multiple + * parts of a parallel computation. + * + * @author Marco Engelhard, 2012 + */ + void write_pvd_record (std::ostream &out, + const std::vector > ×_and_names) const; + + /** + * This function is the exact + * equivalent of the + * write_pvtu_record() function + * but for the VisIt + * visualization program. See + * there for the purpose of this + * function. + * + * This function is documented + * in the "Creating a master file + * for parallel" section (section 5.7) + * of the "Getting data into VisIt" + * report that can be found here: + * https://wci.llnl.gov/codes/visit/2.0.0/GettingDataIntoVisIt2.0.0.pdf + */ + void write_visit_record (std::ostream &out, + const std::vector &piece_names) const; + + /** + * Obtain data through get_patches() + * and write it to out + * in deal.II intermediate + * format. See + * DataOutBase::write_deal_II_intermediate. + * + * Note that the intermediate + * format is what its name + * suggests: a direct + * representation of internal + * data. It isn't standardized + * and will change whenever we + * change our internal + * representation. You can only + * expect to process files + * written in this format using + * the same version of deal.II + * that was used for writing. + */ + void write_deal_II_intermediate (std::ostream &out) const; + + XDMFEntry create_xdmf_entry (const char *h5_filename, + const double cur_time, + MPI_Comm comm) const; + + void write_xdmf_file (const std::vector &entries, + const char *filename, + MPI_Comm comm) const; + + void write_hdf5_parallel (const char *filename, MPI_Comm comm) const; + /** + * Write data and grid to out + * according to the given data + * format. This function simply + * calls the appropriate + * write_* function. If no + * output format is requested, + * the default_format is + * written. + * + * An error occurs if no format + * is provided and the default + * format is default_format. + */ + void write (std::ostream &out, + const OutputFormat output_format = default_format) const; + + /** + * Set the default format. The + * value set here is used + * anytime, output for format + * default_format is + * requested. + */ + void set_default_format (const OutputFormat default_format); + + /** + * Set the flags to be used for + * output in OpenDX format. + */ + void set_flags (const DXFlags &dx_flags); + + /** + * Set the flags to be used for + * output in UCD format. + */ + void set_flags (const UcdFlags &ucd_flags); + + /** + * Set the flags to be used for + * output in GNUPLOT format. + */ + void set_flags (const GnuplotFlags &gnuplot_flags); + + /** + * Set the flags to be used for + * output in POVRAY format. + */ + void set_flags (const PovrayFlags &povray_flags); + + /** + * Set the flags to be used for + * output in EPS output. + */ + void set_flags (const EpsFlags &eps_flags); + + /** + * Set the flags to be used for + * output in GMV format. + */ + void set_flags (const GmvFlags &gmv_flags); + + /** + * Set the flags to be used for + * output in Tecplot format. + */ + void set_flags (const TecplotFlags &tecplot_flags); + + /** + * Set the flags to be used for + * output in VTK format. + */ + void set_flags (const VtkFlags &vtk_flags); + + /** + * Set the flags to be used for output in + * deal.II intermediate format. + */ + void set_flags (const Deal_II_IntermediateFlags &deal_II_intermediate_flags); + + /** + * A function that returns the same + * string as the respective function in + * the base class does; the only + * exception being that if the parameter + * is omitted, then the value for the + * present default format is returned, + * i.e. the correct suffix for the format + * that was set through + * set_default_format() or + * parse_parameters() before calling this + * function. + */ + std::string + default_suffix (const OutputFormat output_format = default_format) const; + + /** + * Declare parameters for all + * output formats by declaring + * subsections within the + * parameter file for each output + * format and call the respective + * declare_parameters + * functions of the flag classes + * for each output format. + * + * Some of the declared + * subsections may not contain + * entries, if the respective + * format does not export any + * flags. + * + * Note that the top-level + * parameters denoting the number + * of subdivisions per patch and + * the output format are not + * declared, since they are only + * passed to virtual functions + * and are not stored inside + * objects of this type. You have + * to declare them yourself. + */ + static void declare_parameters (ParameterHandler &prm); + + /** + * Read the parameters declared + * in declare_parameters and + * set the flags for the output + * formats accordingly. + * + * The flags thus obtained + * overwrite all previous + * contents of the flag objects + * as default-constructed or set + * by the set_flags() function. + */ + void parse_parameters (ParameterHandler &prm); + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + +protected: + /** + * This is the abstract function + * through which derived classes + * propagate preprocessed data in + * the form of Patch + * structures (declared in the + * base class DataOutBase) to + * the actual output + * function. You need to overload + * this function to allow the + * output functions to know what + * they shall print. + */ + virtual + const std::vector > & + get_patches () const = 0; + + /** + * Abstract virtual function + * through which the names of + * data sets are obtained by the + * output functions of the base + * class. + */ + virtual + std::vector + get_dataset_names () const = 0; + + /** + * This functions returns + * information about how the + * individual components of + * output files that consist of + * more than one data set are to + * be interpreted. + * + * It returns a list of index + * pairs and corresponding name + * indicating which components of + * the output are to be + * considered vector-valued + * rather than just a collection + * of scalar data. The index + * pairs are inclusive; for + * example, if we have a Stokes + * problem in 2d with components + * (u,v,p), then the + * corresponding vector data + * range should be (0,1), and the + * returned list would consist of + * only a single element with a + * tuple such as (0,1,"velocity"). + * + * Since some of the derived + * classes do not know about + * vector data, this function has + * a default implementation that + * simply returns an empty + * string, meaning that all data + * is to be considered a + * collection of scalar fields. + */ + virtual + std::vector > + get_vector_data_ranges () const; + + /** + * The default number of + * subdivisions for patches. This + * is filled by parse_parameters() + * and should be obeyed by + * build_patches() in derived + * classes. + */ + unsigned int default_subdivisions; - private: - /** - * Standard output format. Use - * this format, if output format - * default_format is - * requested. It can be changed - * by the set_format function - * or in a parameter file. - */ - OutputFormat default_fmt; - - /** - * Flags to be used upon output - * of OpenDX data. Can be changed by - * using the set_flags - * function. - */ - DXFlags dx_flags; - - /** - * Flags to be used upon output - * of UCD data. Can be changed by - * using the set_flags - * function. - */ - UcdFlags ucd_flags; - - /** - * Flags to be used upon output - * of GNUPLOT data. Can be - * changed by using the - * set_flags function. - */ - GnuplotFlags gnuplot_flags; - - /** - * Flags to be used upon output - * of POVRAY data. Can be changed - * by using the set_flags - * function. - */ - PovrayFlags povray_flags; - - /** - * Flags to be used upon output - * of EPS data in one space - * dimension. Can be changed by - * using the set_flags - * function. - */ - EpsFlags eps_flags; - - /** - * Flags to be used upon output - * of gmv data in one space - * dimension. Can be changed by - * using the set_flags - * function. - */ - GmvFlags gmv_flags; - - /** - * Flags to be used upon output - * of Tecplot data in one space - * dimension. Can be changed by - * using the set_flags - * function. - */ - TecplotFlags tecplot_flags; - - /** - * Flags to be used upon output - * of vtk data in one space - * dimension. Can be changed by - * using the set_flags - * function. - */ - VtkFlags vtk_flags; - - /** - * Flags to be used upon output of - * deal.II intermediate data in one space - * dimension. Can be changed by using the - * set_flags function. - */ - Deal_II_IntermediateFlags deal_II_intermediate_flags; +private: + /** + * Standard output format. Use + * this format, if output format + * default_format is + * requested. It can be changed + * by the set_format function + * or in a parameter file. + */ + OutputFormat default_fmt; + + /** + * Flags to be used upon output + * of OpenDX data. Can be changed by + * using the set_flags + * function. + */ + DXFlags dx_flags; + + /** + * Flags to be used upon output + * of UCD data. Can be changed by + * using the set_flags + * function. + */ + UcdFlags ucd_flags; + + /** + * Flags to be used upon output + * of GNUPLOT data. Can be + * changed by using the + * set_flags function. + */ + GnuplotFlags gnuplot_flags; + + /** + * Flags to be used upon output + * of POVRAY data. Can be changed + * by using the set_flags + * function. + */ + PovrayFlags povray_flags; + + /** + * Flags to be used upon output + * of EPS data in one space + * dimension. Can be changed by + * using the set_flags + * function. + */ + EpsFlags eps_flags; + + /** + * Flags to be used upon output + * of gmv data in one space + * dimension. Can be changed by + * using the set_flags + * function. + */ + GmvFlags gmv_flags; + + /** + * Flags to be used upon output + * of Tecplot data in one space + * dimension. Can be changed by + * using the set_flags + * function. + */ + TecplotFlags tecplot_flags; + + /** + * Flags to be used upon output + * of vtk data in one space + * dimension. Can be changed by + * using the set_flags + * function. + */ + VtkFlags vtk_flags; + + /** + * Flags to be used upon output of + * deal.II intermediate data in one space + * dimension. Can be changed by using the + * set_flags function. + */ + Deal_II_IntermediateFlags deal_II_intermediate_flags; }; @@ -2869,175 +2870,175 @@ class DataOutInterface : private DataOutBase template class DataOutReader : public DataOutInterface { - public: - /** - * Read a sequence of patches as - * written previously by - * DataOutBase::write_deal_II_intermediate - * and store them in the present - * object. This overwrites any - * previous content. - */ - void read (std::istream &in); - - /** - * This function can be used to - * merge the patches read by the - * other object into the patches - * that this present object - * stores. This is sometimes - * handy if one has, for example, - * a domain decomposition - * algorithm where each block is - * represented by a DoFHandler of - * its own, but one wants to - * output the solution on all the - * blocks at the same - * time. Alternatively, it may - * also be used for parallel - * programs, where each process - * only generates output for its - * share of the cells, even if - * all processes can see all - * cells. - * - * For this to work, the input - * files for the present object - * and the given argument need to - * have the same number of output - * vectors, and they need to use - * the same number of - * subdivisions per patch. The - * output will probably look - * rather funny if patches in - * both objects overlap in space. - * - * If you call read() for this - * object after merging in - * patches, the previous state is - * overwritten, and the merged-in - * patches are lost. - * - * This function will fail if - * either this or the other - * object did not yet set up any - * patches. - * - * The use of this function is - * demonstrated in step-19. - */ - void merge (const DataOutReader &other); - - /** - * Exception - */ - DeclException0 (ExcNoPatches); - /** - * Exception - */ - DeclException0 (ExcIncompatibleDatasetNames); - /** - * Exception - */ - DeclException0 (ExcIncompatiblePatchLists); - /** - * Exception - */ - DeclException4 (ExcIncompatibleDimensions, - int, int, int, int, - << "Either the dimensions <" << arg1 << "> and <" - << arg2 << "> or the space dimensions <" - << arg3 << "> and <" << arg4 - << "> do not match!"); - - protected: - /** - * This is the function - * through which this class - * propagates preprocessed data in - * the form of Patch - * structures (declared in the - * base class DataOutBase) to - * the actual output - * function. - * - * It returns the patches as read - * the last time a stream was - * given to the read() function. - */ - virtual const std::vector > & - get_patches () const; - - /** - * Abstract virtual function - * through which the names of - * data sets are obtained by the - * output functions of the base - * class. - * - * Return the names of the - * variables as read the last - * time we read a file. - */ - virtual std::vector get_dataset_names () const; - - /** - * This functions returns - * information about how the - * individual components of - * output files that consist of - * more than one data set are to - * be interpreted. - * - * It returns a list of index - * pairs and corresponding name - * indicating which components of - * the output are to be - * considered vector-valued - * rather than just a collection - * of scalar data. The index - * pairs are inclusive; for - * example, if we have a Stokes - * problem in 2d with components - * (u,v,p), then the - * corresponding vector data - * range should be (0,1), and the - * returned list would consist of - * only a single element with a - * tuple such as (0,1,"velocity"). - * - * Since some of the derived - * classes do not know about - * vector data, this function has - * a default implementation that - * simply returns an empty - * string, meaning that all data - * is to be considered a - * collection of scalar fields. - */ - virtual - std::vector > - get_vector_data_ranges () const; +public: + /** + * Read a sequence of patches as + * written previously by + * DataOutBase::write_deal_II_intermediate + * and store them in the present + * object. This overwrites any + * previous content. + */ + void read (std::istream &in); + + /** + * This function can be used to + * merge the patches read by the + * other object into the patches + * that this present object + * stores. This is sometimes + * handy if one has, for example, + * a domain decomposition + * algorithm where each block is + * represented by a DoFHandler of + * its own, but one wants to + * output the solution on all the + * blocks at the same + * time. Alternatively, it may + * also be used for parallel + * programs, where each process + * only generates output for its + * share of the cells, even if + * all processes can see all + * cells. + * + * For this to work, the input + * files for the present object + * and the given argument need to + * have the same number of output + * vectors, and they need to use + * the same number of + * subdivisions per patch. The + * output will probably look + * rather funny if patches in + * both objects overlap in space. + * + * If you call read() for this + * object after merging in + * patches, the previous state is + * overwritten, and the merged-in + * patches are lost. + * + * This function will fail if + * either this or the other + * object did not yet set up any + * patches. + * + * The use of this function is + * demonstrated in step-19. + */ + void merge (const DataOutReader &other); + + /** + * Exception + */ + DeclException0 (ExcNoPatches); + /** + * Exception + */ + DeclException0 (ExcIncompatibleDatasetNames); + /** + * Exception + */ + DeclException0 (ExcIncompatiblePatchLists); + /** + * Exception + */ + DeclException4 (ExcIncompatibleDimensions, + int, int, int, int, + << "Either the dimensions <" << arg1 << "> and <" + << arg2 << "> or the space dimensions <" + << arg3 << "> and <" << arg4 + << "> do not match!"); + +protected: + /** + * This is the function + * through which this class + * propagates preprocessed data in + * the form of Patch + * structures (declared in the + * base class DataOutBase) to + * the actual output + * function. + * + * It returns the patches as read + * the last time a stream was + * given to the read() function. + */ + virtual const std::vector > & + get_patches () const; + + /** + * Abstract virtual function + * through which the names of + * data sets are obtained by the + * output functions of the base + * class. + * + * Return the names of the + * variables as read the last + * time we read a file. + */ + virtual std::vector get_dataset_names () const; + + /** + * This functions returns + * information about how the + * individual components of + * output files that consist of + * more than one data set are to + * be interpreted. + * + * It returns a list of index + * pairs and corresponding name + * indicating which components of + * the output are to be + * considered vector-valued + * rather than just a collection + * of scalar data. The index + * pairs are inclusive; for + * example, if we have a Stokes + * problem in 2d with components + * (u,v,p), then the + * corresponding vector data + * range should be (0,1), and the + * returned list would consist of + * only a single element with a + * tuple such as (0,1,"velocity"). + * + * Since some of the derived + * classes do not know about + * vector data, this function has + * a default implementation that + * simply returns an empty + * string, meaning that all data + * is to be considered a + * collection of scalar fields. + */ + virtual + std::vector > + get_vector_data_ranges () const; - private: - /** - * Arrays holding the set of - * patches as well as the names - * of output variables, all of - * which we read from an input - * stream. - */ - std::vector > patches; - std::vector dataset_names; - - /** - * Information about whether - * certain components of the - * output field are to be - * considered vectors. - */ - std::vector > - vector_data_ranges; +private: + /** + * Arrays holding the set of + * patches as well as the names + * of output variables, all of + * which we read from an input + * stream. + */ + std::vector > patches; + std::vector dataset_names; + + /** + * Information about whether + * certain components of the + * output field are to be + * considered vectors. + */ + std::vector > + vector_data_ranges; }; @@ -3046,38 +3047,41 @@ class DataOutReader : public DataOutInterface // A class to store relevant data to use when writing the light data XDMF file // This should only contain valid data on the root node which writes the files, // the rest of the nodes will have valid set to false -class XDMFEntry { +class XDMFEntry +{ private: - // Whether this entry is valid and contains data to be written - bool valid; - // The name of the HDF5 heavy data file this entry references - std::string h5_filename; - // The simulation time associated with this entry - double entry_time; - // The number of nodes, cells and dimensionality associated with the data - unsigned int num_nodes, num_cells, dimension; - // The attributes associated with this entry and their dimension - std::map attribute_dims; - - // Small function to create indentation for XML file - std::string indent(const unsigned int indent_level) const { - std::string res = ""; - for (unsigned int i=0;i attribute_dims; + + // Small function to create indentation for XML file + std::string indent(const unsigned int indent_level) const + { + std::string res = ""; + for (unsigned int i=0; i class DerivativeForm { - public: - /** - * Constructor. Initialize all entries - * to zero. - */ - DerivativeForm (); - - /** - Constructor from a second order tensor. - */ - DerivativeForm (const Tensor<2,dim> &); - - /** - * Read-Write access operator. - */ - Tensor &operator [] (const unsigned int i); - - /** - * Read-only access operator. - */ - const Tensor &operator [] (const unsigned int i) const; - - /** - * Assignment operator. - */ - DerivativeForm & operator = (const DerivativeForm &); - - - /** - * Assignment operator. - */ - DerivativeForm & operator = (const Tensor<2,dim> &); - - /** - * Assignment operator. - */ - DerivativeForm & operator = (const Tensor<1,dim> &); - - /** - Converts a DerivativeForm <1,dim, dim> - to Tensor<2,dim>. - If the derivative is the Jacobian of F, - then Tensor[i] = grad(F^i). - */ - operator Tensor<2,dim>() const; - - /** - Converts a DerivativeForm <1, dim, 1> - to Tensor<1,dim>. - */ - operator Tensor<1,dim>() const; - - /** - Return the transpose of a rectangular DerivativeForm, - that is to say viewed as a two dimensional matrix. - */ - DerivativeForm<1, spacedim, dim> transpose () const; - - - /** - Computes the volume element associated with the - jacobian of the tranformation F. - That is to say if $DF$ is square, it computes - $\det(DF)$, in case DF is not square returns - $\sqrt(\det(DF^{t} * DF))$. - */ - double determinant () const; - - - /** - Assuming (*this) stores the jacobian of - the mapping F, it computes its covariant - matrix, namely $DF*G^{-1}$, where - $G = DF^{t}*DF$. - If $DF$ is square, covariant from - gives $DF^{-t}$. - */ - DerivativeForm<1, dim, spacedim> covariant_form() const; - - - - - /** - * Determine an estimate for the - * memory consumption (in bytes) - * of this object. - */ - static std::size_t memory_consumption (); - - /** - * Exception. - */ - DeclException1 (ExcInvalidTensorIndex, - int, - << "Invalid DerivativeForm index " << arg1); - - - private: - /** Auxiliary function that computes - (*this) * T^{t} */ - DerivativeForm<1, dim, spacedim> times_T_t (Tensor<2,dim> T) const; - - - private: - /** - * Array of tensors holding the - * subelements. - */ - Tensor tensor[spacedim]; +public: + /** + * Constructor. Initialize all entries + * to zero. + */ + DerivativeForm (); + + /** + Constructor from a second order tensor. + */ + DerivativeForm (const Tensor<2,dim> &); + + /** + * Read-Write access operator. + */ + Tensor &operator [] (const unsigned int i); + + /** + * Read-only access operator. + */ + const Tensor &operator [] (const unsigned int i) const; + + /** + * Assignment operator. + */ + DerivativeForm &operator = (const DerivativeForm &); + + + /** + * Assignment operator. + */ + DerivativeForm &operator = (const Tensor<2,dim> &); + + /** + * Assignment operator. + */ + DerivativeForm &operator = (const Tensor<1,dim> &); + + /** + Converts a DerivativeForm <1,dim, dim> + to Tensor<2,dim>. + If the derivative is the Jacobian of F, + then Tensor[i] = grad(F^i). + */ + operator Tensor<2,dim>() const; + + /** + Converts a DerivativeForm <1, dim, 1> + to Tensor<1,dim>. + */ + operator Tensor<1,dim>() const; + + /** + Return the transpose of a rectangular DerivativeForm, + that is to say viewed as a two dimensional matrix. + */ + DerivativeForm<1, spacedim, dim> transpose () const; + + + /** + Computes the volume element associated with the + jacobian of the tranformation F. + That is to say if $DF$ is square, it computes + $\det(DF)$, in case DF is not square returns + $\sqrt(\det(DF^{t} * DF))$. + */ + double determinant () const; + + + /** + Assuming (*this) stores the jacobian of + the mapping F, it computes its covariant + matrix, namely $DF*G^{-1}$, where + $G = DF^{t}*DF$. + If $DF$ is square, covariant from + gives $DF^{-t}$. + */ + DerivativeForm<1, dim, spacedim> covariant_form() const; + + + + + /** + * Determine an estimate for the + * memory consumption (in bytes) + * of this object. + */ + static std::size_t memory_consumption (); + + /** + * Exception. + */ + DeclException1 (ExcInvalidTensorIndex, + int, + << "Invalid DerivativeForm index " << arg1); + + +private: + /** Auxiliary function that computes + (*this) * T^{t} */ + DerivativeForm<1, dim, spacedim> times_T_t (Tensor<2,dim> T) const; + + +private: + /** + * Array of tensors holding the + * subelements. + */ + Tensor tensor[spacedim]; }; @@ -187,7 +187,7 @@ operator = (const DerivativeForm &ta) template inline -DerivativeForm & DerivativeForm:: +DerivativeForm &DerivativeForm:: operator = (const Tensor<2,dim> &ta) { Assert( (dim == spacedim) && (order==1), @@ -203,7 +203,7 @@ operator = (const Tensor<2,dim> &ta) template inline -DerivativeForm & DerivativeForm:: +DerivativeForm &DerivativeForm:: operator = (const Tensor<1,dim> &T) { Assert( (1 == spacedim) && (order==1), @@ -219,7 +219,7 @@ operator = (const Tensor<1,dim> &T) template inline -Tensor & DerivativeForm:: +Tensor &DerivativeForm:: operator[] (const unsigned int i) { Assert (i DerivativeForm:: transpose () const - { - Assert(order==1, ExcMessage("Only for rectangular DerivativeForm.")); - DerivativeForm<1,spacedim,dim> tt; +{ + Assert(order==1, ExcMessage("Only for rectangular DerivativeForm.")); + DerivativeForm<1,spacedim,dim> tt; - for (unsigned int i=0; i &DF, for (unsigned int i=0; i inline DerivativeForm<1,spacedim,dim> transpose (const DerivativeForm<1,dim,spacedim> &DF) - { - DerivativeForm<1,spacedim,dim> tt; - tt = DF.transpose(); - return tt; - } +{ + DerivativeForm<1,spacedim,dim> tt; + tt = DF.transpose(); + return tt; +} DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/include/deal.II/base/event.h b/deal.II/include/deal.II/base/event.h index c691c804d1..7d1b92cb6a 100644 --- a/deal.II/include/deal.II/base/event.h +++ b/deal.II/include/deal.II/base/event.h @@ -23,146 +23,146 @@ DEAL_II_NAMESPACE_OPEN namespace Algorithms { -/** - * Objects of this kind are used to notify interior applications of - * changes provoked by an outer loop. They are handed to the - * application through Operator::notify() and it is up to the - * actual application how to handle them. - * - * Event is organized as an extensible binary enumerator. Every class - * can add its own events using assign(). A typical code example is - * - * @code - * class A - * { - * static Event event; - * }; - * - * Event A::event = Event::assign("Event for A"); - * @endcode - */ + /** + * Objects of this kind are used to notify interior applications of + * changes provoked by an outer loop. They are handed to the + * application through Operator::notify() and it is up to the + * actual application how to handle them. + * + * Event is organized as an extensible binary enumerator. Every class + * can add its own events using assign(). A typical code example is + * + * @code + * class A + * { + * static Event event; + * }; + * + * Event A::event = Event::assign("Event for A"); + * @endcode + */ class Event { - public: - /** - * This function registers a - * new event type and assigns a - * unique identifier to it. The - * result of this function - * should be stored for later - * use. - */ - static Event assign (const char* name); - - /** - * If you forgot to store the - * result of assign, here is - * how to retrieve it knowing - * the name. - */ + public: + /** + * This function registers a + * new event type and assigns a + * unique identifier to it. The + * result of this function + * should be stored for later + * use. + */ + static Event assign (const char *name); + + /** + * If you forgot to store the + * result of assign, here is + * how to retrieve it knowing + * the name. + */ // static Event find(const std::string& name); - /** - * Constructor, generating a - * clear Event. - */ - Event (); - - /** - * Clear all flags - */ - void clear(); - - /** - * Set all flags - */ - void all(); - - /** - * Add the flags of the other event - */ - Event& operator += (const Event& event); - - /** - * Clear the flags of the other event - */ - Event& operator -= (const Event& event); - - /** - * Test whether all the flags - * set in the other Event are - * also set in this one. - */ - bool test (const Event& event) const; - - /** - * Return true if any - * event is set. - */ - bool any () const; - - /** - * List the flags to a stream. - */ - template - void print (OS& os) const; - - /** - * List all assigned events. - */ - template - static void print_assigned (OS& os); - - private: - /** - * Sometimes, actions have to - * be taken by all - * means. Therefore, if this - * value is true, test() always - * returns true. - */ - bool all_true; - - /** - * The actual list of events - */ - std::vector flags; - - /** - * The names of registered events - */ + /** + * Constructor, generating a + * clear Event. + */ + Event (); + + /** + * Clear all flags + */ + void clear(); + + /** + * Set all flags + */ + void all(); + + /** + * Add the flags of the other event + */ + Event &operator += (const Event &event); + + /** + * Clear the flags of the other event + */ + Event &operator -= (const Event &event); + + /** + * Test whether all the flags + * set in the other Event are + * also set in this one. + */ + bool test (const Event &event) const; + + /** + * Return true if any + * event is set. + */ + bool any () const; + + /** + * List the flags to a stream. + */ + template + void print (OS &os) const; + + /** + * List all assigned events. + */ + template + static void print_assigned (OS &os); + + private: + /** + * Sometimes, actions have to + * be taken by all + * means. Therefore, if this + * value is true, test() always + * returns true. + */ + bool all_true; + + /** + * The actual list of events + */ + std::vector flags; + + /** + * The names of registered events + */ //TODO: This static field must be guarded by a mutex to be thread-safe! - static std::vector names; + static std::vector names; }; -/** - * Events used by library operators - */ + /** + * Events used by library operators + */ namespace Events { - /** - * The program has just started - * and everything should be - * new. - */ + /** + * The program has just started + * and everything should be + * new. + */ extern const Event initial; - /** - * The current derivative leads - * to slow convergence of - * Newton's method. - */ + /** + * The current derivative leads + * to slow convergence of + * Newton's method. + */ extern const Event bad_derivative; - /** - * The time stepping scheme - * starts a new time step. - */ + /** + * The time stepping scheme + * starts a new time step. + */ extern const Event new_time; - /** - * The time stepping scheme has changed the time step size. - */ + /** + * The time stepping scheme has changed the time step size. + */ extern const Event new_timestep_size; } @@ -184,42 +184,42 @@ namespace Algorithms inline bool - Event::test (const Event& event) const + Event::test (const Event &event) const { - // First, test all_true in this + // First, test all_true in this if (all_true) return true; const unsigned int n = flags.size(); const unsigned int m = event.flags.size(); const unsigned int n_min = std::min(n, m); - // Now, if all_true set in the - // other, then all must be true - // in this + // Now, if all_true set in the + // other, then all must be true + // in this if (event.all_true) { - // Non existing flags are - // always assumed false + // Non existing flags are + // always assumed false if (m > n) return false; - // Test all flags separately - // and return false if one is - // not set + // Test all flags separately + // and return false if one is + // not set for (std::vector::const_iterator i=flags.begin(); i != flags.end(); ++i) if (!*i) return false; - // All flags are set + // All flags are set return true; } - // Finally, compare each flag - // separately - for (unsigned int i=0;i::iterator i=flags.begin(); i != flags.end(); ++i) @@ -258,7 +258,7 @@ namespace Algorithms if (flags.size() < event.flags.size()) flags.resize(event.flags.size()); - for (unsigned int i=0;i inline void - Event::print (OS& os) const + Event::print (OS &os) const { if (all_true) os << " ALL"; - for (unsigned int i=0;i inline void - Event::print_assigned (OS& os) + Event::print_assigned (OS &os) { - for (unsigned int i=0;i - OS& operator << (OS& o, const Event& e) + OS &operator << (OS &o, const Event &e) { e.print(o); return o; diff --git a/deal.II/include/deal.II/base/exceptions.h b/deal.II/include/deal.II/base/exceptions.h index 87c3c8fc09..ed9b1615e1 100644 --- a/deal.II/include/deal.II/base/exceptions.h +++ b/deal.II/include/deal.II/base/exceptions.h @@ -47,131 +47,131 @@ DEAL_II_NAMESPACE_OPEN */ class ExceptionBase : public std::exception { - public: - /** - * Default constructor. - */ - ExceptionBase (); - - /** - * The constructor takes the file in which the - * error happened, the line and the violated - * condition as well as the name of the - * exception class as a char* as arguments. - */ - ExceptionBase (const char* f, const int l, const char *func, - const char* c, const char *e); - - /** - * Copy constructor. - */ - ExceptionBase (const ExceptionBase &exc); - - /** - * Destructor. Empty, but needed - * for the sake of exception - * specification, since the base - * class has this exception - * specification and the - * automatically generated - * destructor would have a - * different one due to member - * objects. - */ - virtual ~ExceptionBase () throw(); - - /** - * Set the file name and line of where the - * exception appeared as well as the violated - * condition and the name of the exception as - * a char pointer. - */ - void set_fields (const char *f, - const int l, - const char *func, - const char *c, - const char *e); - - /** - * Print out the general part of the error - * information. - */ - void print_exc_data (std::ostream &out) const; - - /** - * Print more specific information about the - * exception which occured. Overload this - * function in your own exception classes. - */ - virtual void print_info (std::ostream &out) const; - - - /** - * Function derived from the base class - * which allows to pass information like - * the line and name of the file where the - * exception occurred as well as user - * information. - * - * This function is mainly used - * when using exceptions - * declared by the - * DeclException* - * macros with the - * throw mechanism or - * the AssertThrow - * macro. - */ - virtual const char * what () const throw (); - - /** - * Print a stacktrace, if one has - * been recorded previously, to - * the given stream. - */ - void print_stack_trace (std::ostream &out) const; - - protected: - /** - * Name of the file this exception happen in. - */ - const char *file; - - /** - * Line number in this file. - */ - unsigned int line; - - /** - * Name of the function, pretty printed. - */ - const char *function; - - /** - * The violated condition, as a string. - */ - const char *cond; - - /** - * Name of the exception and call sequence. - */ - const char *exc; - - /** - * A backtrace to the position - * where the problem happened, if - * the system supports this. - */ - char ** stacktrace; - - /** - * The number of stacktrace - * frames that are stored in the - * previous variable. Zero if the - * system does not support stack - * traces. - */ - int n_stacktrace_frames; +public: + /** + * Default constructor. + */ + ExceptionBase (); + + /** + * The constructor takes the file in which the + * error happened, the line and the violated + * condition as well as the name of the + * exception class as a char* as arguments. + */ + ExceptionBase (const char *f, const int l, const char *func, + const char *c, const char *e); + + /** + * Copy constructor. + */ + ExceptionBase (const ExceptionBase &exc); + + /** + * Destructor. Empty, but needed + * for the sake of exception + * specification, since the base + * class has this exception + * specification and the + * automatically generated + * destructor would have a + * different one due to member + * objects. + */ + virtual ~ExceptionBase () throw(); + + /** + * Set the file name and line of where the + * exception appeared as well as the violated + * condition and the name of the exception as + * a char pointer. + */ + void set_fields (const char *f, + const int l, + const char *func, + const char *c, + const char *e); + + /** + * Print out the general part of the error + * information. + */ + void print_exc_data (std::ostream &out) const; + + /** + * Print more specific information about the + * exception which occured. Overload this + * function in your own exception classes. + */ + virtual void print_info (std::ostream &out) const; + + + /** + * Function derived from the base class + * which allows to pass information like + * the line and name of the file where the + * exception occurred as well as user + * information. + * + * This function is mainly used + * when using exceptions + * declared by the + * DeclException* + * macros with the + * throw mechanism or + * the AssertThrow + * macro. + */ + virtual const char *what () const throw (); + + /** + * Print a stacktrace, if one has + * been recorded previously, to + * the given stream. + */ + void print_stack_trace (std::ostream &out) const; + +protected: + /** + * Name of the file this exception happen in. + */ + const char *file; + + /** + * Line number in this file. + */ + unsigned int line; + + /** + * Name of the function, pretty printed. + */ + const char *function; + + /** + * The violated condition, as a string. + */ + const char *cond; + + /** + * Name of the exception and call sequence. + */ + const char *exc; + + /** + * A backtrace to the position + * where the problem happened, if + * the system supports this. + */ + char **stacktrace; + + /** + * The number of stacktrace + * frames that are stored in the + * previous variable. Zero if the + * system does not support stack + * traces. + */ + int n_stacktrace_frames; }; @@ -186,95 +186,95 @@ class ExceptionBase : public std::exception namespace deal_II_exceptions { - /** - * Set a string that is printed - * upon output of the message - * indicating a triggered - * Assert statement. This - * string, which is printed in - * addition to the usual output may - * indicate information that is - * otherwise not readily available - * unless we are using a - * debugger. For example, with - * distributed programs on cluster - * computers, the output of all - * processes is redirected to the - * same console window. In this - * case, it is convenient to set as - * additional name the name of the - * host on which the program runs, - * so that one can see in which - * instance of the program the - * exception occured. - * - * The string pointed to by the - * argument is copied, so needs not - * be stored after the call to this - * function. - * - * Previously set additional output - * is replaced by the argument - * given to this function. - */ - void set_additional_assert_output (const char * const p); - - /** - * Calling this function disables - * printing a stacktrace along with - * the other output printed when an - * exception occurs. Most of the - * time, you will want to see such - * a stacktrace; suppressing it, - * however, is useful if one wants - * to compare the output of a - * program across different - * machines and systems, since the - * stacktrace shows memory - * addresses and library - * names/paths that depend on the - * exact setup of a machine. - */ + /** + * Set a string that is printed + * upon output of the message + * indicating a triggered + * Assert statement. This + * string, which is printed in + * addition to the usual output may + * indicate information that is + * otherwise not readily available + * unless we are using a + * debugger. For example, with + * distributed programs on cluster + * computers, the output of all + * processes is redirected to the + * same console window. In this + * case, it is convenient to set as + * additional name the name of the + * host on which the program runs, + * so that one can see in which + * instance of the program the + * exception occured. + * + * The string pointed to by the + * argument is copied, so needs not + * be stored after the call to this + * function. + * + * Previously set additional output + * is replaced by the argument + * given to this function. + */ + void set_additional_assert_output (const char *const p); + + /** + * Calling this function disables + * printing a stacktrace along with + * the other output printed when an + * exception occurs. Most of the + * time, you will want to see such + * a stacktrace; suppressing it, + * however, is useful if one wants + * to compare the output of a + * program across different + * machines and systems, since the + * stacktrace shows memory + * addresses and library + * names/paths that depend on the + * exact setup of a machine. + */ void suppress_stacktrace_in_exceptions (); - /** - * Calling this function switches off - * the use of std::abort() when an - * exception is created using the - * Assert() macro. Generally, you - * want to abort the execution of a - * program when Assert() is called, - * but it needs to be switched off - * if you want to log all exceptions - * created, or if you want to test - * if an assertion is working - * correctly. This is done for example - * in regression tests. - * Please note that some fatal - * errors will still call abort(), e.g. - * when an exception is caught - * during exception handling - */ + /** + * Calling this function switches off + * the use of std::abort() when an + * exception is created using the + * Assert() macro. Generally, you + * want to abort the execution of a + * program when Assert() is called, + * but it needs to be switched off + * if you want to log all exceptions + * created, or if you want to test + * if an assertion is working + * correctly. This is done for example + * in regression tests. + * Please note that some fatal + * errors will still call abort(), e.g. + * when an exception is caught + * during exception handling + */ void disable_abort_on_exception (); -/** - * The functions in this namespace are in connection with the Assert - * and AssertThrow mechanism but are solely for internal purposes and - * are not for use outside the exception handling and throwing - * mechanism. - * - * @ingroup Exceptions - */ + /** + * The functions in this namespace are in connection with the Assert + * and AssertThrow mechanism but are solely for internal purposes and + * are not for use outside the exception handling and throwing + * mechanism. + * + * @ingroup Exceptions + */ namespace internals { -/** - * This routine does the main work for the - * exception generation mechanism used in the - * Assert macro. - * - * @ref ExceptionBase - */ + /** + * This routine does the main work for the + * exception generation mechanism used in the + * Assert macro. + * + * @ref ExceptionBase + */ void issue_error_assert (const char *file, int line, const char *function, @@ -283,13 +283,13 @@ namespace deal_II_exceptions ExceptionBase &e); -/** - * This routine does the main work for the - * exception generation mechanism used in the - * AssertThrow macro. - * - * @ref ExceptionBase - */ + /** + * This routine does the main work for the + * exception generation mechanism used in the + * AssertThrow macro. + * + * @ref ExceptionBase + */ template void issue_error_throw (const char *file, int line, @@ -298,23 +298,23 @@ namespace deal_II_exceptions const char *exc_name, exc e) { - // Fill the fields of the - // exception object + // Fill the fields of the + // exception object e.set_fields (file, line, function, cond, exc_name); throw e; } -/** - * Relay exceptions from the Assert macro to the - * __IssueError_Assert function. Used to convert the last - * argument from arbitrary type to ExceptionBase which is not - * possible inside the Assert macro due to syntactical - * difficulties in connection with the way we use the macro and the - * declaration of the exception classes. - * - * @ref ExceptionBase - */ + /** + * Relay exceptions from the Assert macro to the + * __IssueError_Assert function. Used to convert the last + * argument from arbitrary type to ExceptionBase which is not + * possible inside the Assert macro due to syntactical + * difficulties in connection with the way we use the macro and the + * declaration of the exception classes. + * + * @ref ExceptionBase + */ template inline void issue_error_assert_1 (const char *file, @@ -329,12 +329,12 @@ namespace deal_II_exceptions -/** - * Abort the program. This function is used so that we need not - * include cstdlib into this file since it is included into all - * other files of the library and we would like to keep its include - * list as short as possible. - */ + /** + * Abort the program. This function is used so that we need not + * include cstdlib into this file since it is included into all + * other files of the library and we would like to keep its include + * list as short as possible. + */ void abort (); } @@ -360,8 +360,8 @@ namespace deal_II_exceptions if (!(cond)) \ ::dealii::deal_II_exceptions::internals:: \ issue_error_assert_1 (__FILE__, \ - __LINE__, \ - __PRETTY_FUNCTION__, #cond, #exc, exc);\ + __LINE__, \ + __PRETTY_FUNCTION__, #cond, #exc, exc);\ } @@ -397,29 +397,29 @@ namespace deal_II_exceptions #ifndef DISABLE_ASSERT_THROW # ifndef HAVE_BUILTIN_EXPECT # define AssertThrow(cond, exc) \ - { \ - if (!(cond)) \ - ::dealii::deal_II_exceptions::internals:: \ - issue_error_throw (__FILE__, \ - __LINE__, \ - __PRETTY_FUNCTION__, #cond, #exc, exc); \ - } + { \ + if (!(cond)) \ + ::dealii::deal_II_exceptions::internals:: \ + issue_error_throw (__FILE__, \ + __LINE__, \ + __PRETTY_FUNCTION__, #cond, #exc, exc); \ + } # else // HAVE_BUILTIN_EXPECT # define AssertThrow(cond, exc) \ - { \ - if (__builtin_expect(!(cond), false)) \ - ::dealii::deal_II_exceptions::internals:: \ - issue_error_throw (__FILE__, \ - __LINE__, \ - __PRETTY_FUNCTION__, #cond, #exc, exc); \ - } + { \ + if (__builtin_expect(!(cond), false)) \ + ::dealii::deal_II_exceptions::internals:: \ + issue_error_throw (__FILE__, \ + __LINE__, \ + __PRETTY_FUNCTION__, #cond, #exc, exc); \ + } # endif #else # define AssertThrow(cond, exc) \ - { \ - if (!(cond)) \ - ::dealii::deal_II_exceptions::internals::abort (); \ - } + { \ + if (!(cond)) \ + ::dealii::deal_II_exceptions::internals::abort (); \ + } #endif @@ -432,7 +432,7 @@ namespace deal_II_exceptions * @ingroup Exceptions */ #define DeclException0(Exception0) \ -class Exception0 : public dealii::ExceptionBase {} + class Exception0 : public dealii::ExceptionBase {} @@ -442,16 +442,16 @@ class Exception0 : public dealii::ExceptionBase {} * @ingroup Exceptions */ #define DeclException1(Exception1, type1, outsequence) \ -class Exception1 : public dealii::ExceptionBase { \ + class Exception1 : public dealii::ExceptionBase { \ public: \ - Exception1 (const type1 a1) : arg1 (a1) {} \ - virtual ~Exception1 () throw () {} \ - virtual void print_info (std::ostream &out) const { \ - out outsequence << std::endl; \ - } \ + Exception1 (const type1 a1) : arg1 (a1) {} \ + virtual ~Exception1 () throw () {} \ + virtual void print_info (std::ostream &out) const { \ + out outsequence << std::endl; \ + } \ private: \ - const type1 arg1; \ -} + const type1 arg1; \ + } @@ -461,18 +461,18 @@ class Exception1 : public dealii::ExceptionBase { \ * @ingroup Exceptions */ #define DeclException2(Exception2, type1, type2, outsequence) \ -class Exception2 : public dealii::ExceptionBase { \ + class Exception2 : public dealii::ExceptionBase { \ public: \ - Exception2 (const type1 a1, const type2 a2) : \ - arg1 (a1), arg2(a2) {} \ - virtual ~Exception2 () throw () {} \ - virtual void print_info (std::ostream &out) const { \ - out outsequence << std::endl; \ - } \ + Exception2 (const type1 a1, const type2 a2) : \ + arg1 (a1), arg2(a2) {} \ + virtual ~Exception2 () throw () {} \ + virtual void print_info (std::ostream &out) const { \ + out outsequence << std::endl; \ + } \ private: \ - const type1 arg1; \ - const type2 arg2; \ -} + const type1 arg1; \ + const type2 arg2; \ + } @@ -482,19 +482,19 @@ class Exception2 : public dealii::ExceptionBase { \ * @ingroup Exceptions */ #define DeclException3(Exception3, type1, type2, type3, outsequence) \ -class Exception3 : public dealii::ExceptionBase { \ + class Exception3 : public dealii::ExceptionBase { \ public: \ - Exception3 (const type1 a1, const type2 a2, const type3 a3) : \ - arg1 (a1), arg2(a2), arg3(a3) {} \ - virtual ~Exception3 () throw () {} \ - virtual void print_info (std::ostream &out) const { \ - out outsequence << std::endl; \ - } \ + Exception3 (const type1 a1, const type2 a2, const type3 a3) : \ + arg1 (a1), arg2(a2), arg3(a3) {} \ + virtual ~Exception3 () throw () {} \ + virtual void print_info (std::ostream &out) const { \ + out outsequence << std::endl; \ + } \ private: \ - const type1 arg1; \ - const type2 arg2; \ - const type3 arg3; \ -} + const type1 arg1; \ + const type2 arg2; \ + const type3 arg3; \ + } @@ -504,21 +504,21 @@ class Exception3 : public dealii::ExceptionBase { \ * @ingroup Exceptions */ #define DeclException4(Exception4, type1, type2, type3, type4, outsequence) \ -class Exception4 : public dealii::ExceptionBase { \ + class Exception4 : public dealii::ExceptionBase { \ public: \ - Exception4 (const type1 a1, const type2 a2, \ - const type3 a3, const type4 a4) : \ - arg1 (a1), arg2(a2), arg3(a3), arg4(a4) {} \ - virtual ~Exception4 () throw () {} \ - virtual void print_info (std::ostream &out) const { \ - out outsequence << std::endl; \ - } \ + Exception4 (const type1 a1, const type2 a2, \ + const type3 a3, const type4 a4) : \ + arg1 (a1), arg2(a2), arg3(a3), arg4(a4) {} \ + virtual ~Exception4 () throw () {} \ + virtual void print_info (std::ostream &out) const { \ + out outsequence << std::endl; \ + } \ private: \ - const type1 arg1; \ - const type2 arg2; \ - const type3 arg3; \ - const type4 arg4; \ -} + const type1 arg1; \ + const type2 arg2; \ + const type3 arg3; \ + const type4 arg4; \ + } @@ -528,22 +528,22 @@ class Exception4 : public dealii::ExceptionBase { \ * @ingroup Exceptions */ #define DeclException5(Exception5, type1, type2, type3, type4, type5, outsequence) \ -class Exception5 : public dealii::ExceptionBase { \ + class Exception5 : public dealii::ExceptionBase { \ public: \ - Exception5 (const type1 a1, const type2 a2, const type3 a3, \ - const type4 a4, const type5 a5) : \ - arg1 (a1), arg2(a2), arg3(a3), arg4(a4), arg5(a5) {} \ - virtual ~Exception5 () throw () {} \ - virtual void print_info (std::ostream &out) const { \ - out outsequence << std::endl; \ - } \ + Exception5 (const type1 a1, const type2 a2, const type3 a3, \ + const type4 a4, const type5 a5) : \ + arg1 (a1), arg2(a2), arg3(a3), arg4(a4), arg5(a5) {} \ + virtual ~Exception5 () throw () {} \ + virtual void print_info (std::ostream &out) const { \ + out outsequence << std::endl; \ + } \ private: \ - const type1 arg1; \ - const type2 arg2; \ - const type3 arg3; \ - const type4 arg4; \ - const type5 arg5; \ -} + const type1 arg1; \ + const type2 arg2; \ + const type3 arg3; \ + const type4 arg4; \ + const type5 arg5; \ + } #else // ifndef DOXYGEN @@ -621,320 +621,320 @@ class Exception5 : public dealii::ExceptionBase { \ */ namespace StandardExceptions { - /** - * @addtogroup Exceptions - */ - //@{ - - /** - * Exception denoting a division by - * zero. - * - * @note Unfortunately, automatic - * detection of division by zero is - * very hardware dependent and - * requires severe hacking on some - * architectures. Therefore, this - * exception is only raised if the - * test s performed explicitly. - */ + /** + * @addtogroup Exceptions + */ + //@{ + + /** + * Exception denoting a division by + * zero. + * + * @note Unfortunately, automatic + * detection of division by zero is + * very hardware dependent and + * requires severe hacking on some + * architectures. Therefore, this + * exception is only raised if the + * test s performed explicitly. + */ DeclException0 (ExcDivideByZero); - /** - * Exception raised if a number is - * not finite. - * - * This exception should be used to - * catch infinite or not a number - * results of arithmetic operations - * that do not result from a - * division by zero (use - * ExcDivideByZero for those). - */ + /** + * Exception raised if a number is + * not finite. + * + * This exception should be used to + * catch infinite or not a number + * results of arithmetic operations + * that do not result from a + * division by zero (use + * ExcDivideByZero for those). + */ DeclException0 (ExcNumberNotFinite); - /** - * Trying to allocate a new - * object failed due to lack of - * free memory. - */ + /** + * Trying to allocate a new + * object failed due to lack of + * free memory. + */ DeclException0 (ExcOutOfMemory); - /** - * A memory handler reached a point - * where all allocated objects - * should have been released. Since - * this exception is thrown, some - * were still allocated. - */ + /** + * A memory handler reached a point + * where all allocated objects + * should have been released. Since + * this exception is thrown, some + * were still allocated. + */ DeclException1 (ExcMemoryLeak, int, << "Destroying memory handler while " << arg1 << " objects are still allocated"); - /** - * An error occurred reading or - * writing a file. - */ + /** + * An error occurred reading or + * writing a file. + */ DeclException0 (ExcIO); - /** - * An error occurred opening the named file. - * - * The constructor takes a single - * argument of type char* - * naming the file. - */ + /** + * An error occurred opening the named file. + * + * The constructor takes a single + * argument of type char* + * naming the file. + */ DeclException1 (ExcFileNotOpen, - char*, + char *, << "Could not open file " << arg1); - /** - * Exception denoting a part of the - * library or application program - * that has not yet been - * implemented. In many cases, this - * only indicates that there wasn't - * much need for something yet, not - * that this is difficult to - * implement. It is therefore quite - * worth the effort to take a look - * at the corresponding place and - * see whether it can be - * implemented without too much - * effort. - */ + /** + * Exception denoting a part of the + * library or application program + * that has not yet been + * implemented. In many cases, this + * only indicates that there wasn't + * much need for something yet, not + * that this is difficult to + * implement. It is therefore quite + * worth the effort to take a look + * at the corresponding place and + * see whether it can be + * implemented without too much + * effort. + */ DeclException0 (ExcNotImplemented); - /** - * This exception usually indicates - * that some condition which the - * programmer thinks must be - * satisfied at a certain point in - * an algorithm, is not - * fulfilled. This might be due to - * some programming error above, - * due to changes to the algorithm - * that did not preserve this - * assertion, or due to assumptions - * the programmer made that are not - * valid at all (i.e. the exception - * is thrown although there is no - * error here). Within the library, - * this exception is most often - * used when we write some kind of - * complicated algorithm and are - * not yet sure whether we got it - * right; we then put in assertions - * after each part of the algorithm - * that check for some conditions - * that should hold there, and - * throw an exception if they do - * not. - * - * We usually leave in these - * assertions even after we are - * confident that the - * implementation is correct, since - * if someone later changes or - * extends the algorithm, these - * exceptions will indicate to him - * if he violates assumptions that - * are used later in the - * algorithm. Furthermore, it - * sometimes happens that an - * algorithm does not work in very - * rare corner cases. These cases - * will then be trapped sooner or - * later by the exception, so that - * the algorithm can then be fixed - * for these cases as well. - */ + /** + * This exception usually indicates + * that some condition which the + * programmer thinks must be + * satisfied at a certain point in + * an algorithm, is not + * fulfilled. This might be due to + * some programming error above, + * due to changes to the algorithm + * that did not preserve this + * assertion, or due to assumptions + * the programmer made that are not + * valid at all (i.e. the exception + * is thrown although there is no + * error here). Within the library, + * this exception is most often + * used when we write some kind of + * complicated algorithm and are + * not yet sure whether we got it + * right; we then put in assertions + * after each part of the algorithm + * that check for some conditions + * that should hold there, and + * throw an exception if they do + * not. + * + * We usually leave in these + * assertions even after we are + * confident that the + * implementation is correct, since + * if someone later changes or + * extends the algorithm, these + * exceptions will indicate to him + * if he violates assumptions that + * are used later in the + * algorithm. Furthermore, it + * sometimes happens that an + * algorithm does not work in very + * rare corner cases. These cases + * will then be trapped sooner or + * later by the exception, so that + * the algorithm can then be fixed + * for these cases as well. + */ DeclException0 (ExcInternalError); - /** - * This exception is used in - * functions that may not be called - * (i.e. in pure functions) but - * could not be declared pure since - * the class is intended to be used - * anyway, even though the - * respective function may only be - * called if a derived class is - * used. - */ + /** + * This exception is used in + * functions that may not be called + * (i.e. in pure functions) but + * could not be declared pure since + * the class is intended to be used + * anyway, even though the + * respective function may only be + * called if a derived class is + * used. + */ DeclException0 (ExcPureFunctionCalled); - /** - * Used for constructors that are - * disabled. Examples are copy - * constructors and assignment - * operators of large objects, - * which are only allowed for empty - * objects. - */ + /** + * Used for constructors that are + * disabled. Examples are copy + * constructors and assignment + * operators of large objects, + * which are only allowed for empty + * objects. + */ DeclException0 (ExcInvalidConstructorCall); - /** - * This exception is used if some - * object is found uninitialized. - */ + /** + * This exception is used if some + * object is found uninitialized. + */ DeclException0 (ExcNotInitialized); - /** - * The object is in a state not - * suitable for this operation. - */ + /** + * The object is in a state not + * suitable for this operation. + */ DeclException0 (ExcInvalidState); - /** - * This exception is raised if a - * functionality is not possible in - * the given dimension. Mostly used - * to throw function calls in 1d. - * - * The constructor takes a single - * int, denoting the - * dimension. - */ + /** + * This exception is raised if a + * functionality is not possible in + * the given dimension. Mostly used + * to throw function calls in 1d. + * + * The constructor takes a single + * int, denoting the + * dimension. + */ DeclException1 (ExcImpossibleInDim, int, << "Impossible in " << arg1 << "d."); - /** - * A number is zero, but it should - * not be here. - */ + /** + * A number is zero, but it should + * not be here. + */ DeclException0(ExcZero); - /** - * The object should have been - * filled with something before - * this member function is called. - */ + /** + * The object should have been + * filled with something before + * this member function is called. + */ DeclException0(ExcEmptyObject); - /** - * This exception is raised - * whenever the sizes of two - * objects were assumed to be - * equal, but were not. - * - * Parameters to the constructor - * are the first and second size, - * both of type int. - */ + /** + * This exception is raised + * whenever the sizes of two + * objects were assumed to be + * equal, but were not. + * + * Parameters to the constructor + * are the first and second size, + * both of type int. + */ DeclException2 (ExcDimensionMismatch, std::size_t, std::size_t, << "Dimension " << arg1 << " not equal to " << arg2); - /** - * The first dimension should be - * either equal to the second or - * the third, but it is neither. - */ + /** + * The first dimension should be + * either equal to the second or + * the third, but it is neither. + */ DeclException3 (ExcDimensionMismatch2, int, int, int, << "Dimension " << arg1 << " neither equal to " << arg2 << " nor to " << arg3); - /** - * This exception is one of the - * most often used ones, and - * indicates that an index is not - * within the expected range. For - * example, you might try to access - * an element of a vector which - * does not exist. - * - * The constructor takes three - * int, namely - *
    - *
  1. the violating index - *
  2. the lower bound - *
  3. the upper bound plus one - *
- */ + /** + * This exception is one of the + * most often used ones, and + * indicates that an index is not + * within the expected range. For + * example, you might try to access + * an element of a vector which + * does not exist. + * + * The constructor takes three + * int, namely + *
    + *
  1. the violating index + *
  2. the lower bound + *
  3. the upper bound plus one + *
+ */ DeclException3 (ExcIndexRange, int, int, int, << "Index " << arg1 << " is not in [" << arg2 << "," << arg3 << "["); - /** - * A number is too small. - */ + /** + * A number is too small. + */ DeclException2 (ExcLowerRange, int, int, << "Number " << arg1 << " must be larger or equal " << arg2); - /** - * This exception indicates that - * the first argument should be an - * integer multiple of the second, - * but is not. - */ + /** + * This exception indicates that + * the first argument should be an + * integer multiple of the second, + * but is not. + */ DeclException2 (ExcNotMultiple, int, int, << "Division " << arg1 << " by " << arg2 << " has remainder different from zero"); - /** - * This exception is thrown if the - * iterator you access has - * corrupted data. It might for - * instance be, that the container - * it refers does not have an entry - * at the point the iterator - * refers. - * - * Typically, this will be an - * internal error of deal.II, - * because the increment and - * decrement operators should never - * yield an invalid iterator. - */ + /** + * This exception is thrown if the + * iterator you access has + * corrupted data. It might for + * instance be, that the container + * it refers does not have an entry + * at the point the iterator + * refers. + * + * Typically, this will be an + * internal error of deal.II, + * because the increment and + * decrement operators should never + * yield an invalid iterator. + */ DeclException0 (ExcInvalidIterator); - /** - * This exception is thrown if the - * iterator you incremented or - * decremented was already at its - * final state. - */ + /** + * This exception is thrown if the + * iterator you incremented or + * decremented was already at its + * final state. + */ DeclException0 (ExcIteratorPastEnd); - /** - * This exception works around a - * design flaw in the - * DeclException0 macro: - * exceptions desclared through - * DeclException0 - * do not allow one to specify a - * message that is displayed when - * the exception is raised, as - * opposed to the other exceptions - * which allow to show a text along - * with the given parameters. - * - * When throwing this exception, - * you can give a message as a - * std::string as argument to the - * exception that is then - * displayed. The argument can, of - * course, be constructed at run-time, - * for example including the name of a - * file that can't be opened, or any - * other text you may want to assemble - * from different pieces. - */ + /** + * This exception works around a + * design flaw in the + * DeclException0 macro: + * exceptions desclared through + * DeclException0 + * do not allow one to specify a + * message that is displayed when + * the exception is raised, as + * opposed to the other exceptions + * which allow to show a text along + * with the given parameters. + * + * When throwing this exception, + * you can give a message as a + * std::string as argument to the + * exception that is then + * displayed. The argument can, of + * course, be constructed at run-time, + * for example including the name of a + * file that can't be opened, or any + * other text you may want to assemble + * from different pieces. + */ DeclException1 (ExcMessage, std::string, << arg1); - /** - * Exception used when running into - * functions that are only supported - * in a backward compatibility mode. - */ + /** + * Exception used when running into + * functions that are only supported + * in a backward compatibility mode. + */ DeclException1 (ExcCompatibility, - char*, + char *, << "You are using a backward compatibility feature\n" << "that you have disabled during configuration of\n" << "the library by the --disable-compat=" @@ -942,78 +942,78 @@ namespace StandardExceptions << "alternative function, or configure again without\n" << "this switch and recompile the library."); - /** - * Some of our numerical classes - * allow for setting alll entries - * to zero using the assignment - * operator =. - * - * In many cases, this assignment - * operator makes sense only - * for the argument zero. In other - * cases, this exception is thrown. - */ + /** + * Some of our numerical classes + * allow for setting alll entries + * to zero using the assignment + * operator =. + * + * In many cases, this assignment + * operator makes sense only + * for the argument zero. In other + * cases, this exception is thrown. + */ DeclException0 (ExcScalarAssignmentOnlyForZeroValue); - /** - * This function requires the BLAS - * library. Please reconfigure - * using the option - * --with-blas and check - * if it is actually included. - */ + /** + * This function requires the BLAS + * library. Please reconfigure + * using the option + * --with-blas and check + * if it is actually included. + */ DeclException0 (ExcNeedsBLAS); - /** - * This function requires the LAPACK - * library. Please reconfigure - * using the option - * --with-lapack and check - * if it is actually included. - */ + /** + * This function requires the LAPACK + * library. Please reconfigure + * using the option + * --with-lapack and check + * if it is actually included. + */ DeclException0 (ExcNeedsLAPACK); - /** - * This function requires the UMFPack - * library. Please reconfigure - * using the option - * --with-umfpack and check - * if it is actually included. - */ + /** + * This function requires the UMFPack + * library. Please reconfigure + * using the option + * --with-umfpack and check + * if it is actually included. + */ DeclException0 (ExcNeedsUMFPACK); - /** - * This function requires the METIS - * library. Please reconfigure - * using the option - * --with-metis and check - * if it is actually included. - */ + /** + * This function requires the METIS + * library. Please reconfigure + * using the option + * --with-metis and check + * if it is actually included. + */ DeclException0 (ExcNeedsMETIS); - /** - * This function requires the Petsc - * library. Please reconfigure - * using the option - * --with-petsc and check - * if it is actually included. - */ + /** + * This function requires the Petsc + * library. Please reconfigure + * using the option + * --with-petsc and check + * if it is actually included. + */ DeclException0 (ExcNeedsPETSC); - /** - * This function requires the - * NetCDF library, which is neither - * in your standard path nor - * configured with --with-netcdf. - */ + /** + * This function requires the + * NetCDF library, which is neither + * in your standard path nor + * configured with --with-netcdf. + */ DeclException0 (ExcNeedsNetCDF); - /** - * A configuration option disabled - * this feature. In order to use - * it, you must reconfigure and - * recompile the libraries. - */ - DeclException1 (ExcDisabled, char*, + /** + * A configuration option disabled + * this feature. In order to use + * it, you must reconfigure and + * recompile the libraries. + */ + DeclException1 (ExcDisabled, char *, << "This feature was disabled by the " "configuration option --disable-" << arg1 << ". Reconfigure to use it!"); @@ -1032,7 +1032,7 @@ namespace StandardExceptions * @author Guido Kanschat 2007 */ #define AssertDimension(dim1,dim2) Assert((dim1) == (dim2), \ - ExcDimensionMismatch((dim1),(dim2))) + ExcDimensionMismatch((dim1),(dim2))) /** * Special assertion, testing whether vec has size @@ -1060,7 +1060,7 @@ namespace StandardExceptions * @author Guido Kanschat 2007 */ #define AssertIndexRange(index,range) Assert((index) < (range), \ - ExcIndexRange((index),0,(range))) + ExcIndexRange((index),0,(range))) /* diff --git a/deal.II/include/deal.II/base/flow_function.h b/deal.II/include/deal.II/base/flow_function.h index cbbb544ad1..7c5671e79a 100644 --- a/deal.II/include/deal.II/base/flow_function.h +++ b/deal.II/include/deal.II/base/flow_function.h @@ -22,298 +22,298 @@ DEAL_II_NAMESPACE_OPEN namespace Functions { -/** - * Base class for analytic solutions to incompressible flow problems. - * - * Additional to the Function interface, this function provides for an - * offset of the pressure: if the pressure of the computed solution - * has an integral mean value different from zero, this value can be - * given to pressure_adjustment() in order to compute correct pressure - * errors. - * - * @note Derived classes should implement pressures with integral mean - * value zero always. - * - * @note Thread safety: Some of the functions make use of internal data to compute - * values. Therefore, every thread should obtain its own object of - * derived classes. - * - * @ingroup functions - * @author Guido Kanschat, 2007 - */ + /** + * Base class for analytic solutions to incompressible flow problems. + * + * Additional to the Function interface, this function provides for an + * offset of the pressure: if the pressure of the computed solution + * has an integral mean value different from zero, this value can be + * given to pressure_adjustment() in order to compute correct pressure + * errors. + * + * @note Derived classes should implement pressures with integral mean + * value zero always. + * + * @note Thread safety: Some of the functions make use of internal data to compute + * values. Therefore, every thread should obtain its own object of + * derived classes. + * + * @ingroup functions + * @author Guido Kanschat, 2007 + */ template class FlowFunction : public Function { - public: - /** - * Constructor, setting up some - * internal data structures. - */ - FlowFunction(); + public: + /** + * Constructor, setting up some + * internal data structures. + */ + FlowFunction(); - /** - * Virtual destructor. - */ - virtual ~FlowFunction(); + /** + * Virtual destructor. + */ + virtual ~FlowFunction(); - /** - * Store an adjustment for the - * pressure function, such that - * its mean value is - * p. - */ - void pressure_adjustment(double p); + /** + * Store an adjustment for the + * pressure function, such that + * its mean value is + * p. + */ + void pressure_adjustment(double p); - /** - * Values in a structure more - * suitable for vector valued - * functions. The outer vector - * is indexed by solution - * component, the inner by - * quadrature point. - */ - virtual void vector_values (const std::vector >& points, - std::vector >& values) const = 0; - /** - * Gradients in a structure more - * suitable for vector valued - * functions. The outer vector - * is indexed by solution - * component, the inner by - * quadrature point. - */ - virtual void vector_gradients (const std::vector > &points, - std::vector > > &gradients) const = 0; - /** - * Force terms in a structure more - * suitable for vector valued - * functions. The outer vector - * is indexed by solution - * component, the inner by - * quadrature point. - * - * @warning This is not the - * true Laplacian, but the - * force term to be used as - * right hand side in Stokes' - * equations - */ - virtual void vector_laplacians (const std::vector > &points, - std::vector > &values) const = 0; + /** + * Values in a structure more + * suitable for vector valued + * functions. The outer vector + * is indexed by solution + * component, the inner by + * quadrature point. + */ + virtual void vector_values (const std::vector > &points, + std::vector > &values) const = 0; + /** + * Gradients in a structure more + * suitable for vector valued + * functions. The outer vector + * is indexed by solution + * component, the inner by + * quadrature point. + */ + virtual void vector_gradients (const std::vector > &points, + std::vector > > &gradients) const = 0; + /** + * Force terms in a structure more + * suitable for vector valued + * functions. The outer vector + * is indexed by solution + * component, the inner by + * quadrature point. + * + * @warning This is not the + * true Laplacian, but the + * force term to be used as + * right hand side in Stokes' + * equations + */ + virtual void vector_laplacians (const std::vector > &points, + std::vector > &values) const = 0; - virtual void vector_value (const Point& points, Vector& value) const; - virtual double value (const Point& points, const unsigned int component) const; - virtual void vector_value_list (const std::vector > &points, - std::vector > &values) const; - virtual void vector_gradient_list (const std::vector > &points, - std::vector > > &gradients) const; - /** - * The force term in the - * momentum equation. - */ - virtual void vector_laplacian_list (const std::vector > &points, - std::vector > &values) const; + virtual void vector_value (const Point &points, Vector &value) const; + virtual double value (const Point &points, const unsigned int component) const; + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; + virtual void vector_gradient_list (const std::vector > &points, + std::vector > > &gradients) const; + /** + * The force term in the + * momentum equation. + */ + virtual void vector_laplacian_list (const std::vector > &points, + std::vector > &values) const; - std::size_t memory_consumption () const; + std::size_t memory_consumption () const; - protected: - /** - * Mean value of the pressure - * to be added by derived - * classes. - */ - double mean_pressure; + protected: + /** + * Mean value of the pressure + * to be added by derived + * classes. + */ + double mean_pressure; - private: + private: - /** - * A mutex that guards the - * following scratch arrays. - */ - mutable Threads::Mutex mutex; + /** + * A mutex that guards the + * following scratch arrays. + */ + mutable Threads::Mutex mutex; - /** - * Auxiliary values for the usual - * Function interface. - */ - mutable std::vector > aux_values; + /** + * Auxiliary values for the usual + * Function interface. + */ + mutable std::vector > aux_values; - /** - * Auxiliary values for the usual - * Function interface. - */ - mutable std::vector > > aux_gradients; + /** + * Auxiliary values for the usual + * Function interface. + */ + mutable std::vector > > aux_gradients; }; -/** - * Laminar pipe flow in two and three dimensions. The channel - * stretches along the x-axis and has radius @p radius. The - * @p Reynolds number is used to scale the pressure properly for a - * Navier-Stokes problem. - * - * @ingroup functions - * @author Guido Kanschat, 2007 - */ + /** + * Laminar pipe flow in two and three dimensions. The channel + * stretches along the x-axis and has radius @p radius. The + * @p Reynolds number is used to scale the pressure properly for a + * Navier-Stokes problem. + * + * @ingroup functions + * @author Guido Kanschat, 2007 + */ template class PoisseuilleFlow : public FlowFunction { - public: - /** - * Construct an object for the - * given channel radius - * r and the Reynolds - * number Re. - */ - PoisseuilleFlow (const double r, - const double Re); - virtual ~PoisseuilleFlow(); + public: + /** + * Construct an object for the + * given channel radius + * r and the Reynolds + * number Re. + */ + PoisseuilleFlow (const double r, + const double Re); + virtual ~PoisseuilleFlow(); - virtual void vector_values (const std::vector >& points, - std::vector >& values) const; - virtual void vector_gradients (const std::vector >& points, - std::vector > >& gradients) const; - virtual void vector_laplacians (const std::vector > &points, - std::vector > &values) const; + virtual void vector_values (const std::vector > &points, + std::vector > &values) const; + virtual void vector_gradients (const std::vector > &points, + std::vector > > &gradients) const; + virtual void vector_laplacians (const std::vector > &points, + std::vector > &values) const; - private: - const double radius; - const double Reynolds; + private: + const double radius; + const double Reynolds; }; -/** - * Artificial divergence free function with homogeneous boundary - * conditions on the cube [-1,1]dim. - * - * The function in 2D is - * @f[ - * \left(\begin{array}{c}u\\v\\p\end{array}\right) - * \left(\begin{array}{c}\cos^2x \sin y\cos y\\-\sin x\cos x\cos^2y\\ - * \sin x\cos x\sin y\cos y\end{array}\right) - * @f] - * @ingroup functions - * @author Guido Kanschat, 2007 - */ + /** + * Artificial divergence free function with homogeneous boundary + * conditions on the cube [-1,1]dim. + * + * The function in 2D is + * @f[ + * \left(\begin{array}{c}u\\v\\p\end{array}\right) + * \left(\begin{array}{c}\cos^2x \sin y\cos y\\-\sin x\cos x\cos^2y\\ + * \sin x\cos x\sin y\cos y\end{array}\right) + * @f] + * @ingroup functions + * @author Guido Kanschat, 2007 + */ template class StokesCosine : - public FlowFunction + public FlowFunction { - public: - /** - * Constructor setting the - * Reynolds number required for - * pressure computation and - * scaling of the right hand side. - */ - StokesCosine (const double viscosity = 1., const double reaction = 0.); - /** - * Change the viscosity and the - * reaction parameter. - */ - void set_parameters (const double viscosity, const double reaction); - virtual ~StokesCosine(); + public: + /** + * Constructor setting the + * Reynolds number required for + * pressure computation and + * scaling of the right hand side. + */ + StokesCosine (const double viscosity = 1., const double reaction = 0.); + /** + * Change the viscosity and the + * reaction parameter. + */ + void set_parameters (const double viscosity, const double reaction); + virtual ~StokesCosine(); - virtual void vector_values (const std::vector >& points, - std::vector >& values) const; - virtual void vector_gradients (const std::vector >& points, - std::vector > >& gradients) const; - virtual void vector_laplacians (const std::vector > &points, - std::vector > &values) const; + virtual void vector_values (const std::vector > &points, + std::vector > &values) const; + virtual void vector_gradients (const std::vector > &points, + std::vector > > &gradients) const; + virtual void vector_laplacians (const std::vector > &points, + std::vector > &values) const; - private: - /// The viscosity - double viscosity; - /// The reaction parameter - double reaction; + private: + /// The viscosity + double viscosity; + /// The reaction parameter + double reaction; }; -/** - * The solution to Stokes' equations on an L-shaped domain. - * - * Taken from Houston, Schötzau, Wihler, proceeding ENUMATH 2003. - * - * @ingroup functions - * @author Guido Kanschat, 2007 - */ + /** + * The solution to Stokes' equations on an L-shaped domain. + * + * Taken from Houston, Schötzau, Wihler, proceeding ENUMATH 2003. + * + * @ingroup functions + * @author Guido Kanschat, 2007 + */ class StokesLSingularity : public FlowFunction<2> { - public: - /// Constructor setting upsome data. - StokesLSingularity(); + public: + /// Constructor setting upsome data. + StokesLSingularity(); - virtual void vector_values (const std::vector >& points, - std::vector >& values) const; - virtual void vector_gradients (const std::vector >& points, - std::vector > >& gradients) const; - virtual void vector_laplacians (const std::vector > &points, - std::vector > &values) const; - private: - /// The auxiliary function Psi. - double Psi(double phi) const; - /// The derivative of Psi() - double Psi_1(double phi) const; - /// The 2nd derivative of Psi() - double Psi_2(double phi) const; - /// The 3rd derivative of Psi() - double Psi_3(double phi) const; - /// The 4th derivative of Psi() - double Psi_4(double phi) const; - /// The angle of the reentrant corner - const double omega; - /// The exponent of the radius - static const double lambda; - /// Cosine of lambda times omega - const double coslo; - /// Auxiliary variable 1+lambda - const double lp; - /// Auxiliary variable 1-lambda - const double lm; + virtual void vector_values (const std::vector > &points, + std::vector > &values) const; + virtual void vector_gradients (const std::vector > &points, + std::vector > > &gradients) const; + virtual void vector_laplacians (const std::vector > &points, + std::vector > &values) const; + private: + /// The auxiliary function Psi. + double Psi(double phi) const; + /// The derivative of Psi() + double Psi_1(double phi) const; + /// The 2nd derivative of Psi() + double Psi_2(double phi) const; + /// The 3rd derivative of Psi() + double Psi_3(double phi) const; + /// The 4th derivative of Psi() + double Psi_4(double phi) const; + /// The angle of the reentrant corner + const double omega; + /// The exponent of the radius + static const double lambda; + /// Cosine of lambda times omega + const double coslo; + /// Auxiliary variable 1+lambda + const double lp; + /// Auxiliary variable 1-lambda + const double lm; }; -/** - * Flow solution in 2D by Kovasznay (1947). - * - * This function is valid on the half plane right of the line - * x=1/2. - * - * @ingroup functions - * @author Guido Kanschat, 2007 - */ + /** + * Flow solution in 2D by Kovasznay (1947). + * + * This function is valid on the half plane right of the line + * x=1/2. + * + * @ingroup functions + * @author Guido Kanschat, 2007 + */ class Kovasznay : public FlowFunction<2> { - public: - /** - * Construct an object for the - * give Reynolds number - * Re. If the - * parameter Stokes is - * true, the right hand side of - * the momentum equation - * returned by - * vector_laplacians() contains - * the nonlinearity, such that - * the Kovasznay solution can - * be obtained as the solution - * to a Stokes problem. - */ - Kovasznay (const double Re, bool Stokes = false); - virtual ~Kovasznay(); + public: + /** + * Construct an object for the + * give Reynolds number + * Re. If the + * parameter Stokes is + * true, the right hand side of + * the momentum equation + * returned by + * vector_laplacians() contains + * the nonlinearity, such that + * the Kovasznay solution can + * be obtained as the solution + * to a Stokes problem. + */ + Kovasznay (const double Re, bool Stokes = false); + virtual ~Kovasznay(); - virtual void vector_values (const std::vector >& points, - std::vector >& values) const; - virtual void vector_gradients (const std::vector >& points, - std::vector > >& gradients) const; - virtual void vector_laplacians (const std::vector > &points, - std::vector > &values) const; + virtual void vector_values (const std::vector > &points, + std::vector > &values) const; + virtual void vector_gradients (const std::vector > &points, + std::vector > > &gradients) const; + virtual void vector_laplacians (const std::vector > &points, + std::vector > &values) const; - /// The value of lambda. - double lambda () const; - private: - const double Reynolds; - double lbda; - double p_average; - const bool stokes; + /// The value of lambda. + double lambda () const; + private: + const double Reynolds; + double lbda; + double p_average; + const bool stokes; }; } diff --git a/deal.II/include/deal.II/base/function.h b/deal.II/include/deal.II/base/function.h index 352fdd1cac..fc79c7f79a 100644 --- a/deal.II/include/deal.II/base/function.h +++ b/deal.II/include/deal.II/base/function.h @@ -99,260 +99,260 @@ template class Vector; */ template class Function : public FunctionTime, - public Subscriptor + public Subscriptor { - public: - /** - * Export the value of the - * template parameter as a static - * member constant. Sometimes - * useful for some expression - * template programming. - */ - static const unsigned int dimension = dim; - - /** - * Number of vector components. - */ - const unsigned int n_components; - - /** - * Constructor. May take an - * initial value for the number - * of components (which defaults - * to one, i.e. a scalar - * function), and the time - * variable, which defaults to - * zero. - */ - Function (const unsigned int n_components = 1, - const double initial_time = 0.0); - - /** - * Virtual destructor; absolutely - * necessary in this case. - * - * This destructor is declared - * pure virtual, such that - * objects of this class cannot - * be created. Since all the - * other virtual functions have a - * pseudo-implementation to avoid - * overhead in derived classes, - * this is the best place to do - * this. - * - * Nevertheless, since derived - * classes want to call the - * destructor of a base class, - * the destructor is implemented - * (despite it being pure - * virtual). - */ - virtual ~Function () = 0; - - /** - * Assignment operator. This is - * here only so that you can have - * objects of derived classes in - * containers, or assign them - * otherwise. It will raise an - * exception if the object from - * which you assign has a - * different number of components - * than the one being assigned - * to. - */ - Function & operator= (const Function &f); - - /** - * Return the value of the - * function at the given - * point. Unless there is only - * one component (i.e. the - * function is scalar), you - * should state the component you - * want to have evaluated; it - * defaults to zero, i.e. the - * first component. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Return all components of a - * vector-valued function at a - * given point. - * - * values shall have the right - * size beforehand, - * i.e. #n_components. - */ - virtual void vector_value (const Point &p, - Vector &values) const; - - /** - * Set values to the point - * values of the specified - * component of the function at - * the points. It is assumed - * that values already has the - * right size, i.e. the same - * size as the points array. - * - * Be default, this function - * repeatedly calls value() for - * each point separately, to - * fill the output array. - */ - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; - - /** - * Set values to the point - * values of the function at the - * points. It is assumed that - * values already has the right - * size, i.e. the same size as - * the points array, and that - * all elements be vectors with - * the same number of components - * as this function has. - * - * Be default, this function - * repeatedly calls vector_value() for - * each point separately, to - * fill the output array. - */ - virtual void vector_value_list (const std::vector > &points, - std::vector > &values) const; - - /** - * For each component of the - * function, fill a vector of - * values, one for each point. - * - * The default implementation of - * this function in Function calls - * value_list() for each - * component. In order to improve - * performance, this can be - * reimplemented in derived - * classes to speed up performance. - */ - virtual void vector_values (const std::vector >& points, - std::vector >& values) const; - - /** - * Return the gradient of the - * specified component of the - * function at the given point. - */ - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - - /** - * Return the gradient of all - * components of the - * function at the given point. - */ - virtual void vector_gradient (const Point &p, - std::vector > &gradients) const; - - /** - * Set gradients to the - * gradients of the specified - * component of the function at - * the points. It is assumed - * that gradients already has the - * right size, i.e. the same - * size as the points array. - */ - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; - - /** - * For each component of the - * function, fill a vector of - * gradient values, one for each point. - * - * The default implementation of - * this function in Function calls - * value_list() for each - * component. In order to improve - * performance, this can be - * reimplemented in derived - * classes to speed up performance. - */ - virtual void vector_gradients (const std::vector > &points, - std::vector > > &gradients) const; - - /** - * Set gradients to the gradients of - * the function at the points, - * for all components. - * It is assumed that gradients - * already has the right size, i.e. - * the same size as the points array. - * - * The outer loop over - * gradients is over the points - * in the list, the inner loop - * over the different components - * of the function. - */ - virtual void vector_gradient_list (const std::vector > &points, - std::vector > > &gradients) const; - - /** - * Compute the Laplacian of a - * given component at point p. - */ - virtual double laplacian (const Point &p, - const unsigned int component = 0) const; - - /** - * Compute the Laplacian of all - * components at point p and - * store them in values. - */ - virtual void vector_laplacian (const Point &p, - Vector &values) const; - - /** - * Compute the Laplacian of one - * component at a set of points. - */ - virtual void laplacian_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; - - /** - * Compute the Laplacians of all - * components at a set of points. - */ - virtual void vector_laplacian_list (const std::vector > &points, - std::vector > &values) const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; +public: + /** + * Export the value of the + * template parameter as a static + * member constant. Sometimes + * useful for some expression + * template programming. + */ + static const unsigned int dimension = dim; + + /** + * Number of vector components. + */ + const unsigned int n_components; + + /** + * Constructor. May take an + * initial value for the number + * of components (which defaults + * to one, i.e. a scalar + * function), and the time + * variable, which defaults to + * zero. + */ + Function (const unsigned int n_components = 1, + const double initial_time = 0.0); + + /** + * Virtual destructor; absolutely + * necessary in this case. + * + * This destructor is declared + * pure virtual, such that + * objects of this class cannot + * be created. Since all the + * other virtual functions have a + * pseudo-implementation to avoid + * overhead in derived classes, + * this is the best place to do + * this. + * + * Nevertheless, since derived + * classes want to call the + * destructor of a base class, + * the destructor is implemented + * (despite it being pure + * virtual). + */ + virtual ~Function () = 0; + + /** + * Assignment operator. This is + * here only so that you can have + * objects of derived classes in + * containers, or assign them + * otherwise. It will raise an + * exception if the object from + * which you assign has a + * different number of components + * than the one being assigned + * to. + */ + Function &operator= (const Function &f); + + /** + * Return the value of the + * function at the given + * point. Unless there is only + * one component (i.e. the + * function is scalar), you + * should state the component you + * want to have evaluated; it + * defaults to zero, i.e. the + * first component. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Return all components of a + * vector-valued function at a + * given point. + * + * values shall have the right + * size beforehand, + * i.e. #n_components. + */ + virtual void vector_value (const Point &p, + Vector &values) const; + + /** + * Set values to the point + * values of the specified + * component of the function at + * the points. It is assumed + * that values already has the + * right size, i.e. the same + * size as the points array. + * + * Be default, this function + * repeatedly calls value() for + * each point separately, to + * fill the output array. + */ + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + /** + * Set values to the point + * values of the function at the + * points. It is assumed that + * values already has the right + * size, i.e. the same size as + * the points array, and that + * all elements be vectors with + * the same number of components + * as this function has. + * + * Be default, this function + * repeatedly calls vector_value() for + * each point separately, to + * fill the output array. + */ + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; + + /** + * For each component of the + * function, fill a vector of + * values, one for each point. + * + * The default implementation of + * this function in Function calls + * value_list() for each + * component. In order to improve + * performance, this can be + * reimplemented in derived + * classes to speed up performance. + */ + virtual void vector_values (const std::vector > &points, + std::vector > &values) const; + + /** + * Return the gradient of the + * specified component of the + * function at the given point. + */ + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; + + /** + * Return the gradient of all + * components of the + * function at the given point. + */ + virtual void vector_gradient (const Point &p, + std::vector > &gradients) const; + + /** + * Set gradients to the + * gradients of the specified + * component of the function at + * the points. It is assumed + * that gradients already has the + * right size, i.e. the same + * size as the points array. + */ + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; + + /** + * For each component of the + * function, fill a vector of + * gradient values, one for each point. + * + * The default implementation of + * this function in Function calls + * value_list() for each + * component. In order to improve + * performance, this can be + * reimplemented in derived + * classes to speed up performance. + */ + virtual void vector_gradients (const std::vector > &points, + std::vector > > &gradients) const; + + /** + * Set gradients to the gradients of + * the function at the points, + * for all components. + * It is assumed that gradients + * already has the right size, i.e. + * the same size as the points array. + * + * The outer loop over + * gradients is over the points + * in the list, the inner loop + * over the different components + * of the function. + */ + virtual void vector_gradient_list (const std::vector > &points, + std::vector > > &gradients) const; + + /** + * Compute the Laplacian of a + * given component at point p. + */ + virtual double laplacian (const Point &p, + const unsigned int component = 0) const; + + /** + * Compute the Laplacian of all + * components at point p and + * store them in values. + */ + virtual void vector_laplacian (const Point &p, + Vector &values) const; + + /** + * Compute the Laplacian of one + * component at a set of points. + */ + virtual void laplacian_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + /** + * Compute the Laplacians of all + * components at a set of points. + */ + virtual void vector_laplacian_list (const std::vector > &points, + std::vector > &values) const; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; }; @@ -372,105 +372,105 @@ class Function : public FunctionTime, template class ZeroFunction : public Function { - public: - /** - * Constructor. The number of - * components is preset to one. - */ - ZeroFunction (const unsigned int n_components = 1); - - /** - * Virtual destructor; absolutely - * necessary in this case. - */ - virtual ~ZeroFunction (); - - /** - * Return the value of the function - * at the given point for one - * component. - */ - virtual double value (const Point &p, - const unsigned int component) const; - - /** - * Return the value of the function - * at the given point for all - * components. - */ - virtual void vector_value (const Point &p, - Vector &return_value) const; +public: + /** + * Constructor. The number of + * components is preset to one. + */ + ZeroFunction (const unsigned int n_components = 1); + + /** + * Virtual destructor; absolutely + * necessary in this case. + */ + virtual ~ZeroFunction (); + + /** + * Return the value of the function + * at the given point for one + * component. + */ + virtual double value (const Point &p, + const unsigned int component) const; + + /** + * Return the value of the function + * at the given point for all + * components. + */ + virtual void vector_value (const Point &p, + Vector &return_value) const; + + /** + * Set values to the point values + * of the function at the points, + * for the given component. + * It is assumed that values + * already has the right size, i.e. + * the same size as the points + * array. + */ + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + /** + * Set values to the point values + * of the function at the points, + * for all components. + * It is assumed that values + * already has the right size, i.e. + * the same size as the points + * array. + */ + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; - /** - * Set values to the point values - * of the function at the points, - * for the given component. - * It is assumed that values - * already has the right size, i.e. - * the same size as the points - * array. - */ - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; - - /** - * Set values to the point values - * of the function at the points, - * for all components. - * It is assumed that values - * already has the right size, i.e. - * the same size as the points - * array. - */ - virtual void vector_value_list (const std::vector > &points, - std::vector > &values) const; - - /** - * Return the gradient of the function - * at the given point, for the - * given component. - */ - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - - /** - * Return the gradient of the - * specified component of the - * function at the given point, - * for all components. - */ - virtual void vector_gradient (const Point &p, - std::vector > &gradients) const; - - /** - * Set gradients to the gradients of - * the function at the points, - * for the given component. - * It is assumed that values - * already has the right size, i.e. - * the same size as the points array. - */ - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; - - /** - * Set gradients to the gradients of - * the function at the points, - * for all components. - * It is assumed that gradients - * already has the right size, i.e. - * the same size as the points array. - * - * The outer loop over - * gradients is over the points - * in the list, the inner loop - * over the different components - * of the function. - */ - virtual void vector_gradient_list (const std::vector > &points, - std::vector > > &gradients) const; + /** + * Return the gradient of the function + * at the given point, for the + * given component. + */ + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; + + /** + * Return the gradient of the + * specified component of the + * function at the given point, + * for all components. + */ + virtual void vector_gradient (const Point &p, + std::vector > &gradients) const; + + /** + * Set gradients to the gradients of + * the function at the points, + * for the given component. + * It is assumed that values + * already has the right size, i.e. + * the same size as the points array. + */ + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; + + /** + * Set gradients to the gradients of + * the function at the points, + * for all components. + * It is assumed that gradients + * already has the right size, i.e. + * the same size as the points array. + * + * The outer loop over + * gradients is over the points + * in the list, the inner loop + * over the different components + * of the function. + */ + virtual void vector_gradient_list (const std::vector > &points, + std::vector > > &gradients) const; }; @@ -501,85 +501,85 @@ class ZeroFunction : public Function template class ConstantFunction : public ZeroFunction { - public: - /** - * Constructor; takes the constant function - * value as an argument. The number of - * components is preset to one. - */ - ConstantFunction (const double value, - const unsigned int n_components = 1); - - /** - * Virtual destructor; absolutely - * necessary in this case. - */ - virtual ~ConstantFunction (); - - /** - * Return the value of the function - * at the given point for one - * component. - */ - virtual double value (const Point &p, - const unsigned int component) const; - - /** - * Return the value of the function - * at the given point for all - * components. - */ - virtual void vector_value (const Point &p, - Vector &return_value) const; - - /** - * Set values to the point values - * of the function at the points, - * for the given component. - * It is assumed that values - * already has the right size, i.e. - * the same size as the points - * array. - */ - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; - - /** - * Set values to the point values - * of the function at the points, - * for all components. - * It is assumed that values - * already has the right size, i.e. - * the same size as the points - * array. - */ - virtual void vector_value_list (const std::vector > &points, - std::vector > &values) const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - - protected: - /** - * Store the constant function value. - */ - const double function_value; +public: + /** + * Constructor; takes the constant function + * value as an argument. The number of + * components is preset to one. + */ + ConstantFunction (const double value, + const unsigned int n_components = 1); + + /** + * Virtual destructor; absolutely + * necessary in this case. + */ + virtual ~ConstantFunction (); + + /** + * Return the value of the function + * at the given point for one + * component. + */ + virtual double value (const Point &p, + const unsigned int component) const; + + /** + * Return the value of the function + * at the given point for all + * components. + */ + virtual void vector_value (const Point &p, + Vector &return_value) const; + + /** + * Set values to the point values + * of the function at the points, + * for the given component. + * It is assumed that values + * already has the right size, i.e. + * the same size as the points + * array. + */ + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + /** + * Set values to the point values + * of the function at the points, + * for all components. + * It is assumed that values + * already has the right size, i.e. + * the same size as the points + * array. + */ + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + +protected: + /** + * Store the constant function value. + */ + const double function_value; }; @@ -599,92 +599,92 @@ class ConstantFunction : public ZeroFunction template class ComponentSelectFunction : public ConstantFunction { - public: - /** - * Constructor if only a single - * component shall be - * non-zero. Arguments denote the - * component selected, the value - * for that component and the - * total number of vector - * components. - */ - ComponentSelectFunction (const unsigned int selected, - const double value, - const unsigned int n_components); - - /** - * Constructor. As before, but - * the value for the selected - * component is assumed to be - * one. In essence, this function - * then works as a mask. - */ - ComponentSelectFunction (const unsigned int selected, - const unsigned int n_components); - - /** - * Constructor if multiple - * components shall have - * non-zero, unit values - * (i.e. this should be a mask - * for multiple components). The - * first argument denotes a - * half-open interval of - * components (for example - * std::pair(0,dim) for the first - * dim components), and the - * second argument is the total - * number of vector components. - */ - ComponentSelectFunction (const std::pair &selected, - const unsigned int n_components); - - /** - * Return the value of the function - * at the given point for all - * components. - */ - virtual void vector_value (const Point &p, - Vector &return_value) const; - - /** - * Set values to the point values - * of the function at the points, - * for all components. - * It is assumed that values - * already has the right size, i.e. - * the same size as the points - * array. - */ - virtual void vector_value_list (const std::vector > &points, - std::vector > &values) const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - - protected: - /** - * Half-open interval of the - * indices of selected - * components. - */ - const std::pair selected_components; +public: + /** + * Constructor if only a single + * component shall be + * non-zero. Arguments denote the + * component selected, the value + * for that component and the + * total number of vector + * components. + */ + ComponentSelectFunction (const unsigned int selected, + const double value, + const unsigned int n_components); + + /** + * Constructor. As before, but + * the value for the selected + * component is assumed to be + * one. In essence, this function + * then works as a mask. + */ + ComponentSelectFunction (const unsigned int selected, + const unsigned int n_components); + + /** + * Constructor if multiple + * components shall have + * non-zero, unit values + * (i.e. this should be a mask + * for multiple components). The + * first argument denotes a + * half-open interval of + * components (for example + * std::pair(0,dim) for the first + * dim components), and the + * second argument is the total + * number of vector components. + */ + ComponentSelectFunction (const std::pair &selected, + const unsigned int n_components); + + /** + * Return the value of the function + * at the given point for all + * components. + */ + virtual void vector_value (const Point &p, + Vector &return_value) const; + + /** + * Set values to the point values + * of the function at the points, + * for all components. + * It is assumed that values + * already has the right size, i.e. + * the same size as the points + * array. + */ + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + +protected: + /** + * Half-open interval of the + * indices of selected + * components. + */ + const std::pair selected_components; }; diff --git a/deal.II/include/deal.II/base/function_bessel.h b/deal.II/include/deal.II/base/function_bessel.h index dd45b1a921..0b7f3c144a 100644 --- a/deal.II/include/deal.II/base/function_bessel.h +++ b/deal.II/include/deal.II/base/function_bessel.h @@ -21,32 +21,32 @@ DEAL_II_NAMESPACE_OPEN namespace Functions { -/** - * The Bessel functions of first kind or positive integer order. - * - * @author Guido Kanschat - * @date 2010 - */ + /** + * The Bessel functions of first kind or positive integer order. + * + * @author Guido Kanschat + * @date 2010 + */ template class Bessel1 : public Function { - public: - Bessel1(const unsigned int order, - const double wave_number, - const Point center = Point()); - virtual double value (const Point& points, const unsigned int component) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; - private: - unsigned int order; - double wave_number; - Point center; + public: + Bessel1(const unsigned int order, + const double wave_number, + const Point center = Point()); + virtual double value (const Point &points, const unsigned int component) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; + private: + unsigned int order; + double wave_number; + Point center; }; } diff --git a/deal.II/include/deal.II/base/function_derivative.h b/deal.II/include/deal.II/base/function_derivative.h index 98016982e9..03400629d8 100644 --- a/deal.II/include/deal.II/base/function_derivative.h +++ b/deal.II/include/deal.II/base/function_derivative.h @@ -39,123 +39,123 @@ DEAL_II_NAMESPACE_OPEN template class FunctionDerivative : public AutoDerivativeFunction { - public: - /** - * Constructor. Provided are the - * functions to compute - * derivatives of, the direction - * vector of the differentiation - * and the step size h of the - * difference formula. - */ - FunctionDerivative (const Function &f, - const Point &direction, - const double h = 1.e-6); - - /** - * Constructor. Provided are the - * functions to compute - * derivatives of and the - * direction vector of the - * differentiation in each - * quadrature point and the - * difference step size. - * - * This is the constructor for a - * variable velocity field. Most - * probably, a new object of - * FunctionDerivative has to - * be constructed for each set of - * quadrature points. - * - * The number of quadrature point - * must still be the same, when - * values are accessed. - */ - FunctionDerivative (const Function &f, - const std::vector > &direction, - const double h = 1.e-6); - - /** - * Choose the difference formula. - * This is set to the default in - * the constructor. - * - * Formulas implemented right now - * are first order backward Euler - * (UpwindEuler), second - * order symmetric Euler - * (Euler) and a symmetric - * fourth order formula - * (FourthOrder). - */ - void set_formula (typename AutoDerivativeFunction::DifferenceFormula formula - = AutoDerivativeFunction::Euler); - /** - * Change the base step size of - * the difference formula - */ - void set_h (const double h); - - virtual double value (const Point& p, - const unsigned int component = 0) const; - - virtual void vector_value(const Point& p, - Vector& value) const; - - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - - /** @addtogroup Exceptions - * @{ */ - - /** - * Exception. - */ - DeclException0(ExcInvalidFormula); - //@} - private: - /** - * Function for differentiation. - */ - const Function& f; - - /** - * Step size of the difference - * formula. - */ - double h; - - /** - * Difference formula. - */ - typename AutoDerivativeFunction::DifferenceFormula formula; - - /** - * Helper object. Contains the - * increment vector for the - * formula. - */ - std::vector > incr; +public: + /** + * Constructor. Provided are the + * functions to compute + * derivatives of, the direction + * vector of the differentiation + * and the step size h of the + * difference formula. + */ + FunctionDerivative (const Function &f, + const Point &direction, + const double h = 1.e-6); + + /** + * Constructor. Provided are the + * functions to compute + * derivatives of and the + * direction vector of the + * differentiation in each + * quadrature point and the + * difference step size. + * + * This is the constructor for a + * variable velocity field. Most + * probably, a new object of + * FunctionDerivative has to + * be constructed for each set of + * quadrature points. + * + * The number of quadrature point + * must still be the same, when + * values are accessed. + */ + FunctionDerivative (const Function &f, + const std::vector > &direction, + const double h = 1.e-6); + + /** + * Choose the difference formula. + * This is set to the default in + * the constructor. + * + * Formulas implemented right now + * are first order backward Euler + * (UpwindEuler), second + * order symmetric Euler + * (Euler) and a symmetric + * fourth order formula + * (FourthOrder). + */ + void set_formula (typename AutoDerivativeFunction::DifferenceFormula formula + = AutoDerivativeFunction::Euler); + /** + * Change the base step size of + * the difference formula + */ + void set_h (const double h); + + virtual double value (const Point &p, + const unsigned int component = 0) const; + + virtual void vector_value(const Point &p, + Vector &value) const; + + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + + /** @addtogroup Exceptions + * @{ */ + + /** + * Exception. + */ + DeclException0(ExcInvalidFormula); + //@} +private: + /** + * Function for differentiation. + */ + const Function &f; + + /** + * Step size of the difference + * formula. + */ + double h; + + /** + * Difference formula. + */ + typename AutoDerivativeFunction::DifferenceFormula formula; + + /** + * Helper object. Contains the + * increment vector for the + * formula. + */ + std::vector > incr; }; DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/include/deal.II/base/function_lib.h b/deal.II/include/deal.II/base/function_lib.h index 47a578a05e..26bd37c952 100644 --- a/deal.II/include/deal.II/base/function_lib.h +++ b/deal.II/include/deal.II/base/function_lib.h @@ -32,1142 +32,1142 @@ namespace Functions { -/** - * The distance to the origin squared. - * - * This function returns the square norm of the radius vector of a point. - * - * Together with the function, its derivatives and Laplacian are defined. - * - * @ingroup functions - * @author: Guido Kanschat, 1999 - */ + /** + * The distance to the origin squared. + * + * This function returns the square norm of the radius vector of a point. + * + * Together with the function, its derivatives and Laplacian are defined. + * + * @ingroup functions + * @author: Guido Kanschat, 1999 + */ template class SquareFunction : public Function { - public: - virtual double value (const Point &p, - const unsigned int component = 0) const; - virtual void vector_value (const Point &p, - Vector &values) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - virtual void vector_gradient (const Point &p, - std::vector > &gradient) const; - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; - virtual double laplacian (const Point &p, - const unsigned int component = 0) const; - virtual void laplacian_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + public: + virtual double value (const Point &p, + const unsigned int component = 0) const; + virtual void vector_value (const Point &p, + Vector &values) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; + virtual void vector_gradient (const Point &p, + std::vector > &gradient) const; + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; + virtual double laplacian (const Point &p, + const unsigned int component = 0) const; + virtual void laplacian_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; -/** - * The function xy in 2d and 3d, not implemented in 1d. - * This function serves as an example for - * a vanishing Laplacian. - * - * @ingroup functions - * @author: Guido Kanschat, 2000 - */ + /** + * The function xy in 2d and 3d, not implemented in 1d. + * This function serves as an example for + * a vanishing Laplacian. + * + * @ingroup functions + * @author: Guido Kanschat, 2000 + */ template class Q1WedgeFunction : public Function { - public: - virtual double value (const Point &p, - const unsigned int component = 0) const; - - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; - - virtual void vector_value_list (const std::vector >& points, - std::vector >& values) const; - - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; - - virtual void vector_gradient_list (const std::vector > &, - std::vector > >&) const; - - /** - * Laplacian of the function at one point. - */ - virtual double laplacian (const Point &p, - const unsigned int component = 0) const; - - /** - * Laplacian of the function at multiple points. - */ - virtual void laplacian_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + public: + virtual double value (const Point &p, + const unsigned int component = 0) const; + + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; + + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; + + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; + + virtual void vector_gradient_list (const std::vector > &, + std::vector > > &) const; + + /** + * Laplacian of the function at one point. + */ + virtual double laplacian (const Point &p, + const unsigned int component = 0) const; + + /** + * Laplacian of the function at multiple points. + */ + virtual void laplacian_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; -/** - * d-quadratic pillow on the unit hypercube. - * - * This is a function for testing the implementation. It has zero Dirichlet - * boundary values on the domain $(-1,1)^d$. In the inside, it is the - * product of $1-x_i^2$ over all space dimensions. - * - * Providing a non-zero argument to the constructor, the whole function - * can be offset by a constant. - * - * Together with the function, its derivatives and Laplacian are defined. - * - * @ingroup functions - * @author: Guido Kanschat, 1999 - */ + /** + * d-quadratic pillow on the unit hypercube. + * + * This is a function for testing the implementation. It has zero Dirichlet + * boundary values on the domain $(-1,1)^d$. In the inside, it is the + * product of $1-x_i^2$ over all space dimensions. + * + * Providing a non-zero argument to the constructor, the whole function + * can be offset by a constant. + * + * Together with the function, its derivatives and Laplacian are defined. + * + * @ingroup functions + * @author: Guido Kanschat, 1999 + */ template class PillowFunction : public Function { - public: - /** - * Constructor. Provide a - * constant that will be added to - * each function value. - */ - PillowFunction (const double offset=0.); - - /** - * The value at a single point. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Values at multiple points. - */ - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + public: + /** + * Constructor. Provide a + * constant that will be added to + * each function value. + */ + PillowFunction (const double offset=0.); + + /** + * The value at a single point. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Values at multiple points. + */ + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + /** + * Gradient at a single point. + */ + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; - /** - * Gradient at a single point. - */ - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - - /** - * Gradients at multiple points. - */ - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; - - /** - * Laplacian at a single point. - */ - virtual double laplacian (const Point &p, - const unsigned int component = 0) const; - - /** - * Laplacian at multiple points. - */ - virtual void laplacian_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; - private: - const double offset; + /** + * Gradients at multiple points. + */ + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; + + /** + * Laplacian at a single point. + */ + virtual double laplacian (const Point &p, + const unsigned int component = 0) const; + + /** + * Laplacian at multiple points. + */ + virtual void laplacian_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + private: + const double offset; }; -/** - * Cosine-shaped pillow function. - * This is another function with zero boundary values on $[-1,1]^d$. In the interior - * it is the product of $\cos(\pi/2 x_i)$. - * - * @ingroup functions - * @author Guido Kanschat, 1999 - */ + /** + * Cosine-shaped pillow function. + * This is another function with zero boundary values on $[-1,1]^d$. In the interior + * it is the product of $\cos(\pi/2 x_i)$. + * + * @ingroup functions + * @author Guido Kanschat, 1999 + */ template class CosineFunction : public Function { - public: - /** - * Constructor which allows to - * optionally generate a vector - * valued cosine function with - * the same value in each - * component. - */ - CosineFunction (const unsigned int n_components = 1); - - virtual double value (const Point &p, - const unsigned int component = 0) const; - - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; - - virtual void vector_value_list (const std::vector >& points, - std::vector >& values) const; - - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; - - virtual double laplacian (const Point &p, - const unsigned int component = 0) const; + public: + /** + * Constructor which allows to + * optionally generate a vector + * valued cosine function with + * the same value in each + * component. + */ + CosineFunction (const unsigned int n_components = 1); + + virtual double value (const Point &p, + const unsigned int component = 0) const; + + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; + + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; - virtual void laplacian_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; - /** - * Second derivatives at a - * single point. - */ - virtual Tensor<2,dim> hessian (const Point &p, - const unsigned int component = 0) const; + virtual double laplacian (const Point &p, + const unsigned int component = 0) const; - /** - * Second derivatives at - * multiple points. - */ - virtual void hessian_list (const std::vector > &points, - std::vector > &hessians, + virtual void laplacian_list (const std::vector > &points, + std::vector &values, const unsigned int component = 0) const; + + /** + * Second derivatives at a + * single point. + */ + virtual Tensor<2,dim> hessian (const Point &p, + const unsigned int component = 0) const; + + /** + * Second derivatives at + * multiple points. + */ + virtual void hessian_list (const std::vector > &points, + std::vector > &hessians, + const unsigned int component = 0) const; }; -/** - * Gradient of the cosine-shaped pillow function. - * - * This is a vector-valued function with @p dim components, the - * gradient of CosineFunction. On the square [-1,1], it has tangential - * boundary conditions zero. Thus, it can be used to test - * implementations of Maxwell operators without bothering about - * boundary terms. - * - * @ingroup functions - * @author Guido Kanschat, 2010 - */ + /** + * Gradient of the cosine-shaped pillow function. + * + * This is a vector-valued function with @p dim components, the + * gradient of CosineFunction. On the square [-1,1], it has tangential + * boundary conditions zero. Thus, it can be used to test + * implementations of Maxwell operators without bothering about + * boundary terms. + * + * @ingroup functions + * @author Guido Kanschat, 2010 + */ template class CosineGradFunction : public Function { - public: - /** - * Constructor, creating a - * function with @p dim components. - */ - CosineGradFunction (); - - virtual double value (const Point &p, - const unsigned int component) const; - virtual void vector_value (const Point &p, - Vector &values) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component) const; - - virtual void vector_value_list (const std::vector >& points, - std::vector >& values) const; - - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component) const; - - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component) const; - - virtual void vector_gradient_list (const std::vector > &points, - std::vector > > &gradients) const; - - virtual double laplacian (const Point &p, - const unsigned int component) const; + public: + /** + * Constructor, creating a + * function with @p dim components. + */ + CosineGradFunction (); + + virtual double value (const Point &p, + const unsigned int component) const; + virtual void vector_value (const Point &p, + Vector &values) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component) const; + + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; + + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component) const; + + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component) const; + + virtual void vector_gradient_list (const std::vector > &points, + std::vector > > &gradients) const; + + virtual double laplacian (const Point &p, + const unsigned int component) const; }; -/** - * Product of exponential functions in each coordinate direction. - * - * @ingroup functions - * @author Guido Kanschat, 1999 - */ + /** + * Product of exponential functions in each coordinate direction. + * + * @ingroup functions + * @author Guido Kanschat, 1999 + */ template class ExpFunction : public Function { - public: - /** - * The value at a single point. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Values at multiple points. - */ - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + public: + /** + * The value at a single point. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Values at multiple points. + */ + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + /** + * Gradient at a single point. + */ + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; - /** - * Gradient at a single point. - */ - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - - /** - * Gradients at multiple points. - */ - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; - - /** - * Laplacian at a single point. - */ - virtual double laplacian (const Point &p, - const unsigned int component = 0) const; - - /** - * Laplacian at multiple points. - */ - virtual void laplacian_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + /** + * Gradients at multiple points. + */ + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; + + /** + * Laplacian at a single point. + */ + virtual double laplacian (const Point &p, + const unsigned int component = 0) const; + + /** + * Laplacian at multiple points. + */ + virtual void laplacian_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; -/** - * Harmonic singularity on the L-shaped domain in 2D. - * - * @ingroup functions - * @author Guido Kanschat - * @date 1999 - */ + /** + * Harmonic singularity on the L-shaped domain in 2D. + * + * @ingroup functions + * @author Guido Kanschat + * @date 1999 + */ class LSingularityFunction : public Function<2> { - public: - virtual double value (const Point<2> &p, - const unsigned int component = 0) const; + public: + virtual double value (const Point<2> &p, + const unsigned int component = 0) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; - virtual void vector_value_list (const std::vector >& points, - std::vector >& values) const; + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; - virtual Tensor<1,2> gradient (const Point<2> &p, - const unsigned int component = 0) const; + virtual Tensor<1,2> gradient (const Point<2> &p, + const unsigned int component = 0) const; - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; - virtual void vector_gradient_list (const std::vector > &, - std::vector > >&) const; + virtual void vector_gradient_list (const std::vector > &, + std::vector > > &) const; - virtual double laplacian (const Point<2> &p, - const unsigned int component = 0) const; + virtual double laplacian (const Point<2> &p, + const unsigned int component = 0) const; - virtual void laplacian_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void laplacian_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; -/** - * Gradient of the harmonic singularity on the L-shaped domain in 2D. - * - * The gradient of LSingularityFunction, which is a vector valued - * function with vanishing curl and divergence. - * - * @ingroup functions - * @author Guido Kanschat, 2010 - */ + /** + * Gradient of the harmonic singularity on the L-shaped domain in 2D. + * + * The gradient of LSingularityFunction, which is a vector valued + * function with vanishing curl and divergence. + * + * @ingroup functions + * @author Guido Kanschat, 2010 + */ class LSingularityGradFunction : public Function<2> { - public: - /** - * Default constructor setting - * the dimension to 2. - */ - LSingularityGradFunction (); - virtual double value (const Point<2> &p, - const unsigned int component) const; - - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component) const; - - virtual void vector_value_list (const std::vector >& points, - std::vector >& values) const; - - virtual Tensor<1,2> gradient (const Point<2> &p, - const unsigned int component) const; - - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component) const; - - virtual void vector_gradient_list (const std::vector > &, - std::vector > >&) const; - - virtual double laplacian (const Point<2> &p, - const unsigned int component) const; - - virtual void laplacian_list (const std::vector > &points, - std::vector &values, - const unsigned int component) const; + public: + /** + * Default constructor setting + * the dimension to 2. + */ + LSingularityGradFunction (); + virtual double value (const Point<2> &p, + const unsigned int component) const; + + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component) const; + + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; + + virtual Tensor<1,2> gradient (const Point<2> &p, + const unsigned int component) const; + + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component) const; + + virtual void vector_gradient_list (const std::vector > &, + std::vector > > &) const; + + virtual double laplacian (const Point<2> &p, + const unsigned int component) const; + + virtual void laplacian_list (const std::vector > &points, + std::vector &values, + const unsigned int component) const; }; -/** - * Singularity on the slit domain in 2D and 3D. - * - * @ingroup functions - * @author Guido Kanschat, 1999, 2006 - */ + /** + * Singularity on the slit domain in 2D and 3D. + * + * @ingroup functions + * @author Guido Kanschat, 1999, 2006 + */ template class SlitSingularityFunction : public Function { - public: - virtual double value (const Point &p, - const unsigned int component = 0) const; + public: + virtual double value (const Point &p, + const unsigned int component = 0) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; - virtual void vector_value_list (const std::vector >& points, - std::vector >& values) const; + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; - virtual Tensor<1,dim> gradient (const Point &p, + virtual Tensor<1,dim> gradient (const Point &p, const unsigned int component = 0) const; - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; - virtual void vector_gradient_list (const std::vector > &, - std::vector > >&) const; + virtual void vector_gradient_list (const std::vector > &, + std::vector > > &) const; - virtual double laplacian (const Point &p, - const unsigned int component = 0) const; + virtual double laplacian (const Point &p, + const unsigned int component = 0) const; - virtual void laplacian_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void laplacian_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; -/** - * Singularity on the slit domain with one Neumann boundary in 2D. - * - * @ingroup functions - * @author Guido Kanschat, 2002 - */ + /** + * Singularity on the slit domain with one Neumann boundary in 2D. + * + * @ingroup functions + * @author Guido Kanschat, 2002 + */ class SlitHyperSingularityFunction : public Function<2> { - public: - virtual double value (const Point<2> &p, - const unsigned int component = 0) const; + public: + virtual double value (const Point<2> &p, + const unsigned int component = 0) const; - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; - virtual void vector_value_list (const std::vector >& points, - std::vector >& values) const; + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; - virtual Tensor<1,2> gradient (const Point<2> &p, - const unsigned int component = 0) const; + virtual Tensor<1,2> gradient (const Point<2> &p, + const unsigned int component = 0) const; - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; - virtual void vector_gradient_list (const std::vector > &, - std::vector > >&) const; + virtual void vector_gradient_list (const std::vector > &, + std::vector > > &) const; - virtual double laplacian (const Point<2> &p, - const unsigned int component = 0) const; + virtual double laplacian (const Point<2> &p, + const unsigned int component = 0) const; - virtual void laplacian_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + virtual void laplacian_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; -/** - * A jump in x-direction transported into some direction. - * - * If the advection is parallel to the y-axis, the function is - * -atan(sx), where s is the steepness parameter provided in - * the constructor. - * - * For different advection directions, this function will be turned in - * the parameter space. - * - * Together with the function, its derivatives and Laplacian are defined. - * - * @ingroup functions - * @author: Guido Kanschat, 2000 - */ + /** + * A jump in x-direction transported into some direction. + * + * If the advection is parallel to the y-axis, the function is + * -atan(sx), where s is the steepness parameter provided in + * the constructor. + * + * For different advection directions, this function will be turned in + * the parameter space. + * + * Together with the function, its derivatives and Laplacian are defined. + * + * @ingroup functions + * @author: Guido Kanschat, 2000 + */ template class JumpFunction : public Function { - public: - /** - * Constructor. Provide the - * advection direction here and - * the steepness of the slope. - */ - JumpFunction (const Point &direction, - const double steepness); - - /** - * Function value at one point. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Function values at multiple - * points. - */ - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + public: + /** + * Constructor. Provide the + * advection direction here and + * the steepness of the slope. + */ + JumpFunction (const Point &direction, + const double steepness); + + /** + * Function value at one point. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Function values at multiple + * points. + */ + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + /** + * Gradient at one point. + */ + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; + + /** + Gradients at multiple points. + */ + virtual void gradient_list (const std::vector > &points, + std::vector > &gradients, + const unsigned int component = 0) const; + + /** + * Laplacian of the function at one point. + */ + virtual double laplacian (const Point &p, + const unsigned int component = 0) const; + + /** + * Laplacian of the function at multiple points. + */ + virtual void laplacian_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; - /** - * Gradient at one point. - */ - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - - /** - Gradients at multiple points. - */ - virtual void gradient_list (const std::vector > &points, - std::vector > &gradients, - const unsigned int component = 0) const; - - /** - * Laplacian of the function at one point. - */ - virtual double laplacian (const Point &p, - const unsigned int component = 0) const; - - /** - * Laplacian of the function at multiple points. - */ - virtual void laplacian_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - - protected: - /** - * Advection vector. - */ - const Point direction; - - /** - * Steepness (maximal derivative) - * of the slope. - */ - const double steepness; - - /** - * Advection angle. - */ - double angle; - - /** - * Sine of angle. - */ - double sine; - - /** - * Cosine of angle. - */ - double cosine; + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + + protected: + /** + * Advection vector. + */ + const Point direction; + + /** + * Steepness (maximal derivative) + * of the slope. + */ + const double steepness; + + /** + * Advection angle. + */ + double angle; + + /** + * Sine of angle. + */ + double sine; + + /** + * Cosine of angle. + */ + double cosine; }; -/** - * Given a wavenumber vector generate a cosine function. The - * wavenumber coefficient is given as a $d$-dimensional point $k$ - * in Fourier space, and the function is then recovered as $f(x) = - * \cos(\sum_i k_i x_i) = Re(\exp(i k.x))$. - * - * The class has its name from the fact that it resembles one - * component of a Fourier cosine decomposition. - * - * @ingroup functions - * @author Wolfgang Bangerth, 2001 - */ + /** + * Given a wavenumber vector generate a cosine function. The + * wavenumber coefficient is given as a $d$-dimensional point $k$ + * in Fourier space, and the function is then recovered as $f(x) = + * \cos(\sum_i k_i x_i) = Re(\exp(i k.x))$. + * + * The class has its name from the fact that it resembles one + * component of a Fourier cosine decomposition. + * + * @ingroup functions + * @author Wolfgang Bangerth, 2001 + */ template class FourierCosineFunction : public Function { - public: - /** - * Constructor. Take the Fourier - * coefficients in each space - * direction as argument. - */ - FourierCosineFunction (const Point &fourier_coefficients); - - /** - * Return the value of the - * function at the given - * point. Unless there is only - * one component (i.e. the - * function is scalar), you - * should state the component you - * want to have evaluated; it - * defaults to zero, i.e. the - * first component. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Return the gradient of the - * specified component of the - * function at the given point. - */ - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - - /** - * Compute the Laplacian of a - * given component at point p. - */ - virtual double laplacian (const Point &p, - const unsigned int component = 0) const; - private: - /** - * Stored Fourier coefficients. - */ - const Point fourier_coefficients; + public: + /** + * Constructor. Take the Fourier + * coefficients in each space + * direction as argument. + */ + FourierCosineFunction (const Point &fourier_coefficients); + + /** + * Return the value of the + * function at the given + * point. Unless there is only + * one component (i.e. the + * function is scalar), you + * should state the component you + * want to have evaluated; it + * defaults to zero, i.e. the + * first component. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Return the gradient of the + * specified component of the + * function at the given point. + */ + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; + + /** + * Compute the Laplacian of a + * given component at point p. + */ + virtual double laplacian (const Point &p, + const unsigned int component = 0) const; + private: + /** + * Stored Fourier coefficients. + */ + const Point fourier_coefficients; }; -/** - * Given a wavenumber vector generate a sine function. The - * wavenumber coefficient is given as a $d$-dimensional point $k$ - * in Fourier space, and the function is then recovered as $f(x) = - * \sin(\sum_i k_i x_i) = Im(\exp(i k.x))$. - * - * The class has its name from the fact that it resembles one - * component of a Fourier sine decomposition. - * - * @ingroup functions - * @author Wolfgang Bangerth, 2001 - */ + /** + * Given a wavenumber vector generate a sine function. The + * wavenumber coefficient is given as a $d$-dimensional point $k$ + * in Fourier space, and the function is then recovered as $f(x) = + * \sin(\sum_i k_i x_i) = Im(\exp(i k.x))$. + * + * The class has its name from the fact that it resembles one + * component of a Fourier sine decomposition. + * + * @ingroup functions + * @author Wolfgang Bangerth, 2001 + */ template class FourierSineFunction : public Function { - public: - /** - * Constructor. Take the Fourier - * coefficients in each space - * direction as argument. - */ - FourierSineFunction (const Point &fourier_coefficients); - - /** - * Return the value of the - * function at the given - * point. Unless there is only - * one component (i.e. the - * function is scalar), you - * should state the component you - * want to have evaluated; it - * defaults to zero, i.e. the - * first component. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Return the gradient of the - * specified component of the - * function at the given point. - */ - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - - /** - * Compute the Laplacian of a - * given component at point p. - */ - virtual double laplacian (const Point &p, - const unsigned int component = 0) const; - private: - /** - * Stored Fourier coefficients. - */ - const Point fourier_coefficients; + public: + /** + * Constructor. Take the Fourier + * coefficients in each space + * direction as argument. + */ + FourierSineFunction (const Point &fourier_coefficients); + + /** + * Return the value of the + * function at the given + * point. Unless there is only + * one component (i.e. the + * function is scalar), you + * should state the component you + * want to have evaluated; it + * defaults to zero, i.e. the + * first component. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Return the gradient of the + * specified component of the + * function at the given point. + */ + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; + + /** + * Compute the Laplacian of a + * given component at point p. + */ + virtual double laplacian (const Point &p, + const unsigned int component = 0) const; + private: + /** + * Stored Fourier coefficients. + */ + const Point fourier_coefficients; }; -/** - * Given a sequence of wavenumber vectors and weights generate a sum - * of sine functions. Each wavenumber coefficient is given as a - * $d$-dimensional point $k$ in Fourier space, and the entire - * function is then recovered as - * $f(x) = \sum_j w_j sin(\sum_i k_i x_i) = Im(\sum_j w_j \exp(i k.x))$. - * - * @ingroup functions - * @author Wolfgang Bangerth, 2001 - */ + /** + * Given a sequence of wavenumber vectors and weights generate a sum + * of sine functions. Each wavenumber coefficient is given as a + * $d$-dimensional point $k$ in Fourier space, and the entire + * function is then recovered as + * $f(x) = \sum_j w_j sin(\sum_i k_i x_i) = Im(\sum_j w_j \exp(i k.x))$. + * + * @ingroup functions + * @author Wolfgang Bangerth, 2001 + */ template class FourierSineSum : public Function { - public: - /** - * Constructor. Take the Fourier - * coefficients in each space - * direction as argument. - */ - FourierSineSum (const std::vector > &fourier_coefficients, - const std::vector &weights); + public: + /** + * Constructor. Take the Fourier + * coefficients in each space + * direction as argument. + */ + FourierSineSum (const std::vector > &fourier_coefficients, + const std::vector &weights); + + /** + * Return the value of the + * function at the given + * point. Unless there is only + * one component (i.e. the + * function is scalar), you + * should state the component you + * want to have evaluated; it + * defaults to zero, i.e. the + * first component. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Return the gradient of the + * specified component of the + * function at the given point. + */ + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; - /** - * Return the value of the - * function at the given - * point. Unless there is only - * one component (i.e. the - * function is scalar), you - * should state the component you - * want to have evaluated; it - * defaults to zero, i.e. the - * first component. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Return the gradient of the - * specified component of the - * function at the given point. - */ - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - - /** - * Compute the Laplacian of a - * given component at point p. - */ - virtual double laplacian (const Point &p, - const unsigned int component = 0) const; - private: - /** - * Stored Fourier coefficients - * and weights. - */ - const std::vector > fourier_coefficients; - const std::vector weights; + /** + * Compute the Laplacian of a + * given component at point p. + */ + virtual double laplacian (const Point &p, + const unsigned int component = 0) const; + private: + /** + * Stored Fourier coefficients + * and weights. + */ + const std::vector > fourier_coefficients; + const std::vector weights; }; -/** - * Given a sequence of wavenumber vectors and weights generate a sum - * of cosine functions. Each wavenumber coefficient is given as a - * $d$-dimensional point $k$ in Fourier space, and the entire - * function is then recovered as - * $f(x) = \sum_j w_j cos(\sum_i k_i x_i) = Re(\sum_j w_j \exp(i k.x))$. - * - * @ingroup functions - * @author Wolfgang Bangerth, 2001 - */ + /** + * Given a sequence of wavenumber vectors and weights generate a sum + * of cosine functions. Each wavenumber coefficient is given as a + * $d$-dimensional point $k$ in Fourier space, and the entire + * function is then recovered as + * $f(x) = \sum_j w_j cos(\sum_i k_i x_i) = Re(\sum_j w_j \exp(i k.x))$. + * + * @ingroup functions + * @author Wolfgang Bangerth, 2001 + */ template class FourierCosineSum : public Function { - public: - /** - * Constructor. Take the Fourier - * coefficients in each space - * direction as argument. - */ - FourierCosineSum (const std::vector > &fourier_coefficients, - const std::vector &weights); - - /** - * Return the value of the - * function at the given - * point. Unless there is only - * one component (i.e. the - * function is scalar), you - * should state the component you - * want to have evaluated; it - * defaults to zero, i.e. the - * first component. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Return the gradient of the - * specified component of the - * function at the given point. - */ - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - - /** - * Compute the Laplacian of a - * given component at point p. - */ - virtual double laplacian (const Point &p, - const unsigned int component = 0) const; - - private: - /** - * Stored Fourier coefficients - * and weights. - */ - const std::vector > fourier_coefficients; - const std::vector weights; + public: + /** + * Constructor. Take the Fourier + * coefficients in each space + * direction as argument. + */ + FourierCosineSum (const std::vector > &fourier_coefficients, + const std::vector &weights); + + /** + * Return the value of the + * function at the given + * point. Unless there is only + * one component (i.e. the + * function is scalar), you + * should state the component you + * want to have evaluated; it + * defaults to zero, i.e. the + * first component. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Return the gradient of the + * specified component of the + * function at the given point. + */ + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; + + /** + * Compute the Laplacian of a + * given component at point p. + */ + virtual double laplacian (const Point &p, + const unsigned int component = 0) const; + + private: + /** + * Stored Fourier coefficients + * and weights. + */ + const std::vector > fourier_coefficients; + const std::vector weights; }; -/** - * Base function for cut-off function. This class stores the center - * and the radius of the supporting ball of a cut-off function. It - * also stores the number of the non-zero component, if the function - * is vector-valued. - * - * @ingroup functions - * @author Guido Kanschat, 2002 - */ + /** + * Base function for cut-off function. This class stores the center + * and the radius of the supporting ball of a cut-off function. It + * also stores the number of the non-zero component, if the function + * is vector-valued. + * + * @ingroup functions + * @author Guido Kanschat, 2002 + */ template class CutOffFunctionBase : public Function { - public: - /** - * Value used in the - * constructor of this and - * derived classes to denote - * that no component is - * selected. - */ - static const unsigned int no_component = numbers::invalid_unsigned_int; - - /** - * Constructor. Arguments are the - * center of the ball and its - * radius. - * - * If an argument select is - * given and not -1, the - * cut-off function will be - * non-zero for this component - * only. - */ - CutOffFunctionBase (const double radius = 1., - const Point = Point(), - const unsigned int n_components = 1, - const unsigned int select = CutOffFunctionBase::no_component); + public: + /** + * Value used in the + * constructor of this and + * derived classes to denote + * that no component is + * selected. + */ + static const unsigned int no_component = numbers::invalid_unsigned_int; + + /** + * Constructor. Arguments are the + * center of the ball and its + * radius. + * + * If an argument select is + * given and not -1, the + * cut-off function will be + * non-zero for this component + * only. + */ + CutOffFunctionBase (const double radius = 1., + const Point = Point(), + const unsigned int n_components = 1, + const unsigned int select = CutOffFunctionBase::no_component); - /** - * Move the center of the ball - * to new point p. - */ - void new_center (const Point& p); - - /** - * Set the radius of the ball to r. - */ - void new_radius (const double r); - - protected: - /** - * Center of the integration ball. - */ - Point center; - - /** - * Radius of the ball. - */ - double radius; - - /** - * Component selected. If - * no_component, the function is - * the same in all components. - */ - const unsigned int selected; + /** + * Move the center of the ball + * to new point p. + */ + void new_center (const Point &p); + + /** + * Set the radius of the ball to r. + */ + void new_radius (const double r); + + protected: + /** + * Center of the integration ball. + */ + Point center; + + /** + * Radius of the ball. + */ + double radius; + + /** + * Component selected. If + * no_component, the function is + * the same in all components. + */ + const unsigned int selected; }; -/** - * Cut-off function in L-infinity for an arbitrary ball. This - * function is the characteristic function of a ball around center - * with a specified radius, that is, - * \f[ - * f = \chi(B_r(c)). - * \f] - * If vector valued, it can be restricted - * to a single component. - * - * @ingroup functions - * @author Guido Kanschat, 2001, 2002 - */ + /** + * Cut-off function in L-infinity for an arbitrary ball. This + * function is the characteristic function of a ball around center + * with a specified radius, that is, + * \f[ + * f = \chi(B_r(c)). + * \f] + * If vector valued, it can be restricted + * to a single component. + * + * @ingroup functions + * @author Guido Kanschat, 2001, 2002 + */ template class CutOffFunctionLinfty : public CutOffFunctionBase { - public: - /** - * Constructor. Arguments are the - * center of the ball and its - * radius. - * - * If an argument select is - * given and not -1, the - * cut-off function will be - * non-zero for this component - * only. - */ - CutOffFunctionLinfty (const double radius = 1., - const Point = Point(), - const unsigned int n_components = 1, - const unsigned int select = CutOffFunctionBase::no_component); - - /** - * Function value at one point. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Function values at multiple points. - */ - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + public: + /** + * Constructor. Arguments are the + * center of the ball and its + * radius. + * + * If an argument select is + * given and not -1, the + * cut-off function will be + * non-zero for this component + * only. + */ + CutOffFunctionLinfty (const double radius = 1., + const Point = Point(), + const unsigned int n_components = 1, + const unsigned int select = CutOffFunctionBase::no_component); - /** - * Function values at multiple points. - */ - virtual void vector_value_list (const std::vector > &points, - std::vector > &values) const; + /** + * Function value at one point. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Function values at multiple points. + */ + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + /** + * Function values at multiple points. + */ + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; }; -/** - * Cut-off function for an arbitrary ball. This function is a cone - * with support in a ball of certain radius around center. The - * maximum value is 1. If vector valued, it can be restricted - * to a single component. - * - * @ingroup functions - * @author Guido Kanschat, 2001, 2002 - */ + /** + * Cut-off function for an arbitrary ball. This function is a cone + * with support in a ball of certain radius around center. The + * maximum value is 1. If vector valued, it can be restricted + * to a single component. + * + * @ingroup functions + * @author Guido Kanschat, 2001, 2002 + */ template class CutOffFunctionW1 : public CutOffFunctionBase { - public: - /** - * Constructor. Arguments are the - * center of the ball and its - * radius. - * radius. - * - * If an argument select is - * given, the cut-off function - * will be non-zero for this - * component only. - */ - CutOffFunctionW1 (const double radius = 1., - const Point = Point(), - const unsigned int n_components = 1, - const unsigned int select = CutOffFunctionBase::no_component); - - /** - * Function value at one point. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Function values at multiple points. - */ - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; - - /** - * Function values at multiple points. - */ - virtual void vector_value_list (const std::vector > &points, - std::vector > &values) const; + public: + /** + * Constructor. Arguments are the + * center of the ball and its + * radius. + * radius. + * + * If an argument select is + * given, the cut-off function + * will be non-zero for this + * component only. + */ + CutOffFunctionW1 (const double radius = 1., + const Point = Point(), + const unsigned int n_components = 1, + const unsigned int select = CutOffFunctionBase::no_component); + + /** + * Function value at one point. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Function values at multiple points. + */ + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + /** + * Function values at multiple points. + */ + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; }; -/** - * Cut-off function for an arbitrary ball. This is the traditional - * cut-off function in C-infinity for a ball of certain radius - * around center, $f(r)=exp(1-1/(1-r**2/s**2))$, where $r$ is the - * distance to the center, and $s$ is the radius of the sphere. If - * vector valued, it can be restricted to a single component. - * - * @ingroup functions - * @author Guido Kanschat, 2001, 2002 - */ + /** + * Cut-off function for an arbitrary ball. This is the traditional + * cut-off function in C-infinity for a ball of certain radius + * around center, $f(r)=exp(1-1/(1-r**2/s**2))$, where $r$ is the + * distance to the center, and $s$ is the radius of the sphere. If + * vector valued, it can be restricted to a single component. + * + * @ingroup functions + * @author Guido Kanschat, 2001, 2002 + */ template class CutOffFunctionCinfty : public CutOffFunctionBase { - public: - /** - * Constructor. Arguments are the - * center of the ball and its - * radius. - * radius. - * - * If an argument select is - * given, the cut-off function - * will be non-zero for this - * component only. - */ - CutOffFunctionCinfty (const double radius = 1., - const Point = Point(), - const unsigned int n_components = 1, - const unsigned int select = CutOffFunctionBase::no_component); - - /** - * Function value at one point. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Function values at multiple points. - */ - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + public: + /** + * Constructor. Arguments are the + * center of the ball and its + * radius. + * radius. + * + * If an argument select is + * given, the cut-off function + * will be non-zero for this + * component only. + */ + CutOffFunctionCinfty (const double radius = 1., + const Point = Point(), + const unsigned int n_components = 1, + const unsigned int select = CutOffFunctionBase::no_component); - /** - * Function values at multiple points. - */ - virtual void vector_value_list (const std::vector > &points, - std::vector > &values) const; - - /** - * Function gradient at one point. - */ - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; + /** + * Function value at one point. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Function values at multiple points. + */ + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + /** + * Function values at multiple points. + */ + virtual void vector_value_list (const std::vector > &points, + std::vector > &values) const; + + /** + * Function gradient at one point. + */ + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; }; -/** - * A class that represents a function object for a monomial. Monomials are - * polynomials with only a single term, i.e. in 1-d they have the form - * $x^\alpha$, in 2-d the form $x_1^{\alpha_1}x_2^{\alpha_2}$, and in 3-d - * $x_1^{\alpha_1}x_2^{\alpha_2}x_3^{\alpha_3}$. Monomials are therefore - * described by a $dim$-tuple of exponents. Consequently, the class's - * constructor takes a Tensor<1,dim> to describe the set of exponents. Most of - * the time these exponents will of course be integers, but real exponents are - * of course equally valid. - * - * @author Wolfgang Bangerth, 2006 - */ + /** + * A class that represents a function object for a monomial. Monomials are + * polynomials with only a single term, i.e. in 1-d they have the form + * $x^\alpha$, in 2-d the form $x_1^{\alpha_1}x_2^{\alpha_2}$, and in 3-d + * $x_1^{\alpha_1}x_2^{\alpha_2}x_3^{\alpha_3}$. Monomials are therefore + * described by a $dim$-tuple of exponents. Consequently, the class's + * constructor takes a Tensor<1,dim> to describe the set of exponents. Most of + * the time these exponents will of course be integers, but real exponents are + * of course equally valid. + * + * @author Wolfgang Bangerth, 2006 + */ template class Monomial : public Function { - public: - /** - * Constructor. The first argument is - * explained in the general description - * of the class. The second argument - * denotes the number of vector - * components this object shall - * represent. All vector components - * will have the same value. - */ - Monomial (const Tensor<1,dim> &exponents, - const unsigned int n_components = 1); - - /** - * Function value at one point. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Return all components of a - * vector-valued function at a - * given point. - * - * values shall have the right - * size beforehand, - * i.e. #n_components. - */ - virtual void vector_value (const Point &p, - Vector &values) const; - - /** - * Function values at multiple points. - */ - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; + public: + /** + * Constructor. The first argument is + * explained in the general description + * of the class. The second argument + * denotes the number of vector + * components this object shall + * represent. All vector components + * will have the same value. + */ + Monomial (const Tensor<1,dim> &exponents, + const unsigned int n_components = 1); + + /** + * Function value at one point. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Return all components of a + * vector-valued function at a + * given point. + * + * values shall have the right + * size beforehand, + * i.e. #n_components. + */ + virtual void vector_value (const Point &p, + Vector &values) const; + + /** + * Function values at multiple points. + */ + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; + + /** + * Function gradient at one point. + */ + virtual Tensor<1,dim> gradient (const Point &p, + const unsigned int component = 0) const; - /** - * Function gradient at one point. - */ - virtual Tensor<1,dim> gradient (const Point &p, - const unsigned int component = 0) const; - - private: - /** - * The set of exponents. - */ - const Tensor<1,dim> exponents; + private: + /** + * The set of exponents. + */ + const Tensor<1,dim> exponents; }; } diff --git a/deal.II/include/deal.II/base/function_parser.h b/deal.II/include/deal.II/base/function_parser.h index 538e643eaa..6e0654f798 100644 --- a/deal.II/include/deal.II/base/function_parser.h +++ b/deal.II/include/deal.II/base/function_parser.h @@ -314,256 +314,256 @@ template class Vector; template class FunctionParser : public Function { - public: - /** - * Constructor for Parsed - * functions. Its arguments are - * the same of the base class - * Function. The only difference - * is that this object needs to - * be initialized with - * initialize() method before you - * can use it. If an attempt to - * use this function is made - * before the initialize() method - * has been called, then an - * exception is thrown. - */ - FunctionParser (const unsigned int n_components = 1, - const double initial_time = 0.0); - - /** - * Destructor. Explicitly delete - * the FunctionParser objects - * (there is one for each - * component of the function). - */ - ~FunctionParser(); - - /** - * Type for the constant - * map. Used by the initialize() - * method. - */ - typedef std::map ConstMap; - - /** - * Iterator for the constants - * map. Used by the initialize() - * method. - */ - typedef ConstMap::iterator ConstMapIterator; - - /** - * Initialize the function. This methods - * accepts the following parameters: - * - * vars: a string with - * the variables that will be used by the - * expressions to be evaluated. Note that - * the variables can have any name (of - * course different from the function - * names defined above!), but the order - * IS important. The first variable will - * correspond to the first component of - * the point in which the function is - * evaluated, the second variable to the - * second component and so forth. If this - * function is also time dependent, then - * it is necessary to specify it by - * setting the time_dependent parameter - * to true. An exception is thrown if - * the number of variables specified here - * is different from dim (if this - * function is not time-dependent) or - * from dim+1 (if it is time-dependent). - * - * expressions: a list of strings - * containing the expressions that will - * be byte compiled by the internal - * parser (FunctionParser). Note that the - * size of this vector must match exactly - * the number of components of the - * FunctionParser, as declared in the - * constructor. If this is not the case, - * an exception is thrown. - * - * - * constants: a map of constants - * used to pass any necessary constant - * that we want to specify in our - * expressions (in the example above the - * number pi). An expression is valid if - * and only if it contains only defined - * variables and defined constants (other - * than the functions specified - * above). If a constant is given whose - * name is not valid (eg: - * constants["sin"] = 1.5;) an - * exception is thrown. - * - * time_dependent. If this is a - * time dependent function, then the last - * variable declared in vars is - * assumed to be the time variable, and - * this->get_time() is used to initialize - * it when evaluating the - * function. Naturally the number of - * variables parsed by the initialize() - * method in this case is dim+1. The - * value of this parameter defaults to - * false, i.e. do not consider time. - * - * use_degrees. Parameter to - * decide if the trigonometric functions - * work in radians or degrees. The - * default for this parameter is false, - * i.e. use radians and not degrees. - */ - void initialize (const std::string &vars, - const std::vector &expressions, - const ConstMap &constants, - const bool time_dependent = false, - const bool use_degrees = false); - - - /** - * Initialize the function. Same as - * above, but with an additional argument - * units - a map of units passed to - * FunctionParser via AddUnint. - * - * Can be used as "3cm". - * Have higher precedence in parsing, i.e. - * if cm=10 then 3/2cm is 3 /(2*10). - * See contrib/functionparser/fparser.txt - * for more details. - */ - void initialize (const std::string &vars, - const std::vector &expressions, - const ConstMap &constants, - const ConstMap &units, - const bool time_dependent = false, - const bool use_degrees = false); - - /** - * Initialize the function. Same as - * above, but accepts a string rather - * than a vector of strings. If this is a - * vector valued function, its - * componenents are expected to be - * separated by a semicolon. An exception - * is thrown if this method is called and - * the number of components successfully - * parsed does not match the number of - * components of the base function. - */ - void initialize (const std::string &vars, - const std::string &expression, - const ConstMap &constants, - const bool time_dependent = false, - const bool use_degrees = false); - - /** - * Initialize the function. Same as - * above, but with units. - */ - - void initialize (const std::string &vars, - const std::string &expression, - const ConstMap &constants, - const ConstMap &units, - const bool time_dependent = false, - const bool use_degrees = false); - - /** - * A function that returns - * default names for variables, - * to be used in the first - * argument of the initialize() - * functions: it returns "x" in - * 1d, "x,y" in 2d, and "x,y,z" - * in 3d. - */ - static - std::string - default_variable_names (); - - /** - * Return the value of the - * function at the given - * point. Unless there is only - * one component (i.e. the - * function is scalar), you - * should state the component you - * want to have evaluated; it - * defaults to zero, i.e. the - * first component. - */ - virtual double value (const Point &p, - const unsigned int component = 0) const; - - /** - * Return all components of a - * vector-valued function at a - * given point. - * - * values shall have the - * right size beforehand, - * i.e. #n_components. - */ - virtual void vector_value (const Point &p, - Vector &values) const; - - /** @addtogroup Exceptions - * @{ */ - DeclException2 (ExcParseError, - int, char*, - << "Parsing Error at Column " << arg1 - << ". The parser said: " << arg2); - - DeclException2 (ExcInvalidExpressionSize, - int, int, - << "The number of components (" << arg1 - << ") is not equal to the number of expressions (" - << arg2 << ")."); - - //@} - private: - /** - * A pointer to the actual - * function parsers. The pointer is - * to an array of parsers, not - * just a single one. The length of - * the array equals the number of - * vector components. - */ - fparser::FunctionParser *fp; - - /** - * State of usability. This - * variable is checked every time - * the function is called for - * evaluation. It's set to true - * in the initialize() methods. - */ - bool initialized; - - /** - * Number of variables. If this - * is also a function of time, - * then the number of variables - * is dim+1, otherwhise it is - * dim. In the case that this is - * a time dependent function, the - * time is supposed to be the - * last variable. If #n_vars is - * not identical to the number of - * the variables parsed by the - * initialize() method, then an - * exception is thrown. - */ - unsigned int n_vars; +public: + /** + * Constructor for Parsed + * functions. Its arguments are + * the same of the base class + * Function. The only difference + * is that this object needs to + * be initialized with + * initialize() method before you + * can use it. If an attempt to + * use this function is made + * before the initialize() method + * has been called, then an + * exception is thrown. + */ + FunctionParser (const unsigned int n_components = 1, + const double initial_time = 0.0); + + /** + * Destructor. Explicitly delete + * the FunctionParser objects + * (there is one for each + * component of the function). + */ + ~FunctionParser(); + + /** + * Type for the constant + * map. Used by the initialize() + * method. + */ + typedef std::map ConstMap; + + /** + * Iterator for the constants + * map. Used by the initialize() + * method. + */ + typedef ConstMap::iterator ConstMapIterator; + + /** + * Initialize the function. This methods + * accepts the following parameters: + * + * vars: a string with + * the variables that will be used by the + * expressions to be evaluated. Note that + * the variables can have any name (of + * course different from the function + * names defined above!), but the order + * IS important. The first variable will + * correspond to the first component of + * the point in which the function is + * evaluated, the second variable to the + * second component and so forth. If this + * function is also time dependent, then + * it is necessary to specify it by + * setting the time_dependent parameter + * to true. An exception is thrown if + * the number of variables specified here + * is different from dim (if this + * function is not time-dependent) or + * from dim+1 (if it is time-dependent). + * + * expressions: a list of strings + * containing the expressions that will + * be byte compiled by the internal + * parser (FunctionParser). Note that the + * size of this vector must match exactly + * the number of components of the + * FunctionParser, as declared in the + * constructor. If this is not the case, + * an exception is thrown. + * + * + * constants: a map of constants + * used to pass any necessary constant + * that we want to specify in our + * expressions (in the example above the + * number pi). An expression is valid if + * and only if it contains only defined + * variables and defined constants (other + * than the functions specified + * above). If a constant is given whose + * name is not valid (eg: + * constants["sin"] = 1.5;) an + * exception is thrown. + * + * time_dependent. If this is a + * time dependent function, then the last + * variable declared in vars is + * assumed to be the time variable, and + * this->get_time() is used to initialize + * it when evaluating the + * function. Naturally the number of + * variables parsed by the initialize() + * method in this case is dim+1. The + * value of this parameter defaults to + * false, i.e. do not consider time. + * + * use_degrees. Parameter to + * decide if the trigonometric functions + * work in radians or degrees. The + * default for this parameter is false, + * i.e. use radians and not degrees. + */ + void initialize (const std::string &vars, + const std::vector &expressions, + const ConstMap &constants, + const bool time_dependent = false, + const bool use_degrees = false); + + + /** + * Initialize the function. Same as + * above, but with an additional argument + * units - a map of units passed to + * FunctionParser via AddUnint. + * + * Can be used as "3cm". + * Have higher precedence in parsing, i.e. + * if cm=10 then 3/2cm is 3 /(2*10). + * See contrib/functionparser/fparser.txt + * for more details. + */ + void initialize (const std::string &vars, + const std::vector &expressions, + const ConstMap &constants, + const ConstMap &units, + const bool time_dependent = false, + const bool use_degrees = false); + + /** + * Initialize the function. Same as + * above, but accepts a string rather + * than a vector of strings. If this is a + * vector valued function, its + * componenents are expected to be + * separated by a semicolon. An exception + * is thrown if this method is called and + * the number of components successfully + * parsed does not match the number of + * components of the base function. + */ + void initialize (const std::string &vars, + const std::string &expression, + const ConstMap &constants, + const bool time_dependent = false, + const bool use_degrees = false); + + /** + * Initialize the function. Same as + * above, but with units. + */ + + void initialize (const std::string &vars, + const std::string &expression, + const ConstMap &constants, + const ConstMap &units, + const bool time_dependent = false, + const bool use_degrees = false); + + /** + * A function that returns + * default names for variables, + * to be used in the first + * argument of the initialize() + * functions: it returns "x" in + * 1d, "x,y" in 2d, and "x,y,z" + * in 3d. + */ + static + std::string + default_variable_names (); + + /** + * Return the value of the + * function at the given + * point. Unless there is only + * one component (i.e. the + * function is scalar), you + * should state the component you + * want to have evaluated; it + * defaults to zero, i.e. the + * first component. + */ + virtual double value (const Point &p, + const unsigned int component = 0) const; + + /** + * Return all components of a + * vector-valued function at a + * given point. + * + * values shall have the + * right size beforehand, + * i.e. #n_components. + */ + virtual void vector_value (const Point &p, + Vector &values) const; + + /** @addtogroup Exceptions + * @{ */ + DeclException2 (ExcParseError, + int, char *, + << "Parsing Error at Column " << arg1 + << ". The parser said: " << arg2); + + DeclException2 (ExcInvalidExpressionSize, + int, int, + << "The number of components (" << arg1 + << ") is not equal to the number of expressions (" + << arg2 << ")."); + + //@} +private: + /** + * A pointer to the actual + * function parsers. The pointer is + * to an array of parsers, not + * just a single one. The length of + * the array equals the number of + * vector components. + */ + fparser::FunctionParser *fp; + + /** + * State of usability. This + * variable is checked every time + * the function is called for + * evaluation. It's set to true + * in the initialize() methods. + */ + bool initialized; + + /** + * Number of variables. If this + * is also a function of time, + * then the number of variables + * is dim+1, otherwhise it is + * dim. In the case that this is + * a time dependent function, the + * time is supposed to be the + * last variable. If #n_vars is + * not identical to the number of + * the variables parsed by the + * initialize() method, then an + * exception is thrown. + */ + unsigned int n_vars; }; @@ -573,14 +573,14 @@ FunctionParser::default_variable_names () { switch (dim) { - case 1: - return "x"; - case 2: - return "x,y"; - case 3: - return "x,y,z"; - default: - Assert (false, ExcNotImplemented()); + case 1: + return "x"; + case 2: + return "x,y"; + case 3: + return "x,y,z"; + default: + Assert (false, ExcNotImplemented()); } return ""; } diff --git a/deal.II/include/deal.II/base/function_time.h b/deal.II/include/deal.II/base/function_time.h index d5d28254a5..62fbb75cd9 100644 --- a/deal.II/include/deal.II/base/function_time.h +++ b/deal.II/include/deal.II/base/function_time.h @@ -61,41 +61,41 @@ DEAL_II_NAMESPACE_OPEN */ class FunctionTime { - public: - /** - * Constructor. May take an initial vakue - * for the time variable, which defaults - * to zero. - */ - FunctionTime (const double initial_time = 0.0); +public: + /** + * Constructor. May take an initial vakue + * for the time variable, which defaults + * to zero. + */ + FunctionTime (const double initial_time = 0.0); - /** - * Virtual destructor. - */ - virtual ~FunctionTime(); + /** + * Virtual destructor. + */ + virtual ~FunctionTime(); - /** - * Return the value of the time variable/ - */ - double get_time () const; + /** + * Return the value of the time variable/ + */ + double get_time () const; - /** - * Set the time to new_time, overwriting - * the old value. - */ - virtual void set_time (const double new_time); + /** + * Set the time to new_time, overwriting + * the old value. + */ + virtual void set_time (const double new_time); - /** - * Advance the time by the given - * time step delta_t. - */ - virtual void advance_time (const double delta_t); + /** + * Advance the time by the given + * time step delta_t. + */ + virtual void advance_time (const double delta_t); - private: - /** - * Store the present time. - */ - double time; +private: + /** + * Store the present time. + */ + double time; }; diff --git a/deal.II/include/deal.II/base/geometry_info.h b/deal.II/include/deal.II/base/geometry_info.h index e2b005a2d7..7c2c7bff25 100644 --- a/deal.II/include/deal.II/base/geometry_info.h +++ b/deal.II/include/deal.II/base/geometry_info.h @@ -37,46 +37,46 @@ DEAL_II_NAMESPACE_OPEN template struct RefinementPossibilities { - /** - * Possible values for refinement - * cases in the current - * dimension. - * - * Note the construction of the - * values: the lowest bit - * describes a cut of the x-axis, - * the second to lowest bit - * corresponds to a cut of the - * y-axis and the third to lowest - * bit corresponds to a cut of - * the z-axis. Thus, the - * following relations hold - * (among others): - * - * @code - * cut_xy == cut_x | cut_y - * cut_xyz == cut_xy | cut_xz - * cut_x == cut_xy & cut_xz - * @endcode - * - * Only those cuts that are - * reasonable in a given space - * dimension are offered, of - * course. - * - * In addition, the tag - * isotropic_refinement - * denotes isotropic refinement - * in the space dimension - * selected by the template - * argument of this class. - */ - enum Possibilities - { - no_refinement= 0, + /** + * Possible values for refinement + * cases in the current + * dimension. + * + * Note the construction of the + * values: the lowest bit + * describes a cut of the x-axis, + * the second to lowest bit + * corresponds to a cut of the + * y-axis and the third to lowest + * bit corresponds to a cut of + * the z-axis. Thus, the + * following relations hold + * (among others): + * + * @code + * cut_xy == cut_x | cut_y + * cut_xyz == cut_xy | cut_xz + * cut_x == cut_xy & cut_xz + * @endcode + * + * Only those cuts that are + * reasonable in a given space + * dimension are offered, of + * course. + * + * In addition, the tag + * isotropic_refinement + * denotes isotropic refinement + * in the space dimension + * selected by the template + * argument of this class. + */ + enum Possibilities + { + no_refinement= 0, - isotropic_refinement = static_cast(-1) - }; + isotropic_refinement = static_cast(-1) + }; }; @@ -94,47 +94,47 @@ struct RefinementPossibilities template <> struct RefinementPossibilities<1> { - /** - * Possible values for refinement - * cases in the current - * dimension. - * - * Note the construction of the - * values: the lowest bit - * describes a cut of the x-axis, - * the second to lowest bit - * corresponds to a cut of the - * y-axis and the third to lowest - * bit corresponds to a cut of - * the z-axis. Thus, the - * following relations hold - * (among others): - * - * @code - * cut_xy == cut_x | cut_y - * cut_xyz == cut_xy | cut_xz - * cut_x == cut_xy & cut_xz - * @endcode - * - * Only those cuts that are - * reasonable in a given space - * dimension are offered, of - * course. - * - * In addition, the tag - * isotropic_refinement - * denotes isotropic refinement - * in the space dimension - * selected by the template - * argument of this class. - */ - enum Possibilities - { - no_refinement= 0, - cut_x = 1, + /** + * Possible values for refinement + * cases in the current + * dimension. + * + * Note the construction of the + * values: the lowest bit + * describes a cut of the x-axis, + * the second to lowest bit + * corresponds to a cut of the + * y-axis and the third to lowest + * bit corresponds to a cut of + * the z-axis. Thus, the + * following relations hold + * (among others): + * + * @code + * cut_xy == cut_x | cut_y + * cut_xyz == cut_xy | cut_xz + * cut_x == cut_xy & cut_xz + * @endcode + * + * Only those cuts that are + * reasonable in a given space + * dimension are offered, of + * course. + * + * In addition, the tag + * isotropic_refinement + * denotes isotropic refinement + * in the space dimension + * selected by the template + * argument of this class. + */ + enum Possibilities + { + no_refinement= 0, + cut_x = 1, - isotropic_refinement = cut_x - }; + isotropic_refinement = cut_x + }; }; @@ -153,49 +153,49 @@ struct RefinementPossibilities<1> template <> struct RefinementPossibilities<2> { - /** - * Possible values for refinement - * cases in the current - * dimension. - * - * Note the construction of the - * values: the lowest bit - * describes a cut of the x-axis, - * the second to lowest bit - * corresponds to a cut of the - * y-axis and the third to lowest - * bit corresponds to a cut of - * the z-axis. Thus, the - * following relations hold - * (among others): - * - * @code - * cut_xy == cut_x | cut_y - * cut_xyz == cut_xy | cut_xz - * cut_x == cut_xy & cut_xz - * @endcode - * - * Only those cuts that are - * reasonable in a given space - * dimension are offered, of - * course. - * - * In addition, the tag - * isotropic_refinement - * denotes isotropic refinement - * in the space dimension - * selected by the template - * argument of this class. - */ - enum Possibilities - { - no_refinement= 0, - cut_x = 1, - cut_y = 2, - cut_xy = cut_x | cut_y, + /** + * Possible values for refinement + * cases in the current + * dimension. + * + * Note the construction of the + * values: the lowest bit + * describes a cut of the x-axis, + * the second to lowest bit + * corresponds to a cut of the + * y-axis and the third to lowest + * bit corresponds to a cut of + * the z-axis. Thus, the + * following relations hold + * (among others): + * + * @code + * cut_xy == cut_x | cut_y + * cut_xyz == cut_xy | cut_xz + * cut_x == cut_xy & cut_xz + * @endcode + * + * Only those cuts that are + * reasonable in a given space + * dimension are offered, of + * course. + * + * In addition, the tag + * isotropic_refinement + * denotes isotropic refinement + * in the space dimension + * selected by the template + * argument of this class. + */ + enum Possibilities + { + no_refinement= 0, + cut_x = 1, + cut_y = 2, + cut_xy = cut_x | cut_y, - isotropic_refinement = cut_xy - }; + isotropic_refinement = cut_xy + }; }; @@ -215,53 +215,53 @@ struct RefinementPossibilities<2> template <> struct RefinementPossibilities<3> { - /** - * Possible values for refinement - * cases in the current - * dimension. - * - * Note the construction of the - * values: the lowest bit - * describes a cut of the x-axis, - * the second to lowest bit - * corresponds to a cut of the - * y-axis and the third to lowest - * bit corresponds to a cut of - * the z-axis. Thus, the - * following relations hold - * (among others): - * - * @code - * cut_xy == cut_x | cut_y - * cut_xyz == cut_xy | cut_xz - * cut_x == cut_xy & cut_xz - * @endcode - * - * Only those cuts that are - * reasonable in a given space - * dimension are offered, of - * course. - * - * In addition, the tag - * isotropic_refinement - * denotes isotropic refinement - * in the space dimension - * selected by the template - * argument of this class. - */ - enum Possibilities - { - no_refinement= 0, - cut_x = 1, - cut_y = 2, - cut_xy = cut_x | cut_y, - cut_z = 4, - cut_xz = cut_x | cut_z, - cut_yz = cut_y | cut_z, - cut_xyz = cut_x | cut_y | cut_z, - - isotropic_refinement = cut_xyz - }; + /** + * Possible values for refinement + * cases in the current + * dimension. + * + * Note the construction of the + * values: the lowest bit + * describes a cut of the x-axis, + * the second to lowest bit + * corresponds to a cut of the + * y-axis and the third to lowest + * bit corresponds to a cut of + * the z-axis. Thus, the + * following relations hold + * (among others): + * + * @code + * cut_xy == cut_x | cut_y + * cut_xyz == cut_xy | cut_xz + * cut_x == cut_xy & cut_xz + * @endcode + * + * Only those cuts that are + * reasonable in a given space + * dimension are offered, of + * course. + * + * In addition, the tag + * isotropic_refinement + * denotes isotropic refinement + * in the space dimension + * selected by the template + * argument of this class. + */ + enum Possibilities + { + no_refinement= 0, + cut_x = 1, + cut_y = 2, + cut_xy = cut_x | cut_y, + cut_z = 4, + cut_xz = cut_x | cut_z, + cut_yz = cut_y | cut_z, + cut_xyz = cut_x | cut_y | cut_z, + + isotropic_refinement = cut_xyz + }; }; @@ -279,137 +279,138 @@ struct RefinementPossibilities<3> template class RefinementCase : public RefinementPossibilities { - public: - /** - * Default constructor. Initialize the - * refinement case with no_refinement. - */ - RefinementCase (); - - /** - * Constructor. Take and store a - * value indicating a particular - * refinement from the list of - * possible refinements specified - * in the base class. - */ - RefinementCase (const typename RefinementPossibilities::Possibilities refinement_case); - - /** - * Constructor. Take and store a - * value indicating a particular - * refinement as a bit field. To - * avoid implicit conversions to - * and from integral values, this - * constructor is marked as - * explicit. - */ - explicit RefinementCase (const unsigned char refinement_case); - - /** - * Return the numeric value - * stored by this class. While - * the presence of this operator - * might seem dangerous, it is - * useful in cases where one - * would like to have code like - * switch - * (refinement_flag)... case - * RefinementCase::cut_x: - * ... , which can be - * written as switch - * (static_cast@(refinement_flag). Another - * application is to use an - * object of the current type as - * an index into an array; - * however, this use is - * deprecated as it assumes a - * certain mapping from the - * symbolic flags defined in the - * RefinementPossibilities base - * class to actual numerical - * values (the array indices). - */ - operator unsigned char () const; - - /** - * Return the union of the - * refinement flags represented - * by the current object and the - * one given as argument. - */ - RefinementCase operator | (const RefinementCase &r) const; - - /** - * Return the intersection of the - * refinement flags represented - * by the current object and the - * one given as argument. - */ - RefinementCase operator & (const RefinementCase &r) const; - - /** - * Return the negation of the - * refinement flags represented - * by the current object. For - * example, in 2d, if the current - * object holds the flag - * cut_x, then the - * returned value will be - * cut_y; if the - * current value is - * isotropic_refinement - * then the result will be - * no_refinement; - * etc. - */ - RefinementCase operator ~ () const; - - - /** - * Return the flag that - * corresponds to cutting a cell - * along the axis given as - * argument. For example, if - * i=0 then the - * returned value is - * RefinementPossibilities::cut_x. - */ - static - RefinementCase cut_axis (const unsigned int i); - - /** - * Return the amount of memory - * occupied by an object of this - * type. - */ - static std::size_t memory_consumption (); - - /** - * Read or write the data of this object to or - * from a stream for the purpose of serialization - */ - template - void serialize(Archive & ar, - const unsigned int version); - - /** - * Exception. - */ - DeclException1 (ExcInvalidRefinementCase, - int, - << "The refinement flags given (" << arg1 << ") contain set bits that do not " - << "make sense for the space dimension of the object to which they are applied."); - - private: - /** - * Store the refinement case as a - * bit field with as many bits as - * are necessary in any given - * dimension. - */ - unsigned char value : (dim > 0 ? dim : 1); +public: + /** + * Default constructor. Initialize the + * refinement case with no_refinement. + */ + RefinementCase (); + + /** + * Constructor. Take and store a + * value indicating a particular + * refinement from the list of + * possible refinements specified + * in the base class. + */ + RefinementCase (const typename RefinementPossibilities::Possibilities refinement_case); + + /** + * Constructor. Take and store a + * value indicating a particular + * refinement as a bit field. To + * avoid implicit conversions to + * and from integral values, this + * constructor is marked as + * explicit. + */ + explicit RefinementCase (const unsigned char refinement_case); + + /** + * Return the numeric value + * stored by this class. While + * the presence of this operator + * might seem dangerous, it is + * useful in cases where one + * would like to have code like + * switch + * (refinement_flag)... case + * RefinementCase::cut_x: + * ... , which can be + * written as switch + * (static_cast@(refinement_flag). Another + * application is to use an + * object of the current type as + * an index into an array; + * however, this use is + * deprecated as it assumes a + * certain mapping from the + * symbolic flags defined in the + * RefinementPossibilities base + * class to actual numerical + * values (the array indices). + */ + operator unsigned char () const; + + /** + * Return the union of the + * refinement flags represented + * by the current object and the + * one given as argument. + */ + RefinementCase operator | (const RefinementCase &r) const; + + /** + * Return the intersection of the + * refinement flags represented + * by the current object and the + * one given as argument. + */ + RefinementCase operator & (const RefinementCase &r) const; + + /** + * Return the negation of the + * refinement flags represented + * by the current object. For + * example, in 2d, if the current + * object holds the flag + * cut_x, then the + * returned value will be + * cut_y; if the + * current value is + * isotropic_refinement + * then the result will be + * no_refinement; + * etc. + */ + RefinementCase operator ~ () const; + + + /** + * Return the flag that + * corresponds to cutting a cell + * along the axis given as + * argument. For example, if + * i=0 then the + * returned value is + * RefinementPossibilities::cut_x. + */ + static + RefinementCase cut_axis (const unsigned int i); + + /** + * Return the amount of memory + * occupied by an object of this + * type. + */ + static std::size_t memory_consumption (); + + /** + * Read or write the data of this object to or + * from a stream for the purpose of serialization + */ + template + void serialize(Archive &ar, + const unsigned int version); + + /** + * Exception. + */ + DeclException1 (ExcInvalidRefinementCase, + int, + << "The refinement flags given (" << arg1 << ") contain set bits that do not " + << "make sense for the space dimension of the object to which they are applied."); + +private: + /** + * Store the refinement case as a + * bit field with as many bits as + * are necessary in any given + * dimension. + */ +unsigned char value : + (dim > 0 ? dim : 1); }; @@ -417,345 +418,346 @@ namespace internal { -/** - * A class that provides all possible situations a face (in the - * current space dimension @p dim) might be subdivided into - * subfaces. For dim=1 and dim=2 they - * correspond to the cases given in - * RefinementPossibilities@. However, - * SubfacePossibilities@<3@> includes the refinement - * cases of RefinementPossibilities@<2@>, but - * additionally some subface possibilities a face might be subdivided - * into which occur through repeated anisotropic refinement steps - * performed on one of two neighboring cells. - * - * This general template is unused except in some weird template - * constructs. Actual is made, however, of the specializations - * SubfacePossibilities@<1@>, - * SubfacePossibilities@<2@> and - * SubfacePossibilities@<3@>. - * - * @ingroup aniso - * @author Tobias Leicht 2007, Ralf Hartmann, 2008 - */ + /** + * A class that provides all possible situations a face (in the + * current space dimension @p dim) might be subdivided into + * subfaces. For dim=1 and dim=2 they + * correspond to the cases given in + * RefinementPossibilities@. However, + * SubfacePossibilities@<3@> includes the refinement + * cases of RefinementPossibilities@<2@>, but + * additionally some subface possibilities a face might be subdivided + * into which occur through repeated anisotropic refinement steps + * performed on one of two neighboring cells. + * + * This general template is unused except in some weird template + * constructs. Actual is made, however, of the specializations + * SubfacePossibilities@<1@>, + * SubfacePossibilities@<2@> and + * SubfacePossibilities@<3@>. + * + * @ingroup aniso + * @author Tobias Leicht 2007, Ralf Hartmann, 2008 + */ template struct SubfacePossibilities { - /** - * Possible cases of faces - * being subdivided into - * subface. - */ - enum Possibilities - { - case_none = 0, - - case_isotropic = static_cast(-1) - }; + /** + * Possible cases of faces + * being subdivided into + * subface. + */ + enum Possibilities + { + case_none = 0, + + case_isotropic = static_cast(-1) + }; }; -/** - * A class that provides all possible situations a face (in the - * current space dimension @p dim) might be subdivided into - * subfaces. - * - * For dim=0 we provide a dummy implementation only. - * - * @ingroup aniso - * @author Ralf Hartmann, 2008 - */ + /** + * A class that provides all possible situations a face (in the + * current space dimension @p dim) might be subdivided into + * subfaces. + * + * For dim=0 we provide a dummy implementation only. + * + * @ingroup aniso + * @author Ralf Hartmann, 2008 + */ template <> struct SubfacePossibilities<0> { - /** - * Possible cases of faces - * being subdivided into - * subface. - * - * Dummy implementation. - */ - enum Possibilities - { - case_none = 0, - - case_isotropic = case_none - }; + /** + * Possible cases of faces + * being subdivided into + * subface. + * + * Dummy implementation. + */ + enum Possibilities + { + case_none = 0, + + case_isotropic = case_none + }; }; -/** - * A class that provides all possible situations a face (in the - * current space dimension @p dim) might be subdivided into - * subfaces. - * - * For dim=1 there are no faces. Thereby, there are no - * subface possibilities. - * - * @ingroup aniso - * @author Ralf Hartmann, 2008 - */ + /** + * A class that provides all possible situations a face (in the + * current space dimension @p dim) might be subdivided into + * subfaces. + * + * For dim=1 there are no faces. Thereby, there are no + * subface possibilities. + * + * @ingroup aniso + * @author Ralf Hartmann, 2008 + */ template <> struct SubfacePossibilities<1> { - /** - * Possible cases of faces - * being subdivided into - * subface. - * - * In 1d there are no faces, - * thus no subface - * possibilities. - */ - enum Possibilities - { - case_none = 0, - - case_isotropic = case_none - }; + /** + * Possible cases of faces + * being subdivided into + * subface. + * + * In 1d there are no faces, + * thus no subface + * possibilities. + */ + enum Possibilities + { + case_none = 0, + + case_isotropic = case_none + }; }; -/** - * A class that provides all possible situations a face (in the - * current space dimension @p dim) might be subdivided into - * subfaces. - * - * This specialization is used for dim=2, where it offers - * the following possibilities: a face (line) being refined - * (case_x) or not refined (case_no). - * - * @ingroup aniso - * @author Ralf Hartmann, 2008 - */ + /** + * A class that provides all possible situations a face (in the + * current space dimension @p dim) might be subdivided into + * subfaces. + * + * This specialization is used for dim=2, where it offers + * the following possibilities: a face (line) being refined + * (case_x) or not refined (case_no). + * + * @ingroup aniso + * @author Ralf Hartmann, 2008 + */ template <> struct SubfacePossibilities<2> { - /** - * Possible cases of faces - * being subdivided into - * subface. - * - * In 2d there are following - * possibilities: a face (line) - * being refined * - * (case_x) or not - * refined - * (case_no). - */ - enum Possibilities - { - case_none = 0, - case_x = 1, - - case_isotropic = case_x - }; + /** + * Possible cases of faces + * being subdivided into + * subface. + * + * In 2d there are following + * possibilities: a face (line) + * being refined * + * (case_x) or not + * refined + * (case_no). + */ + enum Possibilities + { + case_none = 0, + case_x = 1, + + case_isotropic = case_x + }; }; -/** - * A class that provides all possible situations a face (in the - * current space dimension @p dim) might be subdivided into - * subfaces. - * - * This specialization is used for dim=3, where it offers following - * possibilities: a face (quad) being refined in x- or y-direction (in - * the face-intern coordinate system) separately, (case_x - * or (case_y), and in both directions - * (case_x which corresponds to - * (case_isotropic). Additionally, it offers the - * possibilities a face can have through repeated anisotropic - * refinement steps performed on one of the two neighboring cells. It - * might be possible for example, that a face (quad) is refined with - * cut_x and afterwards the left child is again refined - * with cut_y, so that there are three active - * subfaces. Note, however, that only refinement cases are allowed - * such that each line on a face between two hexes has not more than - * one hanging node. Furthermore, it is not allowed that two - * neighboring hexes are refined such that one of the hexes refines - * the common face with cut_x and the other hex refines - * that face with cut_y. In fact, - * Triangulation::prepare_coarsening_and_refinement takes care of this - * situation and ensures that each face of a refined cell is - * completely contained in a single face of neighboring cells. - * - * The following drawings explain the SubfacePossibilities and give - * the corresponding subface numbers: - * @code - - *-------* - | | - | 0 | case_none - | | - *-------* - - *---*---* - | | | - | 0 | 1 | case_x - | | | - *---*---* - - *---*---* - | 1 | | - *---* 2 | case_x1y - | 0 | | - *---*---* - - *---*---* - | | 2 | - | 0 *---* case_x2y - | | 1 | - *---*---* - - *---*---* - | 1 | 3 | - *---*---* case_x1y2y (successive refinement: first cut_x, then cut_y for both children) - | 0 | 2 | - *---*---* - - *-------* - | 1 | - *-------* case_y - | 0 | - *-------* - - *-------* - | 2 | - *---*---* case_y1x - | 0 | 1 | - *---*---* - - *---*---* - | 1 | 2 | - *---*---* case_y2x - | 0 | - *-------* - - *---*---* - | 2 | 3 | - *---*---* case_y1x2x (successive refinement: first cut_y, then cut_x for both children) - | 0 | 1 | - *---+---* - - *---*---* - | 2 | 3 | - *---*---* case_xy (one isotropic refinement step) - | 0 | 1 | - *---*---* - - * @endcode - * - * @ingroup aniso - * @author Tobias Leicht 2007, Ralf Hartmann, 2008 - */ + /** + * A class that provides all possible situations a face (in the + * current space dimension @p dim) might be subdivided into + * subfaces. + * + * This specialization is used for dim=3, where it offers following + * possibilities: a face (quad) being refined in x- or y-direction (in + * the face-intern coordinate system) separately, (case_x + * or (case_y), and in both directions + * (case_x which corresponds to + * (case_isotropic). Additionally, it offers the + * possibilities a face can have through repeated anisotropic + * refinement steps performed on one of the two neighboring cells. It + * might be possible for example, that a face (quad) is refined with + * cut_x and afterwards the left child is again refined + * with cut_y, so that there are three active + * subfaces. Note, however, that only refinement cases are allowed + * such that each line on a face between two hexes has not more than + * one hanging node. Furthermore, it is not allowed that two + * neighboring hexes are refined such that one of the hexes refines + * the common face with cut_x and the other hex refines + * that face with cut_y. In fact, + * Triangulation::prepare_coarsening_and_refinement takes care of this + * situation and ensures that each face of a refined cell is + * completely contained in a single face of neighboring cells. + * + * The following drawings explain the SubfacePossibilities and give + * the corresponding subface numbers: + * @code + + *-------* + | | + | 0 | case_none + | | + *-------* + + *---*---* + | | | + | 0 | 1 | case_x + | | | + *---*---* + + *---*---* + | 1 | | + *---* 2 | case_x1y + | 0 | | + *---*---* + + *---*---* + | | 2 | + | 0 *---* case_x2y + | | 1 | + *---*---* + + *---*---* + | 1 | 3 | + *---*---* case_x1y2y (successive refinement: first cut_x, then cut_y for both children) + | 0 | 2 | + *---*---* + + *-------* + | 1 | + *-------* case_y + | 0 | + *-------* + + *-------* + | 2 | + *---*---* case_y1x + | 0 | 1 | + *---*---* + + *---*---* + | 1 | 2 | + *---*---* case_y2x + | 0 | + *-------* + + *---*---* + | 2 | 3 | + *---*---* case_y1x2x (successive refinement: first cut_y, then cut_x for both children) + | 0 | 1 | + *---+---* + + *---*---* + | 2 | 3 | + *---*---* case_xy (one isotropic refinement step) + | 0 | 1 | + *---*---* + + * @endcode + * + * @ingroup aniso + * @author Tobias Leicht 2007, Ralf Hartmann, 2008 + */ template <> struct SubfacePossibilities<3> { - /** - * Possible cases of faces - * being subdivided into - * subface. - * - * See documentation to the - * SubfacePossibilities<3> for - * more details on the subface - * possibilities. - */ - enum Possibilities - { - case_none = 0, - case_x = 1, - case_x1y = 2, - case_x2y = 3, - case_x1y2y = 4, - case_y = 5, - case_y1x = 6, - case_y2x = 7, - case_y1x2x = 8, - case_xy = 9, - - case_isotropic = case_xy - }; + /** + * Possible cases of faces + * being subdivided into + * subface. + * + * See documentation to the + * SubfacePossibilities<3> for + * more details on the subface + * possibilities. + */ + enum Possibilities + { + case_none = 0, + case_x = 1, + case_x1y = 2, + case_x2y = 3, + case_x1y2y = 4, + case_y = 5, + case_y1x = 6, + case_y2x = 7, + case_y1x2x = 8, + case_xy = 9, + + case_isotropic = case_xy + }; }; -/** - * A class that provides all possible cases a face (in the - * current space dimension @p dim) might be subdivided into - * subfaces. - * - * @ingroup aniso - * @author Ralf Hartmann, 2008 - */ + /** + * A class that provides all possible cases a face (in the + * current space dimension @p dim) might be subdivided into + * subfaces. + * + * @ingroup aniso + * @author Ralf Hartmann, 2008 + */ template class SubfaceCase : public SubfacePossibilities { - public: - /** - * Constructor. Take and store - * a value indicating a - * particular subface - * possibility in the list of - * possible situations - * specified in the base class. - */ - SubfaceCase (const typename SubfacePossibilities::Possibilities subface_possibility); - - /** - * Return the numeric value - * stored by this class. While - * the presence of this operator - * might seem dangerous, it is - * useful in cases where one - * would like to have code like - * switch - * (subface_case)... case - * SubfaceCase::case_x: - * ... , which can be - * written as switch - * (static_cast@(subface_case). Another - * application is to use an - * object of the current type as - * an index into an array; - * however, this use is - * deprecated as it assumes a - * certain mapping from the - * symbolic flags defined in the - * SubfacePossibilities - * base class to actual numerical - * values (the array indices). - */ - operator unsigned char () const; - - /** - * Return the amount of memory - * occupied by an object of this - * type. - */ - static std::size_t memory_consumption (); - - /** - * Exception. - */ - DeclException1 (ExcInvalidSubfaceCase, - int, - << "The subface case given (" << arg1 << ") does not make sense " - << "for the space dimension of the object to which they are applied."); - - private: - /** - * Store the refinement case as a - * bit field with as many bits as - * are necessary in any given - * dimension. - */ - unsigned char value : (dim == 3 ? 4 : 1); + public: + /** + * Constructor. Take and store + * a value indicating a + * particular subface + * possibility in the list of + * possible situations + * specified in the base class. + */ + SubfaceCase (const typename SubfacePossibilities::Possibilities subface_possibility); + + /** + * Return the numeric value + * stored by this class. While + * the presence of this operator + * might seem dangerous, it is + * useful in cases where one + * would like to have code like + * switch + * (subface_case)... case + * SubfaceCase::case_x: + * ... , which can be + * written as switch + * (static_cast@(subface_case). Another + * application is to use an + * object of the current type as + * an index into an array; + * however, this use is + * deprecated as it assumes a + * certain mapping from the + * symbolic flags defined in the + * SubfacePossibilities + * base class to actual numerical + * values (the array indices). + */ + operator unsigned char () const; + + /** + * Return the amount of memory + * occupied by an object of this + * type. + */ + static std::size_t memory_consumption (); + + /** + * Exception. + */ + DeclException1 (ExcInvalidSubfaceCase, + int, + << "The subface case given (" << arg1 << ") does not make sense " + << "for the space dimension of the object to which they are applied."); + + private: + /** + * Store the refinement case as a + * bit field with as many bits as + * are necessary in any given + * dimension. + */ + unsigned char value : + (dim == 3 ? 4 : 1); }; } // namespace internal @@ -791,89 +793,89 @@ template <> struct GeometryInfo<0> { - /** - * Maximum number of children of - * a cell, i.e. the number of - * children of an isotropically - * refined cell. - * - * If a cell is refined - * anisotropically, the actual - * number of children may be less - * than the value given here. - */ - static const unsigned int max_children_per_cell = 1; - - /** - * Number of faces a cell has. - */ - static const unsigned int faces_per_cell = 0; - - /** - * Maximum number of children of - * a refined face, i.e. the - * number of children of an - * isotropically refined face. - * - * If a cell is refined - * anisotropically, the actual - * number of children may be less - * than the value given here. - */ - static const unsigned int max_children_per_face = 0; - - /** - * Return the number of children - * of a cell (or face) refined - * with ref_case. Since - * we are concerned here with - * points, the number of children - * is equal to one. - */ - static unsigned int n_children(const RefinementCase<0> &refinement_case); - - /** - * Number of vertices a cell has. - */ - static const unsigned int vertices_per_cell = 1; - - /** - * Number of vertices each face has. - * Since this is not useful in one - * dimension, we provide a useless - * number (in the hope that a compiler - * may warn when it sees constructs like - * for (i=0; i, - * at least if @p i is an unsigned int. - */ - static const unsigned int vertices_per_face = 0; - - /** - * Number of lines each face has. - */ - static const unsigned int lines_per_face = 0; - - /** - * Number of quads on each face. - */ - static const unsigned int quads_per_face = 0; - - /** - * Number of lines of a cell. - */ - static const unsigned int lines_per_cell = 0; - - /** - * Number of quadrilaterals of a - * cell. - */ - static const unsigned int quads_per_cell = 0; - - /** - * Number of hexahedra of a - * cell. - */ - static const unsigned int hexes_per_cell = 0; + /** + * Maximum number of children of + * a cell, i.e. the number of + * children of an isotropically + * refined cell. + * + * If a cell is refined + * anisotropically, the actual + * number of children may be less + * than the value given here. + */ + static const unsigned int max_children_per_cell = 1; + + /** + * Number of faces a cell has. + */ + static const unsigned int faces_per_cell = 0; + + /** + * Maximum number of children of + * a refined face, i.e. the + * number of children of an + * isotropically refined face. + * + * If a cell is refined + * anisotropically, the actual + * number of children may be less + * than the value given here. + */ + static const unsigned int max_children_per_face = 0; + + /** + * Return the number of children + * of a cell (or face) refined + * with ref_case. Since + * we are concerned here with + * points, the number of children + * is equal to one. + */ + static unsigned int n_children(const RefinementCase<0> &refinement_case); + + /** + * Number of vertices a cell has. + */ + static const unsigned int vertices_per_cell = 1; + + /** + * Number of vertices each face has. + * Since this is not useful in one + * dimension, we provide a useless + * number (in the hope that a compiler + * may warn when it sees constructs like + * for (i=0; i, + * at least if @p i is an unsigned int. + */ + static const unsigned int vertices_per_face = 0; + + /** + * Number of lines each face has. + */ + static const unsigned int lines_per_face = 0; + + /** + * Number of quads on each face. + */ + static const unsigned int quads_per_face = 0; + + /** + * Number of lines of a cell. + */ + static const unsigned int lines_per_cell = 0; + + /** + * Number of quadrilaterals of a + * cell. + */ + static const unsigned int quads_per_cell = 0; + + /** + * Number of hexahedra of a + * cell. + */ + static const unsigned int hexes_per_cell = 0; }; @@ -1415,834 +1417,834 @@ template struct GeometryInfo { - /** - * Maximum number of children of - * a refined cell, i.e. the - * number of children of an - * isotropically refined cell. - * - * If a cell is refined - * anisotropically, the actual - * number of children may be less - * than the value given here. - */ - static const unsigned int max_children_per_cell = 1 << dim; - - /** - * Number of faces of a cell. - */ - static const unsigned int faces_per_cell = 2 * dim; - - /** - * Maximum number of children of - * a refined face, i.e. the - * number of children of an - * isotropically refined face. - * - * If a cell is refined - * anisotropically, the actual - * number of children may be less - * than the value given here. - */ - static const unsigned int max_children_per_face = GeometryInfo::max_children_per_cell; - - /** - * Number of vertices of a cell. - */ - static const unsigned int vertices_per_cell = 1 << dim; - - /** - * Number of vertices on each - * face. - */ - static const unsigned int vertices_per_face = GeometryInfo::vertices_per_cell; - - /** - * Number of lines on each face. - */ - static const unsigned int lines_per_face + /** + * Maximum number of children of + * a refined cell, i.e. the + * number of children of an + * isotropically refined cell. + * + * If a cell is refined + * anisotropically, the actual + * number of children may be less + * than the value given here. + */ + static const unsigned int max_children_per_cell = 1 << dim; + + /** + * Number of faces of a cell. + */ + static const unsigned int faces_per_cell = 2 * dim; + + /** + * Maximum number of children of + * a refined face, i.e. the + * number of children of an + * isotropically refined face. + * + * If a cell is refined + * anisotropically, the actual + * number of children may be less + * than the value given here. + */ + static const unsigned int max_children_per_face = GeometryInfo::max_children_per_cell; + + /** + * Number of vertices of a cell. + */ + static const unsigned int vertices_per_cell = 1 << dim; + + /** + * Number of vertices on each + * face. + */ + static const unsigned int vertices_per_face = GeometryInfo::vertices_per_cell; + + /** + * Number of lines on each face. + */ + static const unsigned int lines_per_face = GeometryInfo::lines_per_cell; - /** - * Number of quads on each face. - */ - static const unsigned int quads_per_face + /** + * Number of quads on each face. + */ + static const unsigned int quads_per_face = GeometryInfo::quads_per_cell; - /** - * Number of lines of a cell. - * - * The formula to compute this makes use - * of the fact that when going from one - * dimension to the next, the object of - * the lower dimension is copied once - * (thus twice the old number of lines) - * and then a new line is inserted - * between each vertex of the old object - * and the corresponding one in the copy. - */ - static const unsigned int lines_per_cell + /** + * Number of lines of a cell. + * + * The formula to compute this makes use + * of the fact that when going from one + * dimension to the next, the object of + * the lower dimension is copied once + * (thus twice the old number of lines) + * and then a new line is inserted + * between each vertex of the old object + * and the corresponding one in the copy. + */ + static const unsigned int lines_per_cell = (2*GeometryInfo::lines_per_cell + GeometryInfo::vertices_per_cell); - /** - * Number of quadrilaterals of a - * cell. - * - * This number is computed recursively - * just as the previous one, with the - * exception that new quads result from - * connecting an original line and its - * copy. - */ - static const unsigned int quads_per_cell + /** + * Number of quadrilaterals of a + * cell. + * + * This number is computed recursively + * just as the previous one, with the + * exception that new quads result from + * connecting an original line and its + * copy. + */ + static const unsigned int quads_per_cell = (2*GeometryInfo::quads_per_cell + GeometryInfo::lines_per_cell); - /** - * Number of hexahedra of a - * cell. - */ - static const unsigned int hexes_per_cell + /** + * Number of hexahedra of a + * cell. + */ + static const unsigned int hexes_per_cell = (2*GeometryInfo::hexes_per_cell + GeometryInfo::quads_per_cell); - /** - * Rearrange vertices for UCD - * output. For a cell being - * written in UCD format, each - * entry in this field contains - * the number of a vertex in - * deal.II that corresponds - * to the UCD numbering at this - * location. - * - * Typical example: write a cell - * and arrange the vertices, such - * that UCD understands them. - * - * @code - * for (i=0; i< n_vertices; ++i) - * out << cell->vertex(ucd_to_deal[i]); - * @endcode - * - * As the vertex numbering in - * deal.II versions <= 5.1 - * happened to coincide with the - * UCD numbering, this field can - * also be used like a - * old_to_lexicographic mapping. - */ - static const unsigned int ucd_to_deal[vertices_per_cell]; - - /** - * Rearrange vertices for OpenDX - * output. For a cell being - * written in OpenDX format, each - * entry in this field contains - * the number of a vertex in - * deal.II that corresponds - * to the DX numbering at this - * location. - * - * Typical example: write a cell - * and arrange the vertices, such - * that OpenDX understands them. - * - * @code - * for (i=0; i< n_vertices; ++i) - * out << cell->vertex(dx_to_deal[i]); - * @endcode - */ - static const unsigned int dx_to_deal[vertices_per_cell]; - - /** - * This field stores for each vertex - * to which faces it belongs. In any - * given dimension, the number of - * faces is equal to the dimension. - * The first index in this 2D-array - * runs over all vertices, the second - * index over @p dim faces to which - * the vertex belongs. - * - * The order of the faces for - * each vertex is such that the - * first listed face bounds the - * reference cell in x - * direction, the second in - * y direction, and so on. - */ - static const unsigned int vertex_to_face[vertices_per_cell][dim]; - - /** - * Return the number of children - * of a cell (or face) refined - * with ref_case. - */ - static - unsigned int - n_children(const RefinementCase &refinement_case); - - /** - * Return the number of subfaces - * of a face refined according to - * internal::SubfaceCase - * @p face_ref_case. - */ - static - unsigned int - n_subfaces(const internal::SubfaceCase &subface_case); - - /** - * Given a face on the reference - * element with a - * internal::SubfaceCase@ - * @p face_refinement_case this - * function returns the ratio - * between the area of the @p - * subface_no th subface and the - * area(=1) of the face. - * - * E.g. for - * internal::SubfaceCase::cut_xy - * the ratio is 1/4 for each of - * the subfaces. - */ - static - double - subface_ratio(const internal::SubfaceCase &subface_case, - const unsigned int subface_no); - - /** - * Given a cell refined with the - * RefinementCase - * @p cell_refinement_case - * return the - * SubfaceCase of - * the @p face_no th face. - */ - static - RefinementCase - face_refinement_case (const RefinementCase &cell_refinement_case, - const unsigned int face_no, - const bool face_orientation = true, - const bool face_flip = false, - const bool face_rotation = false); - - /** - * Given the SubfaceCase @p - * face_refinement_case of the @p - * face_no th face, return the - * smallest RefinementCase of the - * cell, which corresponds to - * that refinement of the face. - */ - static - RefinementCase - min_cell_refinement_case_for_face_refinement - (const RefinementCase &face_refinement_case, - const unsigned int face_no, - const bool face_orientation = true, - const bool face_flip = false, - const bool face_rotation = false); - - /** - * Given a cell refined with the - * RefinementCase @p - * cell_refinement_case return - * the RefinementCase of the @p - * line_no th face. - */ - static - RefinementCase<1> - line_refinement_case(const RefinementCase &cell_refinement_case, - const unsigned int line_no); - - /** - * Return the minimal / smallest - * RefinementCase of the cell, which - * ensures refinement of line - * @p line_no. - */ - static - RefinementCase - min_cell_refinement_case_for_line_refinement(const unsigned int line_no); - - /** - * This field stores which child - * cells are adjacent to a - * certain face of the mother - * cell. - * - * For example, in 2D the layout of - * a cell is as follows: - * @verbatim - * . 3 - * . 2-->--3 - * . | | - * . 0 ^ ^ 1 - * . | | - * . 0-->--1 - * . 2 - * @endverbatim - * Vertices and faces are indicated - * with their numbers, faces also with - * their directions. - * - * Now, when refined, the layout is - * like this: - * @verbatim - * *--*--* - * | 2|3 | - * *--*--* - * | 0|1 | - * *--*--* - * @endverbatim - * - * Thus, the child cells on face - * 0 are (ordered in the - * direction of the face) 0 and - * 2, on face 3 they are 2 and 3, - * etc. - * - * For three spatial dimensions, the exact - * order of the children is laid down in - * the general documentation of this - * class. - * - * Through the face_orientation, - * face_flip and - * face_rotation arguments this - * function handles faces oriented in the - * standard and non-standard orientation. - * face_orientation defaults to - * true, face_flip and - * face_rotation default to - * false (standard orientation) - * and has no effect in 2d. The concept of - * face orientations is explained in this - * @ref GlossFaceOrientation "glossary" - * entry. - * - * In the case of anisotropically refined - * cells and faces, the @p RefinementCase of - * the face, face_ref_case, - * might have an influence on - * which child is behind which given - * subface, thus this is an additional - * argument, defaulting to isotropic - * refinement of the face. - */ - static - unsigned int - child_cell_on_face (const RefinementCase &ref_case, - const unsigned int face, - const unsigned int subface, - const bool face_orientation = true, - const bool face_flip = false, - const bool face_rotation = false, - const RefinementCase &face_refinement_case - = RefinementCase::isotropic_refinement); - - /** - * Map line vertex number to cell - * vertex number, i.e. give the - * cell vertex number of the - * vertexth vertex of - * line line, e.g. - * GeometryInfo<2>::line_to_cell_vertices(3,0)=2. - * - * The order of the lines, as well as - * their direction (which in turn - * determines which is the first and - * which the second vertex on a line) is - * the canonical one in deal.II, as - * described in the general documentation - * of this class. - * - * For dim=2 this call - * is simply passed down to the - * face_to_cell_vertices() - * function. - */ - static - unsigned int - line_to_cell_vertices (const unsigned int line, - const unsigned int vertex); - - /** - * Map face vertex number to cell - * vertex number, i.e. give the - * cell vertex number of the - * vertexth vertex of - * face face, e.g. - * GeometryInfo<2>::face_to_cell_vertices(3,0)=2, - * see the image under point N4 - * in the 2d section of this - * class's documentation. - * - * Through the face_orientation, - * face_flip and - * face_rotation arguments this - * function handles faces oriented in the - * standard and non-standard orientation. - * face_orientation defaults to - * true, face_flip and - * face_rotation default to - * false (standard orientation) - * and has no effect in 2d. - * - * As the children of a cell are - * ordered according to the - * vertices of the cell, this - * call is passed down to the - * child_cell_on_face() function. - * Hence this function is simply - * a wrapper of - * child_cell_on_face() giving it - * a suggestive name. - */ - static - unsigned int - face_to_cell_vertices (const unsigned int face, - const unsigned int vertex, - const bool face_orientation = true, - const bool face_flip = false, - const bool face_rotation = false); - - /** - * Map face line number to cell - * line number, i.e. give the - * cell line number of the - * lineth line of face - * face, e.g. - * GeometryInfo<3>::face_to_cell_lines(5,0)=4. - * - * Through the face_orientation, - * face_flip and - * face_rotation arguments this - * function handles faces oriented in the - * standard and non-standard orientation. - * face_orientation defaults to - * true, face_flip and - * face_rotation default to - * false (standard orientation) - * and has no effect in 2d. - */ - static - unsigned int - face_to_cell_lines (const unsigned int face, - const unsigned int line, + /** + * Rearrange vertices for UCD + * output. For a cell being + * written in UCD format, each + * entry in this field contains + * the number of a vertex in + * deal.II that corresponds + * to the UCD numbering at this + * location. + * + * Typical example: write a cell + * and arrange the vertices, such + * that UCD understands them. + * + * @code + * for (i=0; i< n_vertices; ++i) + * out << cell->vertex(ucd_to_deal[i]); + * @endcode + * + * As the vertex numbering in + * deal.II versions <= 5.1 + * happened to coincide with the + * UCD numbering, this field can + * also be used like a + * old_to_lexicographic mapping. + */ + static const unsigned int ucd_to_deal[vertices_per_cell]; + + /** + * Rearrange vertices for OpenDX + * output. For a cell being + * written in OpenDX format, each + * entry in this field contains + * the number of a vertex in + * deal.II that corresponds + * to the DX numbering at this + * location. + * + * Typical example: write a cell + * and arrange the vertices, such + * that OpenDX understands them. + * + * @code + * for (i=0; i< n_vertices; ++i) + * out << cell->vertex(dx_to_deal[i]); + * @endcode + */ + static const unsigned int dx_to_deal[vertices_per_cell]; + + /** + * This field stores for each vertex + * to which faces it belongs. In any + * given dimension, the number of + * faces is equal to the dimension. + * The first index in this 2D-array + * runs over all vertices, the second + * index over @p dim faces to which + * the vertex belongs. + * + * The order of the faces for + * each vertex is such that the + * first listed face bounds the + * reference cell in x + * direction, the second in + * y direction, and so on. + */ + static const unsigned int vertex_to_face[vertices_per_cell][dim]; + + /** + * Return the number of children + * of a cell (or face) refined + * with ref_case. + */ + static + unsigned int + n_children(const RefinementCase &refinement_case); + + /** + * Return the number of subfaces + * of a face refined according to + * internal::SubfaceCase + * @p face_ref_case. + */ + static + unsigned int + n_subfaces(const internal::SubfaceCase &subface_case); + + /** + * Given a face on the reference + * element with a + * internal::SubfaceCase@ + * @p face_refinement_case this + * function returns the ratio + * between the area of the @p + * subface_no th subface and the + * area(=1) of the face. + * + * E.g. for + * internal::SubfaceCase::cut_xy + * the ratio is 1/4 for each of + * the subfaces. + */ + static + double + subface_ratio(const internal::SubfaceCase &subface_case, + const unsigned int subface_no); + + /** + * Given a cell refined with the + * RefinementCase + * @p cell_refinement_case + * return the + * SubfaceCase of + * the @p face_no th face. + */ + static + RefinementCase + face_refinement_case (const RefinementCase &cell_refinement_case, + const unsigned int face_no, const bool face_orientation = true, const bool face_flip = false, const bool face_rotation = false); - /** - * Map the vertex index @p vertex of a face - * in standard orientation to one of a face - * with arbitrary @p face_orientation, @p - * face_flip and @p face_rotation. The - * values of these three flags default to - * true, false and - * false, respectively. this - * combination describes a face in standard - * orientation. - * - * This function is only implemented in 3D. - */ - static - unsigned int - standard_to_real_face_vertex (const unsigned int vertex, - const bool face_orientation = true, - const bool face_flip = false, - const bool face_rotation = false); - - /** - * Map the vertex index @p vertex of a face - * with arbitrary @p face_orientation, @p - * face_flip and @p face_rotation to a face - * in standard orientation. The values of - * these three flags default to - * true, false and - * false, respectively. this - * combination describes a face in standard - * orientation. - * - * This function is only implemented in 3D. - */ - static - unsigned int - real_to_standard_face_vertex (const unsigned int vertex, - const bool face_orientation = true, - const bool face_flip = false, - const bool face_rotation = false); - - /** - * Map the line index @p line of a face - * in standard orientation to one of a face - * with arbitrary @p face_orientation, @p - * face_flip and @p face_rotation. The - * values of these three flags default to - * true, false and - * false, respectively. this - * combination describes a face in standard - * orientation. - * - * This function is only implemented in 3D. - */ - static - unsigned int - standard_to_real_face_line (const unsigned int line, + /** + * Given the SubfaceCase @p + * face_refinement_case of the @p + * face_no th face, return the + * smallest RefinementCase of the + * cell, which corresponds to + * that refinement of the face. + */ + static + RefinementCase + min_cell_refinement_case_for_face_refinement + (const RefinementCase &face_refinement_case, + const unsigned int face_no, + const bool face_orientation = true, + const bool face_flip = false, + const bool face_rotation = false); + + /** + * Given a cell refined with the + * RefinementCase @p + * cell_refinement_case return + * the RefinementCase of the @p + * line_no th face. + */ + static + RefinementCase<1> + line_refinement_case(const RefinementCase &cell_refinement_case, + const unsigned int line_no); + + /** + * Return the minimal / smallest + * RefinementCase of the cell, which + * ensures refinement of line + * @p line_no. + */ + static + RefinementCase + min_cell_refinement_case_for_line_refinement(const unsigned int line_no); + + /** + * This field stores which child + * cells are adjacent to a + * certain face of the mother + * cell. + * + * For example, in 2D the layout of + * a cell is as follows: + * @verbatim + * . 3 + * . 2-->--3 + * . | | + * . 0 ^ ^ 1 + * . | | + * . 0-->--1 + * . 2 + * @endverbatim + * Vertices and faces are indicated + * with their numbers, faces also with + * their directions. + * + * Now, when refined, the layout is + * like this: + * @verbatim + * *--*--* + * | 2|3 | + * *--*--* + * | 0|1 | + * *--*--* + * @endverbatim + * + * Thus, the child cells on face + * 0 are (ordered in the + * direction of the face) 0 and + * 2, on face 3 they are 2 and 3, + * etc. + * + * For three spatial dimensions, the exact + * order of the children is laid down in + * the general documentation of this + * class. + * + * Through the face_orientation, + * face_flip and + * face_rotation arguments this + * function handles faces oriented in the + * standard and non-standard orientation. + * face_orientation defaults to + * true, face_flip and + * face_rotation default to + * false (standard orientation) + * and has no effect in 2d. The concept of + * face orientations is explained in this + * @ref GlossFaceOrientation "glossary" + * entry. + * + * In the case of anisotropically refined + * cells and faces, the @p RefinementCase of + * the face, face_ref_case, + * might have an influence on + * which child is behind which given + * subface, thus this is an additional + * argument, defaulting to isotropic + * refinement of the face. + */ + static + unsigned int + child_cell_on_face (const RefinementCase &ref_case, + const unsigned int face, + const unsigned int subface, + const bool face_orientation = true, + const bool face_flip = false, + const bool face_rotation = false, + const RefinementCase &face_refinement_case + = RefinementCase::isotropic_refinement); + + /** + * Map line vertex number to cell + * vertex number, i.e. give the + * cell vertex number of the + * vertexth vertex of + * line line, e.g. + * GeometryInfo<2>::line_to_cell_vertices(3,0)=2. + * + * The order of the lines, as well as + * their direction (which in turn + * determines which is the first and + * which the second vertex on a line) is + * the canonical one in deal.II, as + * described in the general documentation + * of this class. + * + * For dim=2 this call + * is simply passed down to the + * face_to_cell_vertices() + * function. + */ + static + unsigned int + line_to_cell_vertices (const unsigned int line, + const unsigned int vertex); + + /** + * Map face vertex number to cell + * vertex number, i.e. give the + * cell vertex number of the + * vertexth vertex of + * face face, e.g. + * GeometryInfo<2>::face_to_cell_vertices(3,0)=2, + * see the image under point N4 + * in the 2d section of this + * class's documentation. + * + * Through the face_orientation, + * face_flip and + * face_rotation arguments this + * function handles faces oriented in the + * standard and non-standard orientation. + * face_orientation defaults to + * true, face_flip and + * face_rotation default to + * false (standard orientation) + * and has no effect in 2d. + * + * As the children of a cell are + * ordered according to the + * vertices of the cell, this + * call is passed down to the + * child_cell_on_face() function. + * Hence this function is simply + * a wrapper of + * child_cell_on_face() giving it + * a suggestive name. + */ + static + unsigned int + face_to_cell_vertices (const unsigned int face, + const unsigned int vertex, + const bool face_orientation = true, + const bool face_flip = false, + const bool face_rotation = false); + + /** + * Map face line number to cell + * line number, i.e. give the + * cell line number of the + * lineth line of face + * face, e.g. + * GeometryInfo<3>::face_to_cell_lines(5,0)=4. + * + * Through the face_orientation, + * face_flip and + * face_rotation arguments this + * function handles faces oriented in the + * standard and non-standard orientation. + * face_orientation defaults to + * true, face_flip and + * face_rotation default to + * false (standard orientation) + * and has no effect in 2d. + */ + static + unsigned int + face_to_cell_lines (const unsigned int face, + const unsigned int line, + const bool face_orientation = true, + const bool face_flip = false, + const bool face_rotation = false); + + /** + * Map the vertex index @p vertex of a face + * in standard orientation to one of a face + * with arbitrary @p face_orientation, @p + * face_flip and @p face_rotation. The + * values of these three flags default to + * true, false and + * false, respectively. this + * combination describes a face in standard + * orientation. + * + * This function is only implemented in 3D. + */ + static + unsigned int + standard_to_real_face_vertex (const unsigned int vertex, const bool face_orientation = true, const bool face_flip = false, const bool face_rotation = false); - /** - * Map the line index @p line of a face - * with arbitrary @p face_orientation, @p - * face_flip and @p face_rotation to a face - * in standard orientation. The values of - * these three flags default to - * true, false and - * false, respectively. this - * combination describes a face in standard - * orientation. - * - * This function is only implemented in 3D. - */ - static - unsigned int - real_to_standard_face_line (const unsigned int line, + /** + * Map the vertex index @p vertex of a face + * with arbitrary @p face_orientation, @p + * face_flip and @p face_rotation to a face + * in standard orientation. The values of + * these three flags default to + * true, false and + * false, respectively. this + * combination describes a face in standard + * orientation. + * + * This function is only implemented in 3D. + */ + static + unsigned int + real_to_standard_face_vertex (const unsigned int vertex, const bool face_orientation = true, const bool face_flip = false, const bool face_rotation = false); - /** - * Return the position of the @p ith - * vertex on the unit cell. The order of - * vertices is the canonical one in - * deal.II, as described in the general - * documentation of this class. - */ - static - Point - unit_cell_vertex (const unsigned int vertex); - - /** - * Given a point @p p in unit - * coordinates, return the number - * of the child cell in which it - * would lie in. If the point - * lies on the interface of two - * children, return any one of - * their indices. The result is - * always less than - * GeometryInfo::max_children_per_cell. - * - * The order of child cells is described - * the general documentation of this - * class. - */ - static - unsigned int - child_cell_from_point (const Point &p); - - /** - * Given coordinates @p p on the - * unit cell, return the values - * of the coordinates of this - * point in the coordinate system - * of the given child. Neither - * original nor returned - * coordinates need actually be - * inside the cell, we simply - * perform a scale-and-shift - * operation with a shift that - * depends on the number of the - * child. - */ - static - Point - cell_to_child_coordinates (const Point &p, - const unsigned int child_index, - const RefinementCase refine_case - = RefinementCase::isotropic_refinement); - - /** - * The reverse function to the - * one above: take a point in the - * coordinate system of the - * child, and transform it to the - * coordinate system of the - * mother cell. - */ - static - Point - child_to_cell_coordinates (const Point &p, - const unsigned int child_index, - const RefinementCase refine_case - = RefinementCase::isotropic_refinement); - - /** - * Return true if the given point - * is inside the unit cell of the - * present space dimension. - */ - static - bool - is_inside_unit_cell (const Point &p); - - /** - * Return true if the given point - * is inside the unit cell of the - * present space dimension. This - * * function accepts an - * additional * parameter which - * specifies how * much the point - * position may * actually be - * outside the true * unit - * cell. This is useful because - * in practice we may often not - * be able to compute the - * coordinates of a point in - * reference coordinates exactly, - * but only up to numerical - * roundoff. - * - * The tolerance parameter may be - * less than zero, indicating - * that the point should be - * safely inside the cell. - */ - static - bool - is_inside_unit_cell (const Point &p, - const double eps); - - /** - * Projects a given point onto the - * unit cell, i.e. each coordinate - * outside [0..1] is modified - * to lie within that interval. - */ - static - Point - project_to_unit_cell (const Point &p); - - /** - * Returns the infinity norm of - * the vector between a given point @p p - * outside the unit cell to the closest - * unit cell boundary. - * For points inside the cell, this is - * defined as zero. - */ - static - double - distance_to_unit_cell (const Point &p); - - /** - * Compute the value of the $i$-th - * $d$-linear (i.e. (bi-,tri-)linear) - * shape function at location $\xi$. - */ - static - double - d_linear_shape_function (const Point &xi, - const unsigned int i); - - /** - * Compute the gradient of the $i$-th - * $d$-linear (i.e. (bi-,tri-)linear) - * shape function at location $\xi$. - */ - static - Tensor<1,dim> - d_linear_shape_function_gradient (const Point &xi, - const unsigned int i); - - /** - * For a (bi-, tri-)linear - * mapping from the reference - * cell, face, or edge to the - * object specified by the given - * vertices, compute the - * alternating form of the - * transformed unit vectors - * vertices. For an object of - * dimensionality @p dim, there - * are @p dim vectors with @p - * spacedim components each, and - * the alternating form is a - * tensor of rank spacedim-dim - * that corresponds to the wedge - * product of the @p dim unit - * vectors, and it corresponds to - * the volume and normal vectors - * of the mapping from reference - * element to the element - * described by the vertices. - * - * For example, if dim==spacedim==2, then - * the alternating form is a scalar - * (because spacedim-dim=0) and its value - * equals $\mathbf v_1\wedge \mathbf - * v_2=\mathbf v_1^\perp \cdot\mathbf - * v_2$, where $\mathbf v_1^\perp$ is a - * vector that is rotated to the right by - * 90 degrees from $\mathbf v_1$. If - * dim==spacedim==3, then the result is - * again a scalar with value $\mathbf - * v_1\wedge \mathbf v_2 \wedge \mathbf - * v_3 = (\mathbf v_1\times \mathbf - * v_2)\cdot \mathbf v_3$, where $\mathbf - * v_1, \mathbf v_2, \mathbf v_3$ are the - * images of the unit vectors at a vertex - * of the unit dim-dimensional cell under - * transformation to the dim-dimensional - * cell in spacedim-dimensional space. In - * both cases, i.e. for dim==2 or 3, the - * result happens to equal the - * determinant of the Jacobian of the - * mapping from reference cell to cell in - * real space. Note that it is the actual - * determinant, not its absolute value as - * often used in transforming integrals - * from one coordinate system to - * another. In particular, if the object - * specified by the vertices is a - * parallelogram (i.e. a linear - * transformation of the reference cell) - * then the computed values are the same - * at all vertices and equal the (signed) - * area of the cell; similarly, for - * parallel-epipeds, it is the volume of - * the cell. - * - * Likewise, if we have dim==spacedim-1 - * (e.g. we have a quad in 3d space, or a - * line in 2d), then the alternating - * product denotes the normal vector - * (i.e. a rank-1 tensor, since - * spacedim-dim=1) to the object at each - * vertex, where the normal vector's - * magnitude denotes the area element of - * the transformation from the reference - * object to the object given by the - * vertices. In particular, if again the - * mapping from reference object to the - * object under consideration here is - * linear (not bi- or trilinear), then - * the returned vectors are all - * %parallel, perpendicular to the mapped - * object described by the vertices, and - * have a magnitude equal to the - * area/volume of the mapped object. If - * dim=1, spacedim=2, then the returned - * value is $\mathbf v_1^\perp$, where - * $\mathbf v_1$ is the image of the sole - * unit vector of a line mapped to the - * line in 2d given by the vertices; if - * dim=2, spacedim=3, then the returned - * values are $\mathbf v_1 \wedge \mathbf - * v_2=\mathbf v_1 \times \mathbf v_2$ - * where $\mathbf v_1,\mathbf v_2$ are - * the two three-dimensional vectors that - * are tangential to the quad mapped into - * three-dimensional space. - * - * This function is used in order to - * determine how distorted a cell is (see - * the entry on - * @ref GlossDistorted "distorted cells" - * in the glossary). - */ - template - static - void - alternating_form_at_vertices - (const Point (&vertices)[vertices_per_cell], - Tensor (&forms)[vertices_per_cell]) - ; - - /** - * For each face of the reference - * cell, this field stores the - * coordinate direction in which - * its normal vector points. In - * dim dimension these - * are the 2*dim first - * entries of - * {0,0,1,1,2,2,3,3}. - * - * Note that this is only the - * coordinate number. The actual - * direction of the normal vector - * is obtained by multiplying the - * unit vector in this direction - * with #unit_normal_orientation. - */ - static const unsigned int unit_normal_direction[faces_per_cell]; - - /** - * Orientation of the unit normal - * vector of a face of the - * reference cell. In - * dim dimension these - * are the 2*dim first - * entries of - * {-1,1,-1,1,-1,1,-1,1}. - * - * Each value is either - * 1 or -1, - * corresponding to a normal - * vector pointing in the - * positive or negative - * coordinate direction, - * respectively. - * - * Note that this is only the - * standard orientation - * of faces. At least in 3d, - * actual faces of cells in a - * triangulation can also have - * the opposite orientation, - * depending on a flag that one - * can query from the cell it - * belongs to. For more - * information, see the - * @ref GlossFaceOrientation "glossary" - * entry on - * face orientation. - */ - static const int unit_normal_orientation[faces_per_cell]; - - /** - * List of numbers which denotes which - * face is opposite to a given face. Its - * entries are the first 2*dim - * entries of - * { 1, 0, 3, 2, 5, 4, 7, 6}. - */ - static const unsigned int opposite_face[faces_per_cell]; - - - /** - * Exception - */ - DeclException1 (ExcInvalidCoordinate, - double, - << "The coordinates must satisfy 0 <= x_i <= 1, " - << "but here we have x_i=" << arg1); - - /** - * Exception - */ - DeclException3 (ExcInvalidSubface, - int, int, int, - << "RefinementCase " << arg1 << ": face " << arg2 - << " has no subface " << arg3); + /** + * Map the line index @p line of a face + * in standard orientation to one of a face + * with arbitrary @p face_orientation, @p + * face_flip and @p face_rotation. The + * values of these three flags default to + * true, false and + * false, respectively. this + * combination describes a face in standard + * orientation. + * + * This function is only implemented in 3D. + */ + static + unsigned int + standard_to_real_face_line (const unsigned int line, + const bool face_orientation = true, + const bool face_flip = false, + const bool face_rotation = false); + + /** + * Map the line index @p line of a face + * with arbitrary @p face_orientation, @p + * face_flip and @p face_rotation to a face + * in standard orientation. The values of + * these three flags default to + * true, false and + * false, respectively. this + * combination describes a face in standard + * orientation. + * + * This function is only implemented in 3D. + */ + static + unsigned int + real_to_standard_face_line (const unsigned int line, + const bool face_orientation = true, + const bool face_flip = false, + const bool face_rotation = false); + + /** + * Return the position of the @p ith + * vertex on the unit cell. The order of + * vertices is the canonical one in + * deal.II, as described in the general + * documentation of this class. + */ + static + Point + unit_cell_vertex (const unsigned int vertex); + + /** + * Given a point @p p in unit + * coordinates, return the number + * of the child cell in which it + * would lie in. If the point + * lies on the interface of two + * children, return any one of + * their indices. The result is + * always less than + * GeometryInfo::max_children_per_cell. + * + * The order of child cells is described + * the general documentation of this + * class. + */ + static + unsigned int + child_cell_from_point (const Point &p); + + /** + * Given coordinates @p p on the + * unit cell, return the values + * of the coordinates of this + * point in the coordinate system + * of the given child. Neither + * original nor returned + * coordinates need actually be + * inside the cell, we simply + * perform a scale-and-shift + * operation with a shift that + * depends on the number of the + * child. + */ + static + Point + cell_to_child_coordinates (const Point &p, + const unsigned int child_index, + const RefinementCase refine_case + = RefinementCase::isotropic_refinement); + + /** + * The reverse function to the + * one above: take a point in the + * coordinate system of the + * child, and transform it to the + * coordinate system of the + * mother cell. + */ + static + Point + child_to_cell_coordinates (const Point &p, + const unsigned int child_index, + const RefinementCase refine_case + = RefinementCase::isotropic_refinement); + + /** + * Return true if the given point + * is inside the unit cell of the + * present space dimension. + */ + static + bool + is_inside_unit_cell (const Point &p); + + /** + * Return true if the given point + * is inside the unit cell of the + * present space dimension. This + * * function accepts an + * additional * parameter which + * specifies how * much the point + * position may * actually be + * outside the true * unit + * cell. This is useful because + * in practice we may often not + * be able to compute the + * coordinates of a point in + * reference coordinates exactly, + * but only up to numerical + * roundoff. + * + * The tolerance parameter may be + * less than zero, indicating + * that the point should be + * safely inside the cell. + */ + static + bool + is_inside_unit_cell (const Point &p, + const double eps); + + /** + * Projects a given point onto the + * unit cell, i.e. each coordinate + * outside [0..1] is modified + * to lie within that interval. + */ + static + Point + project_to_unit_cell (const Point &p); + + /** + * Returns the infinity norm of + * the vector between a given point @p p + * outside the unit cell to the closest + * unit cell boundary. + * For points inside the cell, this is + * defined as zero. + */ + static + double + distance_to_unit_cell (const Point &p); + + /** + * Compute the value of the $i$-th + * $d$-linear (i.e. (bi-,tri-)linear) + * shape function at location $\xi$. + */ + static + double + d_linear_shape_function (const Point &xi, + const unsigned int i); + + /** + * Compute the gradient of the $i$-th + * $d$-linear (i.e. (bi-,tri-)linear) + * shape function at location $\xi$. + */ + static + Tensor<1,dim> + d_linear_shape_function_gradient (const Point &xi, + const unsigned int i); + + /** + * For a (bi-, tri-)linear + * mapping from the reference + * cell, face, or edge to the + * object specified by the given + * vertices, compute the + * alternating form of the + * transformed unit vectors + * vertices. For an object of + * dimensionality @p dim, there + * are @p dim vectors with @p + * spacedim components each, and + * the alternating form is a + * tensor of rank spacedim-dim + * that corresponds to the wedge + * product of the @p dim unit + * vectors, and it corresponds to + * the volume and normal vectors + * of the mapping from reference + * element to the element + * described by the vertices. + * + * For example, if dim==spacedim==2, then + * the alternating form is a scalar + * (because spacedim-dim=0) and its value + * equals $\mathbf v_1\wedge \mathbf + * v_2=\mathbf v_1^\perp \cdot\mathbf + * v_2$, where $\mathbf v_1^\perp$ is a + * vector that is rotated to the right by + * 90 degrees from $\mathbf v_1$. If + * dim==spacedim==3, then the result is + * again a scalar with value $\mathbf + * v_1\wedge \mathbf v_2 \wedge \mathbf + * v_3 = (\mathbf v_1\times \mathbf + * v_2)\cdot \mathbf v_3$, where $\mathbf + * v_1, \mathbf v_2, \mathbf v_3$ are the + * images of the unit vectors at a vertex + * of the unit dim-dimensional cell under + * transformation to the dim-dimensional + * cell in spacedim-dimensional space. In + * both cases, i.e. for dim==2 or 3, the + * result happens to equal the + * determinant of the Jacobian of the + * mapping from reference cell to cell in + * real space. Note that it is the actual + * determinant, not its absolute value as + * often used in transforming integrals + * from one coordinate system to + * another. In particular, if the object + * specified by the vertices is a + * parallelogram (i.e. a linear + * transformation of the reference cell) + * then the computed values are the same + * at all vertices and equal the (signed) + * area of the cell; similarly, for + * parallel-epipeds, it is the volume of + * the cell. + * + * Likewise, if we have dim==spacedim-1 + * (e.g. we have a quad in 3d space, or a + * line in 2d), then the alternating + * product denotes the normal vector + * (i.e. a rank-1 tensor, since + * spacedim-dim=1) to the object at each + * vertex, where the normal vector's + * magnitude denotes the area element of + * the transformation from the reference + * object to the object given by the + * vertices. In particular, if again the + * mapping from reference object to the + * object under consideration here is + * linear (not bi- or trilinear), then + * the returned vectors are all + * %parallel, perpendicular to the mapped + * object described by the vertices, and + * have a magnitude equal to the + * area/volume of the mapped object. If + * dim=1, spacedim=2, then the returned + * value is $\mathbf v_1^\perp$, where + * $\mathbf v_1$ is the image of the sole + * unit vector of a line mapped to the + * line in 2d given by the vertices; if + * dim=2, spacedim=3, then the returned + * values are $\mathbf v_1 \wedge \mathbf + * v_2=\mathbf v_1 \times \mathbf v_2$ + * where $\mathbf v_1,\mathbf v_2$ are + * the two three-dimensional vectors that + * are tangential to the quad mapped into + * three-dimensional space. + * + * This function is used in order to + * determine how distorted a cell is (see + * the entry on + * @ref GlossDistorted "distorted cells" + * in the glossary). + */ + template + static + void + alternating_form_at_vertices + (const Point (&vertices)[vertices_per_cell], + Tensor (&forms)[vertices_per_cell]) + ; + + /** + * For each face of the reference + * cell, this field stores the + * coordinate direction in which + * its normal vector points. In + * dim dimension these + * are the 2*dim first + * entries of + * {0,0,1,1,2,2,3,3}. + * + * Note that this is only the + * coordinate number. The actual + * direction of the normal vector + * is obtained by multiplying the + * unit vector in this direction + * with #unit_normal_orientation. + */ + static const unsigned int unit_normal_direction[faces_per_cell]; + + /** + * Orientation of the unit normal + * vector of a face of the + * reference cell. In + * dim dimension these + * are the 2*dim first + * entries of + * {-1,1,-1,1,-1,1,-1,1}. + * + * Each value is either + * 1 or -1, + * corresponding to a normal + * vector pointing in the + * positive or negative + * coordinate direction, + * respectively. + * + * Note that this is only the + * standard orientation + * of faces. At least in 3d, + * actual faces of cells in a + * triangulation can also have + * the opposite orientation, + * depending on a flag that one + * can query from the cell it + * belongs to. For more + * information, see the + * @ref GlossFaceOrientation "glossary" + * entry on + * face orientation. + */ + static const int unit_normal_orientation[faces_per_cell]; + + /** + * List of numbers which denotes which + * face is opposite to a given face. Its + * entries are the first 2*dim + * entries of + * { 1, 0, 3, 2, 5, 4, 7, 6}. + */ + static const unsigned int opposite_face[faces_per_cell]; + + + /** + * Exception + */ + DeclException1 (ExcInvalidCoordinate, + double, + << "The coordinates must satisfy 0 <= x_i <= 1, " + << "but here we have x_i=" << arg1); + + /** + * Exception + */ + DeclException3 (ExcInvalidSubface, + int, int, int, + << "RefinementCase " << arg1 << ": face " << arg2 + << " has no subface " << arg3); }; @@ -2310,8 +2312,8 @@ namespace internal template inline SubfaceCase::SubfaceCase (const typename SubfacePossibilities::Possibilities subface_possibility) - : - value (subface_possibility) + : + value (subface_possibility) {} @@ -2381,8 +2383,8 @@ RefinementCase<3>::cut_axis (const unsigned int i) template inline RefinementCase::RefinementCase () - : - value (RefinementPossibilities::no_refinement) + : + value (RefinementPossibilities::no_refinement) {} @@ -2391,13 +2393,13 @@ template inline RefinementCase:: RefinementCase (const typename RefinementPossibilities::Possibilities refinement_case) - : - value (refinement_case) + : + value (refinement_case) { - // check that only those bits of - // the given argument are set that - // make sense for a given space - // dimension + // check that only those bits of + // the given argument are set that + // make sense for a given space + // dimension Assert ((refinement_case & RefinementPossibilities::isotropic_refinement) == refinement_case, ExcInvalidRefinementCase (refinement_case)); @@ -2408,13 +2410,13 @@ RefinementCase (const typename RefinementPossibilities::Possibilities refin template inline RefinementCase::RefinementCase (const unsigned char refinement_case) - : - value (refinement_case) + : + value (refinement_case) { - // check that only those bits of - // the given argument are set that - // make sense for a given space - // dimension + // check that only those bits of + // the given argument are set that + // make sense for a given space + // dimension Assert ((refinement_case & RefinementPossibilities::isotropic_refinement) == refinement_case, ExcInvalidRefinementCase (refinement_case)); @@ -2481,7 +2483,7 @@ void RefinementCase::serialize (Archive &ar, // serialization can't deal with bitfields, so copy from/to a full sized // unsigned char unsigned char uchar_value = value; - ar & uchar_value; + ar &uchar_value; value = uchar_value; } @@ -2561,7 +2563,7 @@ GeometryInfo<2>::child_cell_from_point (const Point<2> &p) return (p[0] <= 0.5 ? (p[1] <= 0.5 ? 0 : 2) : - (p[1] <= 0.5 ? 1 : 3)); + (p[1] <= 0.5 ? 1 : 3)); } @@ -2578,10 +2580,10 @@ GeometryInfo<3>::child_cell_from_point (const Point<3> &p) return (p[0] <= 0.5 ? (p[1] <= 0.5 ? (p[2] <= 0.5 ? 0 : 4) : - (p[2] <= 0.5 ? 2 : 6)) : - (p[1] <= 0.5 ? - (p[2] <= 0.5 ? 1 : 5) : - (p[2] <= 0.5 ? 3 : 7))); + (p[2] <= 0.5 ? 2 : 6)) : + (p[1] <= 0.5 ? + (p[2] <= 0.5 ? 1 : 5) : + (p[2] <= 0.5 ? 3 : 7))); } @@ -2630,22 +2632,22 @@ GeometryInfo<2>::cell_to_child_coordinates (const Point<2> &p, Point<2> point=p; switch (refine_case) { - case RefinementCase<2>::cut_x: - point[0]*=2.0; - if (child_index==1) - point[0]-=1.0; - break; - case RefinementCase<2>::cut_y: - point[1]*=2.0; - if (child_index==1) - point[1]-=1.0; - break; - case RefinementCase<2>::cut_xy: - point*=2.0; - point-=unit_cell_vertex(child_index); - break; - default: - Assert(false, ExcInternalError()); + case RefinementCase<2>::cut_x: + point[0]*=2.0; + if (child_index==1) + point[0]-=1.0; + break; + case RefinementCase<2>::cut_y: + point[1]*=2.0; + if (child_index==1) + point[1]-=1.0; + break; + case RefinementCase<2>::cut_xy: + point*=2.0; + point-=unit_cell_vertex(child_index); + break; + default: + Assert(false, ExcInternalError()); } return point; @@ -2665,62 +2667,62 @@ GeometryInfo<3>::cell_to_child_coordinates (const Point<3> &p, ExcIndexRange (child_index, 0, GeometryInfo<3>::n_children(refine_case))); Point<3> point=p; - // there might be a cleverer way to do - // this, but since this function is called - // in very few places for initialization - // purposes only, I don't care at the - // moment + // there might be a cleverer way to do + // this, but since this function is called + // in very few places for initialization + // purposes only, I don't care at the + // moment switch (refine_case) { - case RefinementCase<3>::cut_x: - point[0]*=2.0; - if (child_index==1) - point[0]-=1.0; - break; - case RefinementCase<3>::cut_y: - point[1]*=2.0; - if (child_index==1) - point[1]-=1.0; - break; - case RefinementCase<3>::cut_z: - point[2]*=2.0; - if (child_index==1) - point[2]-=1.0; - break; - case RefinementCase<3>::cut_xy: - point[0]*=2.0; - point[1]*=2.0; - if (child_index%2==1) - point[0]-=1.0; - if (child_index/2==1) - point[1]-=1.0; - break; - case RefinementCase<3>::cut_xz: - // careful, this is slightly - // different from xy and yz due to - // different internal numbering of - // children! - point[0]*=2.0; - point[2]*=2.0; - if (child_index/2==1) - point[0]-=1.0; - if (child_index%2==1) - point[2]-=1.0; - break; - case RefinementCase<3>::cut_yz: - point[1]*=2.0; - point[2]*=2.0; - if (child_index%2==1) - point[1]-=1.0; - if (child_index/2==1) - point[2]-=1.0; - break; - case RefinementCase<3>::cut_xyz: - point*=2.0; - point-=unit_cell_vertex(child_index); - break; - default: - Assert(false, ExcInternalError()); + case RefinementCase<3>::cut_x: + point[0]*=2.0; + if (child_index==1) + point[0]-=1.0; + break; + case RefinementCase<3>::cut_y: + point[1]*=2.0; + if (child_index==1) + point[1]-=1.0; + break; + case RefinementCase<3>::cut_z: + point[2]*=2.0; + if (child_index==1) + point[2]-=1.0; + break; + case RefinementCase<3>::cut_xy: + point[0]*=2.0; + point[1]*=2.0; + if (child_index%2==1) + point[0]-=1.0; + if (child_index/2==1) + point[1]-=1.0; + break; + case RefinementCase<3>::cut_xz: + // careful, this is slightly + // different from xy and yz due to + // different internal numbering of + // children! + point[0]*=2.0; + point[2]*=2.0; + if (child_index/2==1) + point[0]-=1.0; + if (child_index%2==1) + point[2]-=1.0; + break; + case RefinementCase<3>::cut_yz: + point[1]*=2.0; + point[2]*=2.0; + if (child_index%2==1) + point[1]-=1.0; + if (child_index/2==1) + point[2]-=1.0; + break; + case RefinementCase<3>::cut_xyz: + point*=2.0; + point-=unit_cell_vertex(child_index); + break; + default: + Assert(false, ExcInternalError()); } return point; @@ -2773,62 +2775,62 @@ GeometryInfo<3>::child_to_cell_coordinates (const Point<3> &p, ExcIndexRange (child_index, 0, GeometryInfo<3>::n_children(refine_case))); Point<3> point=p; - // there might be a cleverer way to do - // this, but since this function is called - // in very few places for initialization - // purposes only, I don't care at the - // moment + // there might be a cleverer way to do + // this, but since this function is called + // in very few places for initialization + // purposes only, I don't care at the + // moment switch (refine_case) { - case RefinementCase<3>::cut_x: - if (child_index==1) - point[0]+=1.0; - point[0]*=0.5; - break; - case RefinementCase<3>::cut_y: - if (child_index==1) - point[1]+=1.0; - point[1]*=0.5; - break; - case RefinementCase<3>::cut_z: - if (child_index==1) - point[2]+=1.0; - point[2]*=0.5; - break; - case RefinementCase<3>::cut_xy: - if (child_index%2==1) - point[0]+=1.0; - if (child_index/2==1) - point[1]+=1.0; - point[0]*=0.5; - point[1]*=0.5; - break; - case RefinementCase<3>::cut_xz: - // careful, this is slightly - // different from xy and yz due to - // different internal numbering of - // children! - if (child_index/2==1) - point[0]+=1.0; - if (child_index%2==1) - point[2]+=1.0; - point[0]*=0.5; - point[2]*=0.5; - break; - case RefinementCase<3>::cut_yz: - if (child_index%2==1) - point[1]+=1.0; - if (child_index/2==1) - point[2]+=1.0; - point[1]*=0.5; - point[2]*=0.5; - break; - case RefinementCase<3>::cut_xyz: - point+=unit_cell_vertex(child_index); - point*=0.5; - break; - default: - Assert(false, ExcInternalError()); + case RefinementCase<3>::cut_x: + if (child_index==1) + point[0]+=1.0; + point[0]*=0.5; + break; + case RefinementCase<3>::cut_y: + if (child_index==1) + point[1]+=1.0; + point[1]*=0.5; + break; + case RefinementCase<3>::cut_z: + if (child_index==1) + point[2]+=1.0; + point[2]*=0.5; + break; + case RefinementCase<3>::cut_xy: + if (child_index%2==1) + point[0]+=1.0; + if (child_index/2==1) + point[1]+=1.0; + point[0]*=0.5; + point[1]*=0.5; + break; + case RefinementCase<3>::cut_xz: + // careful, this is slightly + // different from xy and yz due to + // different internal numbering of + // children! + if (child_index/2==1) + point[0]+=1.0; + if (child_index%2==1) + point[2]+=1.0; + point[0]*=0.5; + point[2]*=0.5; + break; + case RefinementCase<3>::cut_yz: + if (child_index%2==1) + point[1]+=1.0; + if (child_index/2==1) + point[2]+=1.0; + point[1]*=0.5; + point[2]*=0.5; + break; + case RefinementCase<3>::cut_xyz: + point+=unit_cell_vertex(child_index); + point*=0.5; + break; + default: + Assert(false, ExcInternalError()); } return point; @@ -2849,22 +2851,22 @@ GeometryInfo<2>::child_to_cell_coordinates (const Point<2> &p, Point<2> point=p; switch (refine_case) { - case RefinementCase<2>::cut_x: - if (child_index==1) - point[0]+=1.0; - point[0]*=0.5; - break; - case RefinementCase<2>::cut_y: - if (child_index==1) - point[1]+=1.0; - point[1]*=0.5; - break; - case RefinementCase<2>::cut_xy: - point+=unit_cell_vertex(child_index); - point*=0.5; - break; - default: - Assert(false, ExcInternalError()); + case RefinementCase<2>::cut_x: + if (child_index==1) + point[0]+=1.0; + point[0]*=0.5; + break; + case RefinementCase<2>::cut_y: + if (child_index==1) + point[1]+=1.0; + point[1]*=0.5; + break; + case RefinementCase<2>::cut_xy: + point+=unit_cell_vertex(child_index); + point*=0.5; + break; + default: + Assert(false, ExcInternalError()); } return point; diff --git a/deal.II/include/deal.II/base/index_set.h b/deal.II/include/deal.II/base/index_set.h index b1de672aed..3731e02d56 100644 --- a/deal.II/include/deal.II/base/index_set.h +++ b/deal.II/include/deal.II/base/index_set.h @@ -53,511 +53,511 @@ DEAL_II_NAMESPACE_OPEN */ class IndexSet { - public: - /** - * Default constructor. - */ - IndexSet (); - - /** - * Constructor that also sets the - * overall size of the index - * range. - */ - explicit IndexSet (const types::global_dof_index size); - - /** - * Remove all indices from this - * index set. The index set retains - * its size, however. - */ - void clear (); - - /** - * Set the maximal size of the - * indices upon which this object - * operates. - * - * This function can only be - * called if the index set does - * not yet contain any elements. - * This can be achieved by calling - * clear(), for example. - */ - void set_size (const unsigned int size); - - /** - * Return the size of the index - * space of which this index set - * is a subset of. - * - * Note that the result is not equal to - * the number of indices within this - * set. The latter information is - * returned by n_elements(). - */ - unsigned int size () const; - - /** - * Add the half-open range - * $[\text{begin},\text{end})$ to - * the set of indices represented - * by this class. - */ - void add_range (const unsigned int begin, - const unsigned int end); - - /** - * Add an individual index to the - * set of indices. - */ - void add_index (const unsigned int index); - - /** - * Add a whole set of indices - * described by dereferencing - * every element of the the - * iterator range - * [begin,end). - */ - template - void add_indices (const ForwardIterator &begin, - const ForwardIterator &end); - - /** - * Add the given IndexSet @p other to the - * current one, constructing the union of - * *this and @p other. - */ - void add_indices(const IndexSet & other); - - /** - * Return whether the specified - * index is an element of the - * index set. - */ - bool is_element (const types::global_dof_index index) const; - - /** - * Return whether the index set - * stored by this object defines - * a contiguous range. This is - * true also if no indices are - * stored at all. - */ - bool is_contiguous () const; - - /** - * Return the number of elements - * stored in this index set. - */ - types::global_dof_index n_elements () const; - - /** - * Return the global index of the local - * index with number @p local_index - * stored in this index set. @p - * local_index obviously needs to be less - * than n_elements(). - */ - types::global_dof_index nth_index_in_set (const unsigned int local_index) const; - - /** - * Return the how-manyth element of this - * set (counted in ascending order) @p - * global_index is. @p global_index needs - * to be less than the size(). This - * function throws an exception if the - * index @p global_index is not actually - * a member of this index set, i.e. if - * is_element(global_index) is false. - */ - unsigned int index_within_set (const types::global_dof_index global_index) const; - - /** - * Each index set can be - * represented as the union of a - * number of contiguous intervals - * of indices, where if necessary - * intervals may only consist of - * individual elements to - * represent isolated members of - * the index set. - * - * This function returns the - * minimal number of such - * intervals that are needed to - * represent the index set under - * consideration. - */ - unsigned int n_intervals () const; - - /** - * Compress the internal - * representation by merging - * individual elements with - * contiguous ranges, etc. This - * function does not have any - * external effect. - */ - void compress () const; - - /** - * Comparison for equality of - * index sets. This operation is - * only allowed if the size of - * the two sets is the same - * (though of course they do not - * have to have the same number - * of indices). - */ - bool operator == (const IndexSet &is) const; - - /** - * Comparison for inequality of - * index sets. This operation is - * only allowed if the size of - * the two sets is the same - * (though of course they do not - * have to have the same number - * of indices). - */ - bool operator != (const IndexSet &is) const; - - /** - * Return the intersection of the - * current index set and the - * argument given, i.e. a set of - * indices that are elements of - * both index sets. The two index - * sets must have the same size - * (though of course they do not - * have to have the same number - * of indices). - */ - IndexSet operator & (const IndexSet &is) const; - - /** - * This command takes an interval - * [begin, end) and returns - * the intersection of the current - * index set with the interval, shifted - * to the range [0, - * end-begin). - */ - IndexSet get_view (const types::global_dof_index begin, - const types::global_dof_index end) const; - - - /** - * Removes all elements contained in @p - * other from this set. In other words, - * if $x$ is the current object and $o$ - * the argument, then we compute $x - * \leftarrow x \backslash o$. - */ - void subtract_set (const IndexSet & other); - - - /** - * Fills the given vector with all - * indices contained in this IndexSet. - */ - void fill_index_vector(std::vector & indices) const; - - /** - * Fill the given vector with either - * zero or one elements, providing - * a binary representation of this - * index set. The given vector is - * assumed to already have the correct - * size. - * - * The given argument is filled with - * integer values zero and one, using - * vector.operator[]. Thus, - * any object that has such an operator - * can be used as long as it allows - * conversion of integers zero and one to - * elements of the vector. Specifically, - * this is the case for classes Vector, - * BlockVector, but also - * std::vector@, - * std::vector@, and - * std::vector@. - */ - template - void fill_binary_vector (Vector &vector) const; - - /** - * Outputs a text representation of this - * IndexSet to the given stream. Used for - * testing. - */ - template - void print(STREAM &out) const; - - /** - * Writes the IndexSet into a text based - * file format, that can be read in again - * using the read() function. - */ - void write(std::ostream & out) const; - - /** - * Constructs the IndexSet from a text - * based representation given by the - * stream @param in written by the - * write() function. - */ - void read(std::istream & in); - - /** - * Writes the IndexSet into a binary, - * compact representation, that can be - * read in again using the block_read() - * function. - */ - void block_write(std::ostream & out) const; - - /** - * Constructs the IndexSet from a binary - * representation given by the stream - * @param in written by the write_block() - * function. - */ - void block_read(std::istream & in); +public: + /** + * Default constructor. + */ + IndexSet (); + + /** + * Constructor that also sets the + * overall size of the index + * range. + */ + explicit IndexSet (const types::global_dof_index size); + + /** + * Remove all indices from this + * index set. The index set retains + * its size, however. + */ + void clear (); + + /** + * Set the maximal size of the + * indices upon which this object + * operates. + * + * This function can only be + * called if the index set does + * not yet contain any elements. + * This can be achieved by calling + * clear(), for example. + */ + void set_size (const unsigned int size); + + /** + * Return the size of the index + * space of which this index set + * is a subset of. + * + * Note that the result is not equal to + * the number of indices within this + * set. The latter information is + * returned by n_elements(). + */ + unsigned int size () const; + + /** + * Add the half-open range + * $[\text{begin},\text{end})$ to + * the set of indices represented + * by this class. + */ + void add_range (const unsigned int begin, + const unsigned int end); + + /** + * Add an individual index to the + * set of indices. + */ + void add_index (const unsigned int index); + + /** + * Add a whole set of indices + * described by dereferencing + * every element of the the + * iterator range + * [begin,end). + */ + template + void add_indices (const ForwardIterator &begin, + const ForwardIterator &end); + + /** + * Add the given IndexSet @p other to the + * current one, constructing the union of + * *this and @p other. + */ + void add_indices(const IndexSet &other); + + /** + * Return whether the specified + * index is an element of the + * index set. + */ + bool is_element (const types::global_dof_index index) const; + + /** + * Return whether the index set + * stored by this object defines + * a contiguous range. This is + * true also if no indices are + * stored at all. + */ + bool is_contiguous () const; + + /** + * Return the number of elements + * stored in this index set. + */ + types::global_dof_index n_elements () const; + + /** + * Return the global index of the local + * index with number @p local_index + * stored in this index set. @p + * local_index obviously needs to be less + * than n_elements(). + */ + types::global_dof_index nth_index_in_set (const unsigned int local_index) const; + + /** + * Return the how-manyth element of this + * set (counted in ascending order) @p + * global_index is. @p global_index needs + * to be less than the size(). This + * function throws an exception if the + * index @p global_index is not actually + * a member of this index set, i.e. if + * is_element(global_index) is false. + */ + unsigned int index_within_set (const types::global_dof_index global_index) const; + + /** + * Each index set can be + * represented as the union of a + * number of contiguous intervals + * of indices, where if necessary + * intervals may only consist of + * individual elements to + * represent isolated members of + * the index set. + * + * This function returns the + * minimal number of such + * intervals that are needed to + * represent the index set under + * consideration. + */ + unsigned int n_intervals () const; + + /** + * Compress the internal + * representation by merging + * individual elements with + * contiguous ranges, etc. This + * function does not have any + * external effect. + */ + void compress () const; + + /** + * Comparison for equality of + * index sets. This operation is + * only allowed if the size of + * the two sets is the same + * (though of course they do not + * have to have the same number + * of indices). + */ + bool operator == (const IndexSet &is) const; + + /** + * Comparison for inequality of + * index sets. This operation is + * only allowed if the size of + * the two sets is the same + * (though of course they do not + * have to have the same number + * of indices). + */ + bool operator != (const IndexSet &is) const; + + /** + * Return the intersection of the + * current index set and the + * argument given, i.e. a set of + * indices that are elements of + * both index sets. The two index + * sets must have the same size + * (though of course they do not + * have to have the same number + * of indices). + */ + IndexSet operator & (const IndexSet &is) const; + + /** + * This command takes an interval + * [begin, end) and returns + * the intersection of the current + * index set with the interval, shifted + * to the range [0, + * end-begin). + */ + IndexSet get_view (const types::global_dof_index begin, + const types::global_dof_index end) const; + + + /** + * Removes all elements contained in @p + * other from this set. In other words, + * if $x$ is the current object and $o$ + * the argument, then we compute $x + * \leftarrow x \backslash o$. + */ + void subtract_set (const IndexSet &other); + + + /** + * Fills the given vector with all + * indices contained in this IndexSet. + */ + void fill_index_vector(std::vector &indices) const; + + /** + * Fill the given vector with either + * zero or one elements, providing + * a binary representation of this + * index set. The given vector is + * assumed to already have the correct + * size. + * + * The given argument is filled with + * integer values zero and one, using + * vector.operator[]. Thus, + * any object that has such an operator + * can be used as long as it allows + * conversion of integers zero and one to + * elements of the vector. Specifically, + * this is the case for classes Vector, + * BlockVector, but also + * std::vector@, + * std::vector@, and + * std::vector@. + */ + template + void fill_binary_vector (Vector &vector) const; + + /** + * Outputs a text representation of this + * IndexSet to the given stream. Used for + * testing. + */ + template + void print(STREAM &out) const; + + /** + * Writes the IndexSet into a text based + * file format, that can be read in again + * using the read() function. + */ + void write(std::ostream &out) const; + + /** + * Constructs the IndexSet from a text + * based representation given by the + * stream @param in written by the + * write() function. + */ + void read(std::istream &in); + + /** + * Writes the IndexSet into a binary, + * compact representation, that can be + * read in again using the block_read() + * function. + */ + void block_write(std::ostream &out) const; + + /** + * Constructs the IndexSet from a binary + * representation given by the stream + * @param in written by the write_block() + * function. + */ + void block_read(std::istream &in); #ifdef DEAL_II_USE_TRILINOS - /** - * Given an MPI communicator, - * create a Trilinos map object - * that represents a distribution - * of vector elements or matrix - * rows in which we will locally - * store those elements or rows - * for which we store the index - * in the current index set, and - * all the other elements/rows - * elsewhere on one of the other - * MPI processes. - * - * The last argument only plays a - * role if the communicator is a - * parallel one, distributing - * computations across multiple - * processors. In that case, if - * the last argument is false, - * then it is assumed that the - * index sets this function is - * called on on all processors - * are mutually exclusive but - * together enumerate each index - * exactly once. In other words, - * if you call this function on - * two processors, then the index - * sets this function is called - * with must together have all - * possible indices from zero to - * size()-1, and no index must - * appear in both index - * sets. This corresponds, for - * example, to the case where we - * want to split the elements of - * vectors into unique subsets to - * be stored on different - * processors -- no element - * should be owned by more than - * one processor, but each - * element must be owned by one. - * - * On the other hand, if the - * second argument is true, then - * the index sets can be - * overlapping, though they still - * need to contain each index - * exactly once on all processors - * taken together. This is a - * useful operation if we want to - * create vectors that not only - * contain the locally owned - * indices, but for example also - * the elements that correspond - * to degrees of freedom located - * on ghost cells. - */ - Epetra_Map make_trilinos_map (const MPI_Comm &communicator = MPI_COMM_WORLD, - const bool overlapping = false) const; + /** + * Given an MPI communicator, + * create a Trilinos map object + * that represents a distribution + * of vector elements or matrix + * rows in which we will locally + * store those elements or rows + * for which we store the index + * in the current index set, and + * all the other elements/rows + * elsewhere on one of the other + * MPI processes. + * + * The last argument only plays a + * role if the communicator is a + * parallel one, distributing + * computations across multiple + * processors. In that case, if + * the last argument is false, + * then it is assumed that the + * index sets this function is + * called on on all processors + * are mutually exclusive but + * together enumerate each index + * exactly once. In other words, + * if you call this function on + * two processors, then the index + * sets this function is called + * with must together have all + * possible indices from zero to + * size()-1, and no index must + * appear in both index + * sets. This corresponds, for + * example, to the case where we + * want to split the elements of + * vectors into unique subsets to + * be stored on different + * processors -- no element + * should be owned by more than + * one processor, but each + * element must be owned by one. + * + * On the other hand, if the + * second argument is true, then + * the index sets can be + * overlapping, though they still + * need to contain each index + * exactly once on all processors + * taken together. This is a + * useful operation if we want to + * create vectors that not only + * contain the locally owned + * indices, but for example also + * the elements that correspond + * to degrees of freedom located + * on ghost cells. + */ + Epetra_Map make_trilinos_map (const MPI_Comm &communicator = MPI_COMM_WORLD, + const bool overlapping = false) const; #endif - /** - * Determine an estimate for the memory - * consumption (in bytes) of this - * object. - */ - std::size_t memory_consumption () const; + /** + * Determine an estimate for the memory + * consumption (in bytes) of this + * object. + */ + std::size_t memory_consumption () const; + + DeclException1 (ExcIndexNotPresent, int, + << "The global index " << arg1 + << " is not an element of this set."); + + /** + * Write or read the data of this object to or + * from a stream for the purpose of serialization + */ + template + void serialize (Archive &ar, const unsigned int version); + +private: + /** + * A type that denotes the half + * open index range + * [begin,end). + * + * The nth_index_in_set denotes + * the how many-th index within + * this IndexSet the first + * element of the current range + * is. This information is only + * accurate if + * IndexSet::compress() has been + * called after the last + * insertion. + */ + struct Range + { + types::global_dof_index begin; + types::global_dof_index end; + + types::global_dof_index nth_index_in_set; - DeclException1 (ExcIndexNotPresent, int, - << "The global index " << arg1 - << " is not an element of this set."); + /** + * Default constructor. Since there is no useful choice for + * a default constructed interval, this constructor simply + * creates something that resembles an invalid range. We + * need this constructor for serialization purposes, but the + * invalid range should be filled with something read from + * the archive before it is used, so we should hopefully + * never get to see an invalid range in the wild. + **/ + Range (); + + /** + * Constructor. Create a half-open interval with the given indices. + * + * @param i1 Left end point of the interval. + * @param i2 First index greater than the last index of the indicated range. + **/ + Range (const types::global_dof_index i1, + const types::global_dof_index i2); + + friend + inline bool operator< (const Range &range_1, + const Range &range_2) + { + return ((range_1.begin < range_2.begin) + || + ((range_1.begin == range_2.begin) + && + (range_1.end < range_2.end))); + } + + static bool end_compare(const IndexSet::Range &x, const IndexSet::Range &y) + { + return x.end < y.end; + } + + static bool nth_index_compare (const IndexSet::Range &x, + const IndexSet::Range &y) + { + return (x.nth_index_in_set+(x.end-x.begin) < + y.nth_index_in_set+(y.end-y.begin)); + } + + friend + inline bool operator== (const Range &range_1, + const Range &range_2) + { + return ((range_1.begin == range_2.begin) + || + (range_1.begin == range_2.begin)); + } + + std::size_t memory_consumption () const + { + return sizeof(Range); + } /** * Write or read the data of this object to or * from a stream for the purpose of serialization */ template - void serialize (Archive & ar, const unsigned int version); - - private: - /** - * A type that denotes the half - * open index range - * [begin,end). - * - * The nth_index_in_set denotes - * the how many-th index within - * this IndexSet the first - * element of the current range - * is. This information is only - * accurate if - * IndexSet::compress() has been - * called after the last - * insertion. - */ - struct Range - { - types::global_dof_index begin; - types::global_dof_index end; - - types::global_dof_index nth_index_in_set; - - /** - * Default constructor. Since there is no useful choice for - * a default constructed interval, this constructor simply - * creates something that resembles an invalid range. We - * need this constructor for serialization purposes, but the - * invalid range should be filled with something read from - * the archive before it is used, so we should hopefully - * never get to see an invalid range in the wild. - **/ - Range (); - - /** - * Constructor. Create a half-open interval with the given indices. - * - * @param i1 Left end point of the interval. - * @param i2 First index greater than the last index of the indicated range. - **/ - Range (const types::global_dof_index i1, - const types::global_dof_index i2); - - friend - inline bool operator< (const Range &range_1, - const Range &range_2) - { - return ((range_1.begin < range_2.begin) - || - ((range_1.begin == range_2.begin) - && - (range_1.end < range_2.end))); - } - - static bool end_compare(const IndexSet::Range & x, const IndexSet::Range & y) - { - return x.end < y.end; - } - - static bool nth_index_compare (const IndexSet::Range & x, - const IndexSet::Range & y) - { - return (x.nth_index_in_set+(x.end-x.begin) < - y.nth_index_in_set+(y.end-y.begin)); - } - - friend - inline bool operator== (const Range &range_1, - const Range &range_2) - { - return ((range_1.begin == range_2.begin) - || - (range_1.begin == range_2.begin)); - } - - std::size_t memory_consumption () const - { - return sizeof(Range); - } - - /** - * Write or read the data of this object to or - * from a stream for the purpose of serialization - */ - template - void serialize (Archive & ar, const unsigned int version); - }; - - /** - * A set of contiguous ranges of - * indices that make up (part of) - * this index set. This variable - * is always kept sorted. - * - * The variable is marked - * "mutable" so that it can be - * changed by compress(), though - * this of course doesn't change - * anything about the external - * representation of this index - * set. - */ - mutable std::vector ranges; - - /** - * True if compress() has been - * called after the last change - * in the set of indices. - * - * The variable is marked - * "mutable" so that it can be - * changed by compress(), though - * this of course doesn't change - * anything about the external - * representation of this index - * set. - */ - mutable bool is_compressed; - - /** - * The overall size of the index - * range. Elements of this index - * set have to have a smaller - * number than this value. - */ - unsigned int index_space_size; - - /** - * This integer caches the index of the - * largest range in @p ranges. This gives - * O(1) access to the range with - * most elements, while general access - * costs O(log(n_ranges)). The - * largest range is needed for the - * methods @p is_element(), @p - * index_within_set(), @p - * nth_index_in_set. In many - * applications, the largest range - * contains most elements (the locally - * owned range), whereas there are only a - * few other elements (ghosts). - */ - mutable unsigned int largest_range; - - /** - * Actually perform the compress() - * operation. - */ - void do_compress() const; + void serialize (Archive &ar, const unsigned int version); + }; + + /** + * A set of contiguous ranges of + * indices that make up (part of) + * this index set. This variable + * is always kept sorted. + * + * The variable is marked + * "mutable" so that it can be + * changed by compress(), though + * this of course doesn't change + * anything about the external + * representation of this index + * set. + */ + mutable std::vector ranges; + + /** + * True if compress() has been + * called after the last change + * in the set of indices. + * + * The variable is marked + * "mutable" so that it can be + * changed by compress(), though + * this of course doesn't change + * anything about the external + * representation of this index + * set. + */ + mutable bool is_compressed; + + /** + * The overall size of the index + * range. Elements of this index + * set have to have a smaller + * number than this value. + */ + unsigned int index_space_size; + + /** + * This integer caches the index of the + * largest range in @p ranges. This gives + * O(1) access to the range with + * most elements, while general access + * costs O(log(n_ranges)). The + * largest range is needed for the + * methods @p is_element(), @p + * index_within_set(), @p + * nth_index_in_set. In many + * applications, the largest range + * contains most elements (the locally + * owned range), whereas there are only a + * few other elements (ghosts). + */ + mutable unsigned int largest_range; + + /** + * Actually perform the compress() + * operation. + */ + void do_compress() const; }; @@ -565,37 +565,37 @@ class IndexSet inline IndexSet::Range::Range () - : - begin(numbers::invalid_unsigned_int), - end(numbers::invalid_unsigned_int) + : + begin(numbers::invalid_unsigned_int), + end(numbers::invalid_unsigned_int) {} inline IndexSet::Range::Range (const types::global_dof_index i1, const types::global_dof_index i2) - : - begin(i1), - end(i2) + : + begin(i1), + end(i2) {} inline IndexSet::IndexSet () - : - is_compressed (true), - index_space_size (0), - largest_range (deal_II_numbers::invalid_unsigned_int) + : + is_compressed (true), + index_space_size (0), + largest_range (deal_II_numbers::invalid_unsigned_int) {} inline IndexSet::IndexSet (const types::global_dof_index size) - : - is_compressed (true), - index_space_size (size), - largest_range (deal_II_numbers::invalid_unsigned_int) + : + is_compressed (true), + index_space_size (size), + largest_range (deal_II_numbers::invalid_unsigned_int) {} @@ -662,15 +662,15 @@ IndexSet::add_range (const types::global_dof_index begin, { const Range new_range(begin,end); - // the new index might be larger than the last - // index present in the ranges. Then we can - // skip the binary search + // the new index might be larger than the last + // index present in the ranges. Then we can + // skip the binary search if (ranges.size() == 0 || begin > ranges.back().end) ranges.push_back(new_range); else ranges.insert (Utilities::lower_bound (ranges.begin(), - ranges.end(), - new_range), + ranges.end(), + new_range), new_range); is_compressed = false; } @@ -692,8 +692,8 @@ IndexSet::add_index (const types::global_dof_index index) ranges.back().end++; else ranges.insert (Utilities::lower_bound (ranges.begin(), - ranges.end(), - new_range), + ranges.end(), + new_range), new_range); is_compressed = false; } @@ -706,10 +706,10 @@ void IndexSet::add_indices (const ForwardIterator &begin, const ForwardIterator &end) { - // insert each element of the - // range. if some of them happen to - // be consecutive, merge them to a - // range + // insert each element of the + // range. if some of them happen to + // be consecutive, merge them to a + // range for (ForwardIterator p=begin; p!=end;) { const types::global_dof_index begin_index = *p; @@ -731,7 +731,7 @@ IndexSet::add_indices (const ForwardIterator &begin, inline void -IndexSet::add_indices(const IndexSet & other) +IndexSet::add_indices(const IndexSet &other) { if (this == &other) return; @@ -756,62 +756,62 @@ IndexSet::is_element (const types::global_dof_index index) const { compress (); - // fast check whether the index is in the - // largest range + // fast check whether the index is in the + // largest range Assert (largest_range < ranges.size(), ExcInternalError()); if (index >= ranges[largest_range].begin && index < ranges[largest_range].end) return true; - // get the element after which - // we would have to insert a - // range that consists of all - // elements from this element - // to the end of the index - // range plus one. after this - // call we know that if - // p!=end() then - // p->begin<=index unless there - // is no such range at all - // - // if the searched for element - // is an element of this range, - // then we're done. otherwise, - // the element can't be in one - // of the following ranges - // because otherwise p would be - // a different iterator - // - // since we already know the position - // relative to the largest range (we - // called compress!), we can perform - // the binary search on ranges with - // lower/higher number compared to the - // largest range + // get the element after which + // we would have to insert a + // range that consists of all + // elements from this element + // to the end of the index + // range plus one. after this + // call we know that if + // p!=end() then + // p->begin<=index unless there + // is no such range at all + // + // if the searched for element + // is an element of this range, + // then we're done. otherwise, + // the element can't be in one + // of the following ranges + // because otherwise p would be + // a different iterator + // + // since we already know the position + // relative to the largest range (we + // called compress!), we can perform + // the binary search on ranges with + // lower/higher number compared to the + // largest range std::vector::const_iterator - p = std::upper_bound (ranges.begin() + (index= p->begin) && (index < p->end)); Assert ((p == ranges.end()) || (p->begin > index), - ExcInternalError()); + ExcInternalError()); - // now move to that previous - // range + // now move to that previous + // range --p; Assert (p->begin <= index, ExcInternalError()); return (p->end > index); } - // didn't find this index, so it's - // not in the set + // didn't find this index, so it's + // not in the set return false; } @@ -831,14 +831,14 @@ inline types::global_dof_index IndexSet::n_elements () const { - // make sure we have - // non-overlapping ranges + // make sure we have + // non-overlapping ranges compress (); unsigned int v = 0; if (!ranges.empty()) { - Range & r = ranges.back(); + Range &r = ranges.back(); v = r.nth_index_in_set + r.end - r.begin; } @@ -860,24 +860,24 @@ inline unsigned int IndexSet::nth_index_in_set (const types::global_dof_index n) const { - // to make this call thread-safe, compress() - // must not be called through this function + // to make this call thread-safe, compress() + // must not be called through this function Assert (is_compressed == true, ExcMessage ("IndexSet must be compressed.")); Assert (n < n_elements(), ExcIndexRange (n, 0, n_elements())); - // first check whether the index is in the - // largest range + // first check whether the index is in the + // largest range Assert (largest_range < ranges.size(), ExcInternalError()); std::vector::const_iterator main_range=ranges.begin()+largest_range; if (n>=main_range->nth_index_in_set && nnth_index_in_set+(main_range->end-main_range->begin)) return main_range->begin + (n-main_range->nth_index_in_set); - // find out which chunk the local index n - // belongs to by using a binary search. the - // comparator is based on the end of the - // ranges. Use the position relative to main_range to - // subdivide the ranges + // find out which chunk the local index n + // belongs to by using a binary search. the + // comparator is based on the end of the + // ranges. Use the position relative to main_range to + // subdivide the ranges Range r (n,n+1); r.nth_index_in_set = n; std::vector::const_iterator range_begin, range_end; @@ -893,8 +893,8 @@ IndexSet::nth_index_in_set (const types::global_dof_index n) const } std::vector::const_iterator - p = Utilities::lower_bound(range_begin, range_end, r, - Range::nth_index_compare); + p = Utilities::lower_bound(range_begin, range_end, r, + Range::nth_index_compare); if (p != ranges.end()) return p->begin + (n-p->nth_index_in_set); @@ -911,15 +911,15 @@ inline types::global_dof_index IndexSet::index_within_set (const unsigned int n) const { - // to make this call thread-safe, compress() - // must not be called through this function + // to make this call thread-safe, compress() + // must not be called through this function Assert (is_compressed == true, ExcMessage ("IndexSet must be compressed.")); Assert (is_element(n) == true, ExcIndexNotPresent (n)); Assert (n < size(), ExcIndexRange (n, 0, size())); - // check whether the index is in the largest - // range. use the result to perform a - // one-sided binary search afterward + // check whether the index is in the largest + // range. use the result to perform a + // one-sided binary search afterward Assert (largest_range < ranges.size(), ExcInternalError()); std::vector::const_iterator main_range=ranges.begin()+largest_range; if (n >= main_range->begin && n < main_range->end) @@ -939,8 +939,8 @@ IndexSet::index_within_set (const unsigned int n) const } std::vector::const_iterator - p = Utilities::lower_bound(range_begin, range_end, r, - Range::end_compare); + p = Utilities::lower_bound(range_begin, range_end, r, + Range::end_compare); Assert(p!=ranges.end(), ExcInternalError()); Assert(p->begin<=n, ExcInternalError()); @@ -988,12 +988,12 @@ IndexSet::fill_binary_vector (Vector &vector) const ExcDimensionMismatch (vector.size(), size())); compress(); - // first fill all elements of the vector - // with zeroes. + // first fill all elements of the vector + // with zeroes. std::fill (vector.begin(), vector.end(), 0); - // then write ones into the elements whose - // indices are contained in the index set + // then write ones into the elements whose + // indices are contained in the index set for (std::vector::iterator it = ranges.begin(); it != ranges.end(); ++it) @@ -1029,9 +1029,9 @@ IndexSet::print (STREAM &out) const template inline void -IndexSet::Range::serialize (Archive & ar, const unsigned int) +IndexSet::Range::serialize (Archive &ar, const unsigned int) { - ar & begin & end & nth_index_in_set; + ar &begin &end &nth_index_in_set; } @@ -1039,9 +1039,9 @@ IndexSet::Range::serialize (Archive & ar, const unsigned int) template inline void -IndexSet::serialize (Archive & ar, const unsigned int) +IndexSet::serialize (Archive &ar, const unsigned int) { - ar & ranges & is_compressed & index_space_size & largest_range; + ar &ranges &is_compressed &index_space_size &largest_range; } DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/include/deal.II/base/job_identifier.h b/deal.II/include/deal.II/base/job_identifier.h index d7b0e70d2d..d39688e652 100644 --- a/deal.II/include/deal.II/base/job_identifier.h +++ b/deal.II/include/deal.II/base/job_identifier.h @@ -28,58 +28,58 @@ DEAL_II_NAMESPACE_OPEN */ class JobIdentifier { - public: - /** - * Constructor. Set program - * identifier to value of - * program_id concatenated - * with the present time. - */ - JobIdentifier(); +public: + /** + * Constructor. Set program + * identifier to value of + * program_id concatenated + * with the present time. + */ + JobIdentifier(); - /** - * This function returns an - * identifier for the running - * program. Currently, the - * library provides a function - * returning "JobID". - * - * The user may define a - * replacement of this function - * in his source code and avoid - * linking the library - * version. Unfortunately, this - * mechanism does not work with - * shared libraries. - */ - static const char* program_id(); + /** + * This function returns an + * identifier for the running + * program. Currently, the + * library provides a function + * returning "JobID". + * + * The user may define a + * replacement of this function + * in his source code and avoid + * linking the library + * version. Unfortunately, this + * mechanism does not work with + * shared libraries. + */ + static const char *program_id(); - /** - * Obtain the base name of the - * file currently being - * compiled. That is, if the file - * is mypath/file.cc - * return just - * file. Typically, this - * can be called from a program - * with the argument - * __FILE__ and is used - * in the deal.II test suite. - */ - static std::string base_name(const char* filename); + /** + * Obtain the base name of the + * file currently being + * compiled. That is, if the file + * is mypath/file.cc + * return just + * file. Typically, this + * can be called from a program + * with the argument + * __FILE__ and is used + * in the deal.II test suite. + */ + static std::string base_name(const char *filename); - /** - * Return the value of id. - */ - const std::string operator () () const; + /** + * Return the value of id. + */ + const std::string operator () () const; - private: - /** - * String holding the identifier - * of the presently running - * program. - */ - std::string id; +private: + /** + * String holding the identifier + * of the presently running + * program. + */ + std::string id; }; diff --git a/deal.II/include/deal.II/base/logstream.h b/deal.II/include/deal.II/base/logstream.h index e8223227c5..cb50979e38 100644 --- a/deal.II/include/deal.II/base/logstream.h +++ b/deal.II/include/deal.II/base/logstream.h @@ -28,7 +28,7 @@ #else struct tms { - int tms_utime, tms_stime, tms_cutime, tms_cstime; + int tms_utime, tms_stime, tms_cutime, tms_cstime; }; #endif @@ -85,530 +85,530 @@ DEAL_II_NAMESPACE_OPEN */ class LogStream : public Subscriptor { +public: + /** + * A subclass allowing for the + * safe generation and removal of + * prefices. + * + * Somewhere at the beginning of + * a block, create one of these + * objects, and it will appear as + * a prefix in LogStream output + * like @p deallog. At the end of + * the block, the prefix will + * automatically be removed, when + * this object is destroyed. + */ + class Prefix + { public: - /** - * A subclass allowing for the - * safe generation and removal of - * prefices. - * - * Somewhere at the beginning of - * a block, create one of these - * objects, and it will appear as - * a prefix in LogStream output - * like @p deallog. At the end of - * the block, the prefix will - * automatically be removed, when - * this object is destroyed. - */ - class Prefix - { - public: - /** - * Set a new prefix for - * @p deallog, which will be - * removed when the variable - * is destroyed . - */ - Prefix(const std::string& text); - - /** - * Set a new prefix for the - * given stream, which will - * be removed when the - * variable is destroyed . - */ - Prefix(const std::string& text, LogStream& stream); - - /** - * Remove the prefix - * associated with this - * variable. - */ - ~Prefix (); - - private: - SmartPointer stream; - }; - - /** - * Standard constructor, since we - * intend to provide an object - * deallog in the library. Set the - * standard output stream to std::cerr. - */ - LogStream (); - - /** - * Destructor. - */ - ~LogStream(); - - /** - * Enable output to a second - * stream o. - */ - void attach (std::ostream& o); - - /** - * Disable output to the second - * stream. You may want to call - * close on the stream that was - * previously attached to this object. - */ - void detach (); - - /** - * Setup the logstream for - * regression test mode. - * - * This sets the parameters - * #double_threshold, - * #float_threshold, and #offset - * to nonzero values. The exact - * values being used have been - * determined experimentally and - * can be found in the source - * code. - * - * Called with an argument - * false, switches off - * test mode and sets all - * involved parameters to zero. - */ - void test_mode (bool on=true); - - /** - * Gives the default stream (std_out). - */ - std::ostream& get_console (); - - /** - * Gives the file stream. - */ - std::ostream& get_file_stream (); - - /** - * @return true, if file stream - * has already been attached. - */ - bool has_file () const; - - /** - * Reroutes cerr to LogStream. - * Works as a switch, turning - * logging of cerr on - * and off alternatingly with - * every call. - */ - void log_cerr (); - - /** - * Return the prefix string. - */ - const std::string& get_prefix () const; - - /** - * @deprecated Use Prefix instead - * - * Push another prefix on the - * stack. Prefixes are - * automatically separated by a - * colon and there is a double - * colon after the last prefix. - */ - void push (const std::string& text); - - /** - * @deprecated Use Prefix instead - * - * Remove the last prefix. - */ - void pop (); - - /** - * Maximum number of levels to be - * printed on the console. This - * function allows to restrict - * console output to the upmost - * levels of iterations. Only - * output with less than n - * prefixes is printed. By calling - * this function with n=0, no - * console output will be written. - * - * The previous value of this - * parameter is returned. - */ - unsigned int depth_console (const unsigned int n); - - /** - * Maximum number of levels to be - * written to the log file. The - * functionality is the same as - * depth_console, nevertheless, - * this function should be used - * with care, since it may spoile - * the value of a log file. - * - * The previous value of this - * parameter is returned. - */ - unsigned int depth_file (const unsigned int n); - - /** - * Set time printing flag. If this flag - * is true, each output line will - * be prepended by the user time used - * by the running program so far. - * - * The previous value of this - * parameter is returned. - */ - bool log_execution_time (const bool flag); - - /** - * Output time differences - * between consecutive logs. If - * this function is invoked with - * true, the time difference - * between the previous log line - * and the recent one is - * printed. If it is invoked with - * false, the accumulated - * time since start of the - * program is printed (default - * behavior). - * - * The measurement of times is - * not changed by this function, - * just the output. - * - * The previous value of this - * parameter is returned. - */ - bool log_time_differences (const bool flag); - - /** - * Write detailed timing - * information. - * - * - */ - void timestamp(); - - /** - * Log the thread id. - */ - bool log_thread_id (const bool flag); - - /** - * Set a threshold for the - * minimal absolute value of - * double values. All numbers - * with a smaller absolute value - * will be printed as zero. - * - * The default value for this - * threshold is zero, - * i.e. numbers are printed - * according to their real value. - * - * This feature is mostly useful - * for automated tests: there, - * one would like to reproduce - * the exact same solution in - * each run of a - * testsuite. However, subtle - * difference in processor, - * operating system, or compiler - * version can lead to - * differences in the last few - * digits of numbers, due to - * different rounding. While one - * can avoid trouble for most - * numbers when comparing with - * stored results by simply - * limiting the accuracy of - * output, this does not hold for - * numbers very close to zero, - * i.e. zero plus accumulated - * round-off. For these numbers, - * already the first digit is - * tainted by round-off. Using - * the present function, it is - * possible to eliminate this - * source of problems, by simply - * writing zero to the output in - * this case. - */ - void threshold_double(const double t); - /** - * The same as - * threshold_double(), but for - * float values. - */ - void threshold_float(const float t); - - /** - * Output a constant something - * through this stream. - */ - template - LogStream & operator << (const T &t); - - /** - * Output double precision - * numbers through this - * stream. - * - * If they are set, this function - * applies the methods for making - * floating point output - * reproducible as discussed in - * the introduction. - */ - LogStream & operator << (const double t); - - /** - * Output single precision - * numbers through this - * stream. - * - * If they are set, this function - * applies the methods for making - * floating point output - * reproducible as discussed in - * the introduction. - */ - LogStream & operator << (const float t); - - /** - * Treat ostream - * manipulators. This passes on - * the whole thing to the - * template function with the - * exception of the - * std::endl - * manipulator, for which special - * action is performed: write the - * temporary stream buffer - * including a header to the file - * and std::cout and - * empty the buffer. - * - * An overload of this function is needed - * anyway, since the compiler can't bind - * manipulators like @p std::endl - * directly to template arguments @p T - * like in the previous general - * template. This is due to the fact that - * @p std::endl is actually an overloaded - * set of functions for @p std::ostream, - * @p std::wostream, and potentially more - * of this kind. This function is - * therefore necessary to pick one - * element from this overload set. - */ - LogStream & operator<< (std::ostream& (*p) (std::ostream&)); - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - std::size_t memory_consumption () const; - - /** - * Exception. - */ - DeclException0(ExcNoFileStreamGiven); + /** + * Set a new prefix for + * @p deallog, which will be + * removed when the variable + * is destroyed . + */ + Prefix(const std::string &text); + + /** + * Set a new prefix for the + * given stream, which will + * be removed when the + * variable is destroyed . + */ + Prefix(const std::string &text, LogStream &stream); + + /** + * Remove the prefix + * associated with this + * variable. + */ + ~Prefix (); private: - - /** - * Stack of strings which are printed - * at the beginning of each line to - * allow identification where the - * output was generated. - */ - std::stack prefixes; - - /** - * Default stream, where the output - * is to go to. This stream defaults - * to std::cerr, but can be set to another - * stream through the constructor. - */ - std::ostream *std_out; - - /** - * Pointer to a stream, where a copy of - * the output is to go to. Usually, this - * will be a file stream. - * - * You can set and reset this stream - * by the attach function. - */ - std::ostream *file; - - /** - * Value denoting the number of - * prefixes to be printed to the - * standard output. If more than - * this number of prefixes is - * pushed to the stack, then no - * output will be generated until - * the number of prefixes shrinks - * back below this number. - */ - unsigned int std_depth; - - /** - * Same for the maximum depth of - * prefixes for output to a file. - */ - unsigned int file_depth; - - /** - * Flag for printing execution time. - */ - bool print_utime; - - /** - * Flag for printing time differences. - */ - bool diff_utime; - - /** - * Time of last output line. - */ - double last_time; - - /** - * Threshold for printing double - * values. Every number with - * absolute value less than this - * is printed as zero. - */ - double double_threshold; - - /** - * Threshold for printing float - * values. Every number with - * absolute value less than this - * is printed as zero. - */ - float float_threshold; - - /** - * An offset added to every float - * or double number upon - * output. This is done after the - * number is compared to - * #double_threshold or #float_threshold, - * but before rounding. - * - * This functionality was - * introduced to produce more - * reproducible floating point - * output for regression - * tests. The rationale is, that - * an exact output value is much - * more likely to be 1/8 than - * 0.124997. If we round to two - * digits though, 1/8 becomes - * unreliably either .12 or .13 - * due to machine accuracy. On - * the other hand, if we add a - * something above machine - * accuracy first, we will always - * get .13. - * - * It is safe to leave this - * value equal to zero. For - * regression tests, the function - * test_mode() sets it to a - * reasonable value. - * - * The offset is relative to the - * magnitude of the number. - */ - double offset; - - /** - * Flag for printing thread id. - */ - bool print_thread_id; - - /** - * The value times() returned - * on initialization. - */ - double reference_time_val; - - /** - * The tms structure times() - * filled on initialization. - */ - struct tms reference_tms; - - /** - * Original buffer of - * std::cerr. We store - * the address of that buffer - * when #log_cerr is called, and - * reset it to this value if - * #log_cerr is called a second - * time, or when the destructor - * of this class is run. - */ - std::streambuf *old_cerr; - - /** - * Print head of line. This prints - * optional time information and - * the contents of the prefix stack. - */ - void print_line_head (); - - /** - * Actually do the work of - * writing output. This function - * unifies the work that is - * common to the two - * operator<< functions. - */ - template - void print (const T &t); - /** - * Check if we are on a new line - * and print the header before - * the data. - */ - std::ostringstream& get_stream(); - - /** - * Type of the stream map - */ - typedef std::map > stream_map_type; - - /** - * We generate a stringstream for - * every process that sends log - * messages. - */ - stream_map_type outstreams; + SmartPointer stream; + }; + + /** + * Standard constructor, since we + * intend to provide an object + * deallog in the library. Set the + * standard output stream to std::cerr. + */ + LogStream (); + + /** + * Destructor. + */ + ~LogStream(); + + /** + * Enable output to a second + * stream o. + */ + void attach (std::ostream &o); + + /** + * Disable output to the second + * stream. You may want to call + * close on the stream that was + * previously attached to this object. + */ + void detach (); + + /** + * Setup the logstream for + * regression test mode. + * + * This sets the parameters + * #double_threshold, + * #float_threshold, and #offset + * to nonzero values. The exact + * values being used have been + * determined experimentally and + * can be found in the source + * code. + * + * Called with an argument + * false, switches off + * test mode and sets all + * involved parameters to zero. + */ + void test_mode (bool on=true); + + /** + * Gives the default stream (std_out). + */ + std::ostream &get_console (); + + /** + * Gives the file stream. + */ + std::ostream &get_file_stream (); + + /** + * @return true, if file stream + * has already been attached. + */ + bool has_file () const; + + /** + * Reroutes cerr to LogStream. + * Works as a switch, turning + * logging of cerr on + * and off alternatingly with + * every call. + */ + void log_cerr (); + + /** + * Return the prefix string. + */ + const std::string &get_prefix () const; + + /** + * @deprecated Use Prefix instead + * + * Push another prefix on the + * stack. Prefixes are + * automatically separated by a + * colon and there is a double + * colon after the last prefix. + */ + void push (const std::string &text); + + /** + * @deprecated Use Prefix instead + * + * Remove the last prefix. + */ + void pop (); + + /** + * Maximum number of levels to be + * printed on the console. This + * function allows to restrict + * console output to the upmost + * levels of iterations. Only + * output with less than n + * prefixes is printed. By calling + * this function with n=0, no + * console output will be written. + * + * The previous value of this + * parameter is returned. + */ + unsigned int depth_console (const unsigned int n); + + /** + * Maximum number of levels to be + * written to the log file. The + * functionality is the same as + * depth_console, nevertheless, + * this function should be used + * with care, since it may spoile + * the value of a log file. + * + * The previous value of this + * parameter is returned. + */ + unsigned int depth_file (const unsigned int n); + + /** + * Set time printing flag. If this flag + * is true, each output line will + * be prepended by the user time used + * by the running program so far. + * + * The previous value of this + * parameter is returned. + */ + bool log_execution_time (const bool flag); + + /** + * Output time differences + * between consecutive logs. If + * this function is invoked with + * true, the time difference + * between the previous log line + * and the recent one is + * printed. If it is invoked with + * false, the accumulated + * time since start of the + * program is printed (default + * behavior). + * + * The measurement of times is + * not changed by this function, + * just the output. + * + * The previous value of this + * parameter is returned. + */ + bool log_time_differences (const bool flag); + + /** + * Write detailed timing + * information. + * + * + */ + void timestamp(); + + /** + * Log the thread id. + */ + bool log_thread_id (const bool flag); + + /** + * Set a threshold for the + * minimal absolute value of + * double values. All numbers + * with a smaller absolute value + * will be printed as zero. + * + * The default value for this + * threshold is zero, + * i.e. numbers are printed + * according to their real value. + * + * This feature is mostly useful + * for automated tests: there, + * one would like to reproduce + * the exact same solution in + * each run of a + * testsuite. However, subtle + * difference in processor, + * operating system, or compiler + * version can lead to + * differences in the last few + * digits of numbers, due to + * different rounding. While one + * can avoid trouble for most + * numbers when comparing with + * stored results by simply + * limiting the accuracy of + * output, this does not hold for + * numbers very close to zero, + * i.e. zero plus accumulated + * round-off. For these numbers, + * already the first digit is + * tainted by round-off. Using + * the present function, it is + * possible to eliminate this + * source of problems, by simply + * writing zero to the output in + * this case. + */ + void threshold_double(const double t); + /** + * The same as + * threshold_double(), but for + * float values. + */ + void threshold_float(const float t); + + /** + * Output a constant something + * through this stream. + */ + template + LogStream &operator << (const T &t); + + /** + * Output double precision + * numbers through this + * stream. + * + * If they are set, this function + * applies the methods for making + * floating point output + * reproducible as discussed in + * the introduction. + */ + LogStream &operator << (const double t); + + /** + * Output single precision + * numbers through this + * stream. + * + * If they are set, this function + * applies the methods for making + * floating point output + * reproducible as discussed in + * the introduction. + */ + LogStream &operator << (const float t); + + /** + * Treat ostream + * manipulators. This passes on + * the whole thing to the + * template function with the + * exception of the + * std::endl + * manipulator, for which special + * action is performed: write the + * temporary stream buffer + * including a header to the file + * and std::cout and + * empty the buffer. + * + * An overload of this function is needed + * anyway, since the compiler can't bind + * manipulators like @p std::endl + * directly to template arguments @p T + * like in the previous general + * template. This is due to the fact that + * @p std::endl is actually an overloaded + * set of functions for @p std::ostream, + * @p std::wostream, and potentially more + * of this kind. This function is + * therefore necessary to pick one + * element from this overload set. + */ + LogStream &operator<< (std::ostream& (*p) (std::ostream &)); + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + std::size_t memory_consumption () const; + + /** + * Exception. + */ + DeclException0(ExcNoFileStreamGiven); + +private: + + /** + * Stack of strings which are printed + * at the beginning of each line to + * allow identification where the + * output was generated. + */ + std::stack prefixes; + + /** + * Default stream, where the output + * is to go to. This stream defaults + * to std::cerr, but can be set to another + * stream through the constructor. + */ + std::ostream *std_out; + + /** + * Pointer to a stream, where a copy of + * the output is to go to. Usually, this + * will be a file stream. + * + * You can set and reset this stream + * by the attach function. + */ + std::ostream *file; + + /** + * Value denoting the number of + * prefixes to be printed to the + * standard output. If more than + * this number of prefixes is + * pushed to the stack, then no + * output will be generated until + * the number of prefixes shrinks + * back below this number. + */ + unsigned int std_depth; + + /** + * Same for the maximum depth of + * prefixes for output to a file. + */ + unsigned int file_depth; + + /** + * Flag for printing execution time. + */ + bool print_utime; + + /** + * Flag for printing time differences. + */ + bool diff_utime; + + /** + * Time of last output line. + */ + double last_time; + + /** + * Threshold for printing double + * values. Every number with + * absolute value less than this + * is printed as zero. + */ + double double_threshold; + + /** + * Threshold for printing float + * values. Every number with + * absolute value less than this + * is printed as zero. + */ + float float_threshold; + + /** + * An offset added to every float + * or double number upon + * output. This is done after the + * number is compared to + * #double_threshold or #float_threshold, + * but before rounding. + * + * This functionality was + * introduced to produce more + * reproducible floating point + * output for regression + * tests. The rationale is, that + * an exact output value is much + * more likely to be 1/8 than + * 0.124997. If we round to two + * digits though, 1/8 becomes + * unreliably either .12 or .13 + * due to machine accuracy. On + * the other hand, if we add a + * something above machine + * accuracy first, we will always + * get .13. + * + * It is safe to leave this + * value equal to zero. For + * regression tests, the function + * test_mode() sets it to a + * reasonable value. + * + * The offset is relative to the + * magnitude of the number. + */ + double offset; + + /** + * Flag for printing thread id. + */ + bool print_thread_id; + + /** + * The value times() returned + * on initialization. + */ + double reference_time_val; + + /** + * The tms structure times() + * filled on initialization. + */ + struct tms reference_tms; + + /** + * Original buffer of + * std::cerr. We store + * the address of that buffer + * when #log_cerr is called, and + * reset it to this value if + * #log_cerr is called a second + * time, or when the destructor + * of this class is run. + */ + std::streambuf *old_cerr; + + /** + * Print head of line. This prints + * optional time information and + * the contents of the prefix stack. + */ + void print_line_head (); + + /** + * Actually do the work of + * writing output. This function + * unifies the work that is + * common to the two + * operator<< functions. + */ + template + void print (const T &t); + /** + * Check if we are on a new line + * and print the header before + * the data. + */ + std::ostringstream &get_stream(); + + /** + * Type of the stream map + */ + typedef std::map > stream_map_type; + + /** + * We generate a stringstream for + * every process that sends log + * messages. + */ + stream_map_type outstreams; }; @@ -621,12 +621,12 @@ inline void LogStream::print (const T &t) { - // if the previous command was an - // std::endl, print the topmost - // prefix and a colon - std::ostringstream& stream = get_stream(); + // if the previous command was an + // std::endl, print the topmost + // prefix and a colon + std::ostringstream &stream = get_stream(); stream << t; - // print the rest of the message + // print the rest of the message // if (prefixes.size() <= std_depth) // *std_out << t; @@ -639,10 +639,10 @@ LogStream::print (const T &t) template inline LogStream & -LogStream::operator<< (const T& t) +LogStream::operator<< (const T &t) { - // do the work that is common to - // the operator<< functions + // do the work that is common to + // the operator<< functions print (t); return *this; } @@ -653,21 +653,20 @@ inline LogStream & LogStream::operator<< (const double t) { - // we have to make sure that we - // don't catch NaN's and +-Inf's - // with the test, because for these - // denormals all comparisons are - // always false. thus, for a NaN, - // both t<=0 and t>=0 are false at - // the same time, which can't be - // said for any other number + // we have to make sure that we + // don't catch NaN's and +-Inf's + // with the test, because for these + // denormals all comparisons are + // always false. thus, for a NaN, + // both t<=0 and t>=0 are false at + // the same time, which can't be + // said for any other number if (! (t<=0) && !(t>=0)) print (t); + else if (std::fabs(t) < double_threshold) + print ('0'); else - if (std::fabs(t) < double_threshold) - print ('0'); - else - print(t*(1.+offset)); + print(t*(1.+offset)); return *this; } @@ -678,30 +677,29 @@ inline LogStream & LogStream::operator<< (const float t) { - // we have to make sure that we - // don't catch NaN's and +-Inf's - // with the test, because for these - // denormals all comparisons are - // always false. thus, for a NaN, - // both t<=0 and t>=0 are false at - // the same time, which can't be - // said for any other number + // we have to make sure that we + // don't catch NaN's and +-Inf's + // with the test, because for these + // denormals all comparisons are + // always false. thus, for a NaN, + // both t<=0 and t>=0 are false at + // the same time, which can't be + // said for any other number if (! (t<=0) && !(t>=0)) print (t); + else if (std::fabs(t) < float_threshold) + print ('0'); else - if (std::fabs(t) < float_threshold) - print ('0'); - else - print(t*(1.+offset)); + print(t*(1.+offset)); return *this; } inline -LogStream::Prefix::Prefix(const std::string& text, LogStream& s) - : - stream(&s) +LogStream::Prefix::Prefix(const std::string &text, LogStream &s) + : + stream(&s) { stream->push(text); } @@ -724,9 +722,9 @@ extern LogStream deallog; inline -LogStream::Prefix::Prefix(const std::string& text) - : - stream(&deallog) +LogStream::Prefix::Prefix(const std::string &text) + : + stream(&deallog) { stream->push(text); } diff --git a/deal.II/include/deal.II/base/memory_consumption.h b/deal.II/include/deal.II/base/memory_consumption.h index 9bd925f4c5..619e3ba5d9 100644 --- a/deal.II/include/deal.II/base/memory_consumption.h +++ b/deal.II/include/deal.II/base/memory_consumption.h @@ -83,334 +83,334 @@ DEAL_II_NAMESPACE_OPEN namespace MemoryConsumption { - /** - * This function is the generic - * interface for determining the - * memory used by an object. If no - * specialization for the type - * T is specified, it will - * call the member function - * t.memory_consumption(). - * - * The library provides - * specializations for all basic - * C++ data types. Every additional - * type needs to have a member - * function memory_consumption() - * callable for constant objects to - * be used in this framework. - */ + /** + * This function is the generic + * interface for determining the + * memory used by an object. If no + * specialization for the type + * T is specified, it will + * call the member function + * t.memory_consumption(). + * + * The library provides + * specializations for all basic + * C++ data types. Every additional + * type needs to have a member + * function memory_consumption() + * callable for constant objects to + * be used in this framework. + */ template inline std::size_t memory_consumption (const T &t); - /** - * Determine the amount of memory - * in bytes consumed by a bool - * variable. - */ + /** + * Determine the amount of memory + * in bytes consumed by a bool + * variable. + */ inline std::size_t memory_consumption (const bool); - /** - * Determine the amount of memory - * in bytes consumed by a char - * variable. - */ + /** + * Determine the amount of memory + * in bytes consumed by a char + * variable. + */ inline std::size_t memory_consumption (const char); - /** - * Determine the amount of memory - * in bytes consumed by a - * short int variable. - */ + /** + * Determine the amount of memory + * in bytes consumed by a + * short int variable. + */ inline std::size_t memory_consumption (const short int); - /** - * Determine the amount of memory - * in bytes consumed by a - * short unsigned int variable. - */ + /** + * Determine the amount of memory + * in bytes consumed by a + * short unsigned int variable. + */ inline std::size_t memory_consumption (const short unsigned int); - /** - * Determine the amount of memory - * in bytes consumed by a int - * variable. - */ + /** + * Determine the amount of memory + * in bytes consumed by a int + * variable. + */ inline std::size_t memory_consumption (const int); - /** - * Determine the amount of memory - * in bytes consumed by a unsigned int - * variable. - */ + /** + * Determine the amount of memory + * in bytes consumed by a unsigned int + * variable. + */ inline std::size_t memory_consumption (const unsigned int); - /** - * Determine the amount of memory - * in bytes consumed by a float - * variable. - */ + /** + * Determine the amount of memory + * in bytes consumed by a float + * variable. + */ inline std::size_t memory_consumption (const float); - /** - * Determine the amount of memory - * in bytes consumed by a double - * variable. - */ + /** + * Determine the amount of memory + * in bytes consumed by a double + * variable. + */ inline std::size_t memory_consumption (const double); - /** - * Determine the amount of memory - * in bytes consumed by a long double - * variable. - */ + /** + * Determine the amount of memory + * in bytes consumed by a long double + * variable. + */ inline std::size_t memory_consumption (const long double); - /** - * Determine the amount of memory - * in bytes consumed by a std::complex - * variable. - */ + /** + * Determine the amount of memory + * in bytes consumed by a std::complex + * variable. + */ template inline std::size_t memory_consumption (const std::complex &); - /** - * Determine an estimate of the - * amount of memory in bytes - * consumed by a std::string - * variable. - */ + /** + * Determine an estimate of the + * amount of memory in bytes + * consumed by a std::string + * variable. + */ inline std::size_t memory_consumption (const std::string &s); - /** - * Determine the amount of memory - * in bytes consumed by a - * std::vector of elements - * of type T by - * recursively calling - * memory_consumption() for each entry. - * - * This function loops over all - * entries of the vector and - * determines their sizes using - * memory_consumption() for each - * v[i]. If the entries - * are of constant size, there - * might be another global function - * memory_consumption() for this - * data type or if there is a - * member function of that class of - * that names that returns a - * constant value and the compiler - * will unroll this loop so that - * the operation is fast. If the - * size of the data elements is - * variable, for example if they do - * memory allocation themselves, - * then the operation will - * necessarily be more expensive. - * - * Using the algorithm, in - * particular the loop over all - * elements, it is possible to also - * compute the memory consumption - * of vectors of vectors, vectors - * of strings, etc, where the - * individual elements may have - * vastly different sizes. - * - * Note that this algorithm also - * takes into account the size of - * elements that are allocated by - * this vector but not currently - * used. - * - * For the most commonly used - * vectors, there are special - * functions that compute the size - * without a loop. This also - * applies for the special case of - * vectors of bools. - */ + /** + * Determine the amount of memory + * in bytes consumed by a + * std::vector of elements + * of type T by + * recursively calling + * memory_consumption() for each entry. + * + * This function loops over all + * entries of the vector and + * determines their sizes using + * memory_consumption() for each + * v[i]. If the entries + * are of constant size, there + * might be another global function + * memory_consumption() for this + * data type or if there is a + * member function of that class of + * that names that returns a + * constant value and the compiler + * will unroll this loop so that + * the operation is fast. If the + * size of the data elements is + * variable, for example if they do + * memory allocation themselves, + * then the operation will + * necessarily be more expensive. + * + * Using the algorithm, in + * particular the loop over all + * elements, it is possible to also + * compute the memory consumption + * of vectors of vectors, vectors + * of strings, etc, where the + * individual elements may have + * vastly different sizes. + * + * Note that this algorithm also + * takes into account the size of + * elements that are allocated by + * this vector but not currently + * used. + * + * For the most commonly used + * vectors, there are special + * functions that compute the size + * without a loop. This also + * applies for the special case of + * vectors of bools. + */ template inline std::size_t memory_consumption (const std::vector &v); - /** - * Estimate the amount of memory - * (in bytes) occupied by a - * C-style array. Since in this - * library we do not usually - * store simple data elements - * like doubles in such - * arrays (but rather use STL - * std::vectors or deal.II - * Vector objects), we do not - * provide specializations like - * for the std::vector arrays, but - * always use the loop over all - * elements. - */ + /** + * Estimate the amount of memory + * (in bytes) occupied by a + * C-style array. Since in this + * library we do not usually + * store simple data elements + * like doubles in such + * arrays (but rather use STL + * std::vectors or deal.II + * Vector objects), we do not + * provide specializations like + * for the std::vector arrays, but + * always use the loop over all + * elements. + */ template inline std::size_t memory_consumption (const T (&v)[N]); - /** - * Specialization of the - * determination of the memory - * consumption of a vector, here - * for a vector of bools. - * - * This is a special case, as the - * bools are not stored - * one-by-one, but as a bit - * field. - */ + /** + * Specialization of the + * determination of the memory + * consumption of a vector, here + * for a vector of bools. + * + * This is a special case, as the + * bools are not stored + * one-by-one, but as a bit + * field. + */ inline std::size_t memory_consumption (const std::vector &v); - /** - * Specialization of the - * determination of the memory - * consumption of a vector, here - * for a vector of ints. - */ + /** + * Specialization of the + * determination of the memory + * consumption of a vector, here + * for a vector of ints. + */ inline std::size_t memory_consumption (const std::vector &v); - /** - * Specialization of the - * determination of the memory - * consumption of a vector, here - * for a vector of doubles. - */ + /** + * Specialization of the + * determination of the memory + * consumption of a vector, here + * for a vector of doubles. + */ inline std::size_t memory_consumption (const std::vector &v); - /** - * Specialization of the - * determination of the memory - * consumption of a vector, here - * for a vector of floats. - */ + /** + * Specialization of the + * determination of the memory + * consumption of a vector, here + * for a vector of floats. + */ inline std::size_t memory_consumption (const std::vector &v); - /** - * Specialization of the - * determination of the memory - * consumption of a vector, here - * for a vector of chars. - */ + /** + * Specialization of the + * determination of the memory + * consumption of a vector, here + * for a vector of chars. + */ inline std::size_t memory_consumption (const std::vector &v); - /** - * Specialization of the - * determination of the memory - * consumption of a vector, here - * for a vector of unsigned chars. - */ + /** + * Specialization of the + * determination of the memory + * consumption of a vector, here + * for a vector of unsigned chars. + */ inline std::size_t memory_consumption (const std::vector &v); - /** - * Specialization of the - * determination of the memory - * consumption of a vector, here - * for a vector of pointers. - */ + /** + * Specialization of the + * determination of the memory + * consumption of a vector, here + * for a vector of pointers. + */ template inline std::size_t memory_consumption (const std::vector &v); - /** - * Specialization of the - * determination of the memory - * consumption of a vector, here - * for a vector of strings. This - * function is not necessary from a - * strict C++ viewpoint, since it - * could be generated, but is - * necessary for compatibility with - * IBM's xlC 5.0 compiler, and - * doesn't harm for other compilers - * as well. - */ + /** + * Specialization of the + * determination of the memory + * consumption of a vector, here + * for a vector of strings. This + * function is not necessary from a + * strict C++ viewpoint, since it + * could be generated, but is + * necessary for compatibility with + * IBM's xlC 5.0 compiler, and + * doesn't harm for other compilers + * as well. + */ std::size_t memory_consumption (const std::vector &v); - /** - * Determine an estimate of the - * amount of memory in bytes - * consumed by a pair of values. - */ + /** + * Determine an estimate of the + * amount of memory in bytes + * consumed by a pair of values. + */ template inline std::size_t memory_consumption (const std::pair &p); - /** - * Return the amount of memory - * used by a pointer. - * - * @note This returns the size of - * the pointer, not of the object - * pointed to. - */ + /** + * Return the amount of memory + * used by a pointer. + * + * @note This returns the size of + * the pointer, not of the object + * pointed to. + */ template inline - std::size_t memory_consumption (const T * const); + std::size_t memory_consumption (const T *const); - /** - * Return the amount of memory - * used by a pointer. - * - * @note This returns the size of - * the pointer, not of the object - * pointed to. - */ + /** + * Return the amount of memory + * used by a pointer. + * + * @note This returns the size of + * the pointer, not of the object + * pointed to. + */ template inline - std::size_t memory_consumption (T * const); - - /** - * Return the amount of memory - * used by a void pointer. - * - * Note that we needed this - * function since void is no - * type and a void* is thus - * not caught by the general - * T* template function - * above. - * - * @note This returns the size of - * the pointer, not of the object - * pointed to. - */ - inline - std::size_t memory_consumption (void * const); - - /** - * Return the amount of memory used - * by a shared pointer. - * - * @note This returns the size of - * the pointer, not of the object - * pointed to. - */ + std::size_t memory_consumption (T *const); + + /** + * Return the amount of memory + * used by a void pointer. + * + * Note that we needed this + * function since void is no + * type and a void* is thus + * not caught by the general + * T* template function + * above. + * + * @note This returns the size of + * the pointer, not of the object + * pointed to. + */ + inline + std::size_t memory_consumption (void *const); + + /** + * Return the amount of memory used + * by a shared pointer. + * + * @note This returns the size of + * the pointer, not of the object + * pointed to. + */ template inline std::size_t memory_consumption (const std_cxx1x::shared_ptr &); @@ -610,9 +610,9 @@ namespace MemoryConsumption template inline std::size_t - memory_consumption (const T * const) + memory_consumption (const T *const) { - return sizeof(T*); + return sizeof(T *); } @@ -620,18 +620,18 @@ namespace MemoryConsumption template inline std::size_t - memory_consumption (T * const) + memory_consumption (T *const) { - return sizeof(T*); + return sizeof(T *); } inline std::size_t - memory_consumption (void * const) + memory_consumption (void *const) { - return sizeof(void*); + return sizeof(void *); } diff --git a/deal.II/include/deal.II/base/mg_level_object.h b/deal.II/include/deal.II/base/mg_level_object.h index 0d2a58c6f2..89dae1ab30 100644 --- a/deal.II/include/deal.II/base/mg_level_object.h +++ b/deal.II/include/deal.II/base/mg_level_object.h @@ -37,91 +37,91 @@ DEAL_II_NAMESPACE_OPEN template class MGLevelObject : public Subscriptor { - public: - /** - * Constructor allowing to - * initialize the number of - * levels. By default, the object - * is created empty. - */ - MGLevelObject (const unsigned int minlevel = 0, - const unsigned int maxlevel = 0); - - /** - * Access object on level @p level. - */ - Object & operator[] (const unsigned int level); - - /** - * Access object on level - * @p level. Constant version. - */ - const Object & operator[] (const unsigned int level) const; - - /** - * Delete all previous contents - * of this object and reset its - * size according to the values - * of @p new_minlevel and - * @p new_maxlevel. - */ - void resize (const unsigned int new_minlevel, - const unsigned int new_maxlevel); - - /** - * Call operator = (s) - * on all objects stored by this - * object. This is particularly - * useful for - * e.g. Object==Vector@ - */ - MGLevelObject & operator = (const double d); - - /** - * Call @p clear on all objects - * stored by this object. This - * function is only implemented - * for some @p Object classes, - * e.g. the PreconditionBlockSOR - * and similar classes. - */ - void clear(); - - /** - * Coarsest level for multigrid. - */ - unsigned int min_level () const; - - /** - * Finest level for multigrid. - */ - unsigned int max_level () const; - - /** - * @deprecated Replaced by min_level() - */ - unsigned int get_minlevel () const; - - /** - * @deprecated Replaced by max_level() - */ - unsigned int get_maxlevel () const; - - /** - * Memory used by this object. - */ - std::size_t memory_consumption () const; - - private: - /** - * Level of first component. - */ - unsigned int minlevel; - - /** - * Array of the objects to be held. - */ - std::vector > objects; +public: + /** + * Constructor allowing to + * initialize the number of + * levels. By default, the object + * is created empty. + */ + MGLevelObject (const unsigned int minlevel = 0, + const unsigned int maxlevel = 0); + + /** + * Access object on level @p level. + */ + Object &operator[] (const unsigned int level); + + /** + * Access object on level + * @p level. Constant version. + */ + const Object &operator[] (const unsigned int level) const; + + /** + * Delete all previous contents + * of this object and reset its + * size according to the values + * of @p new_minlevel and + * @p new_maxlevel. + */ + void resize (const unsigned int new_minlevel, + const unsigned int new_maxlevel); + + /** + * Call operator = (s) + * on all objects stored by this + * object. This is particularly + * useful for + * e.g. Object==Vector@ + */ + MGLevelObject &operator = (const double d); + + /** + * Call @p clear on all objects + * stored by this object. This + * function is only implemented + * for some @p Object classes, + * e.g. the PreconditionBlockSOR + * and similar classes. + */ + void clear(); + + /** + * Coarsest level for multigrid. + */ + unsigned int min_level () const; + + /** + * Finest level for multigrid. + */ + unsigned int max_level () const; + + /** + * @deprecated Replaced by min_level() + */ + unsigned int get_minlevel () const; + + /** + * @deprecated Replaced by max_level() + */ + unsigned int get_maxlevel () const; + + /** + * Memory used by this object. + */ + std::size_t memory_consumption () const; + +private: + /** + * Level of first component. + */ + unsigned int minlevel; + + /** + * Array of the objects to be held. + */ + std::vector > objects; }; @@ -131,8 +131,8 @@ class MGLevelObject : public Subscriptor template MGLevelObject::MGLevelObject(const unsigned int min, const unsigned int max) - : - minlevel(0) + : + minlevel(0) { resize (min, max); } @@ -164,10 +164,10 @@ MGLevelObject::resize (const unsigned int new_minlevel, const unsigned int new_maxlevel) { Assert (new_minlevel <= new_maxlevel, ExcInternalError()); - // note that on clear(), the - // shared_ptr class takes care of - // deleting the object it points to - // by itself + // note that on clear(), the + // shared_ptr class takes care of + // deleting the object it points to + // by itself objects.clear (); minlevel = new_minlevel; diff --git a/deal.II/include/deal.II/base/mpi.h b/deal.II/include/deal.II/base/mpi.h index 6cf6a981ae..f054dceb3a 100644 --- a/deal.II/include/deal.II/base/mpi.h +++ b/deal.II/include/deal.II/base/mpi.h @@ -19,9 +19,9 @@ #if defined(DEAL_II_COMPILER_SUPPORTS_MPI) || defined(DEAL_II_USE_PETSC) # include #else - // without MPI, we would still like to use - // some constructs with MPI data - // types. Therefore, create some dummies +// without MPI, we would still like to use +// some constructs with MPI data +// types. Therefore, create some dummies typedef int MPI_Comm; const int MPI_COMM_SELF = 0; typedef int MPI_Datatype; @@ -42,298 +42,298 @@ DEAL_II_NAMESPACE_OPEN namespace Utilities { - /** - * A namespace for utility functions that - * abstract certain operations using the - * Message Passing Interface (MPI) or - * provide fallback operations in - * case deal.II is configured not to use - * MPI at all. - * - * @ingroup utilities - */ + /** + * A namespace for utility functions that + * abstract certain operations using the + * Message Passing Interface (MPI) or + * provide fallback operations in + * case deal.II is configured not to use + * MPI at all. + * + * @ingroup utilities + */ namespace MPI { - /** - * Return the number of MPI processes - * there exist in the given communicator - * object. If this is a sequential job, - * it returns 1. - */ + /** + * Return the number of MPI processes + * there exist in the given communicator + * object. If this is a sequential job, + * it returns 1. + */ unsigned int n_mpi_processes (const MPI_Comm &mpi_communicator); - /** - * Return the number of the present MPI - * process in the space of processes - * described by the given - * communicator. This will be a unique - * value for each process between zero - * and (less than) the number of all - * processes (given by - * get_n_mpi_processes()). - */ + /** + * Return the number of the present MPI + * process in the space of processes + * described by the given + * communicator. This will be a unique + * value for each process between zero + * and (less than) the number of all + * processes (given by + * get_n_mpi_processes()). + */ unsigned int this_mpi_process (const MPI_Comm &mpi_communicator); - /** - * Consider an unstructured - * communication pattern where - * every process in an MPI - * universe wants to send some - * data to a subset of the other - * processors. To do that, the - * other processors need to know - * who to expect messages - * from. This function computes - * this information. - * - * @param mpi_comm A communicator - * that describes the processors - * that are going to communicate - * with each other. - * - * @param destinations The list - * of processors the current - * process wants to send - * information to. This list need - * not be sorted in any way. If - * it contains duplicate entries - * that means that multiple - * messages are intended for a - * given destination. - * - * @return A list of processors - * that have indicated that they - * want to send something to the - * current processor. The - * resulting list is not - * sorted. It may contain - * duplicate entries if - * processors enter the same - * destination more than once in - * their destinations list. - */ + /** + * Consider an unstructured + * communication pattern where + * every process in an MPI + * universe wants to send some + * data to a subset of the other + * processors. To do that, the + * other processors need to know + * who to expect messages + * from. This function computes + * this information. + * + * @param mpi_comm A communicator + * that describes the processors + * that are going to communicate + * with each other. + * + * @param destinations The list + * of processors the current + * process wants to send + * information to. This list need + * not be sorted in any way. If + * it contains duplicate entries + * that means that multiple + * messages are intended for a + * given destination. + * + * @return A list of processors + * that have indicated that they + * want to send something to the + * current processor. The + * resulting list is not + * sorted. It may contain + * duplicate entries if + * processors enter the same + * destination more than once in + * their destinations list. + */ std::vector - compute_point_to_point_communication_pattern (const MPI_Comm & mpi_comm, - const std::vector & destinations); - - /** - * Given a communicator, generate a new - * communicator that contains the same - * set of processors but that has a - * different, unique identifier. - * - * This functionality can be used to - * ensure that different objects, such as - * distributed matrices, each have unique - * communicators over which they can - * interact without interfering with each - * other. - * - * When no longer needed, the - * communicator created here needs to - * be destroyed using - * MPI_Comm_free. - */ + compute_point_to_point_communication_pattern (const MPI_Comm &mpi_comm, + const std::vector &destinations); + + /** + * Given a communicator, generate a new + * communicator that contains the same + * set of processors but that has a + * different, unique identifier. + * + * This functionality can be used to + * ensure that different objects, such as + * distributed matrices, each have unique + * communicators over which they can + * interact without interfering with each + * other. + * + * When no longer needed, the + * communicator created here needs to + * be destroyed using + * MPI_Comm_free. + */ MPI_Comm duplicate_communicator (const MPI_Comm &mpi_communicator); - /** - * Return the sum over all processors of the value @p t. This function - * is collective over all processors given in the communicator. If - * deal.II is not configured for use of MPI, this function simply - * returns the value of @p t. This function corresponds to the - * MPI_Allreduce function, i.e. all processors receive - * the result of this operation. - * - * @note This function is only implemented for certain template - * arguments T, namely float, double, int, - * unsigned int. - */ + /** + * Return the sum over all processors of the value @p t. This function + * is collective over all processors given in the communicator. If + * deal.II is not configured for use of MPI, this function simply + * returns the value of @p t. This function corresponds to the + * MPI_Allreduce function, i.e. all processors receive + * the result of this operation. + * + * @note This function is only implemented for certain template + * arguments T, namely float, double, int, + * unsigned int. + */ template T sum (const T &t, const MPI_Comm &mpi_communicator); - /** - * Like the previous function, - * but take the sums over the - * elements of an array - * of length N. In other words, - * the i-th element of the - * results array is the sum over - * the i-th entries of the input - * arrays from each processor. - */ + /** + * Like the previous function, + * but take the sums over the + * elements of an array + * of length N. In other words, + * the i-th element of the + * results array is the sum over + * the i-th entries of the input + * arrays from each processor. + */ template inline void sum (const T (&values)[N], const MPI_Comm &mpi_communicator, T (&sums)[N]); - /** - * Like the previous function, - * but take the sums over the - * elements of a std::vector. In other words, - * the i-th element of the - * results array is the sum over - * the i-th entries of the input - * arrays from each processor. - */ + /** + * Like the previous function, + * but take the sums over the + * elements of a std::vector. In other words, + * the i-th element of the + * results array is the sum over + * the i-th entries of the input + * arrays from each processor. + */ template inline void sum (const std::vector &values, const MPI_Comm &mpi_communicator, std::vector &sums); - /** - * Return the maximum over all processors of the value @p t. This function - * is collective over all processors given in the communicator. If - * deal.II is not configured for use of MPI, this function simply - * returns the value of @p t. This function corresponds to the - * MPI_Allreduce function, i.e. all processors receive - * the result of this operation. - * - * @note This function is only implemented for certain template - * arguments T, namely float, double, int, - * unsigned int. - */ + /** + * Return the maximum over all processors of the value @p t. This function + * is collective over all processors given in the communicator. If + * deal.II is not configured for use of MPI, this function simply + * returns the value of @p t. This function corresponds to the + * MPI_Allreduce function, i.e. all processors receive + * the result of this operation. + * + * @note This function is only implemented for certain template + * arguments T, namely float, double, int, + * unsigned int. + */ template T max (const T &t, const MPI_Comm &mpi_communicator); - /** - * Like the previous function, - * but take the maxima over the - * elements of an array - * of length N. In other words, - * the i-th element of the - * results array is the maximum of - * the i-th entries of the input - * arrays from each processor. - */ + /** + * Like the previous function, + * but take the maxima over the + * elements of an array + * of length N. In other words, + * the i-th element of the + * results array is the maximum of + * the i-th entries of the input + * arrays from each processor. + */ template inline void max (const T (&values)[N], const MPI_Comm &mpi_communicator, T (&maxima)[N]); - /** - * Like the previous function, - * but take the maximum over the - * elements of a std::vector. In other words, - * the i-th element of the - * results array is the maximum over - * the i-th entries of the input - * arrays from each processor. - */ + /** + * Like the previous function, + * but take the maximum over the + * elements of a std::vector. In other words, + * the i-th element of the + * results array is the maximum over + * the i-th entries of the input + * arrays from each processor. + */ template inline void max (const std::vector &values, const MPI_Comm &mpi_communicator, std::vector &maxima); - /** - * Data structure to store the result of - * min_max_avg(). - */ + /** + * Data structure to store the result of + * min_max_avg(). + */ struct MinMaxAvg { - double sum; - double min; - double max; - unsigned int min_index; - unsigned int max_index; - double avg; + double sum; + double min; + double max; + unsigned int min_index; + unsigned int max_index; + double avg; }; - /** - * Returns sum, average, minimum, - * maximum, processor id of minimum and - * maximum as a collective operation of - * on the given MPI communicator @param - * mpi_communicator . Each processor's - * value is given in @param my_value and - * the result will be returned in @p - * result . The result is available on all - * machines. - */ + /** + * Returns sum, average, minimum, + * maximum, processor id of minimum and + * maximum as a collective operation of + * on the given MPI communicator @param + * mpi_communicator . Each processor's + * value is given in @param my_value and + * the result will be returned in @p + * result . The result is available on all + * machines. + */ MinMaxAvg min_max_avg (const double my_value, const MPI_Comm &mpi_communicator); - /** - * A class that is used to initialize the - * MPI system at the beginning of a - * program and to shut it down again at - * the end. - * - * If deal.II is configured with PETSc, - * the library will also be initialized - * in the beginning and destructed at the - * end automatically (internally by calling - * PetscInitialize() and PetscFinalize()). - * - * If a program uses MPI one would - * typically just create an object of - * this type at the beginning of - * main(). The constructor - * of this class then runs - * MPI_Init() with the given - * arguments. At the end of the program, - * the compiler will invoke the - * destructor of this object which in - * turns calls MPI_Finalize - * to shut down the MPI system. - * - * This class is used in step-32, for example. - */ + /** + * A class that is used to initialize the + * MPI system at the beginning of a + * program and to shut it down again at + * the end. + * + * If deal.II is configured with PETSc, + * the library will also be initialized + * in the beginning and destructed at the + * end automatically (internally by calling + * PetscInitialize() and PetscFinalize()). + * + * If a program uses MPI one would + * typically just create an object of + * this type at the beginning of + * main(). The constructor + * of this class then runs + * MPI_Init() with the given + * arguments. At the end of the program, + * the compiler will invoke the + * destructor of this object which in + * turns calls MPI_Finalize + * to shut down the MPI system. + * + * This class is used in step-32, for example. + */ class MPI_InitFinalize { - public: - /** - * Constructor. Takes the arguments - * from the command line (in case of - * MPI, the number of processes is - * specified there), and sets up a - * respective communicator by calling - * MPI_Init(). This - * constructor can only be called once - * in a program, since MPI cannot be - * initialized twice. - */ - MPI_InitFinalize (int &argc, - char** &argv); - - /** - * Destructor. Calls - * MPI_Finalize() in - * case this class owns the MPI - * process. - */ - ~MPI_InitFinalize(); - - private: - /** - * This flag tells the class - * whether it owns the MPI - * process (i.e., it has been - * constructed using the - * argc/argv input, or it has - * been copied). In the former - * case, the command - * MPI_Finalize() will - * be called at destruction. - */ - const bool owns_mpi; + public: + /** + * Constructor. Takes the arguments + * from the command line (in case of + * MPI, the number of processes is + * specified there), and sets up a + * respective communicator by calling + * MPI_Init(). This + * constructor can only be called once + * in a program, since MPI cannot be + * initialized twice. + */ + MPI_InitFinalize (int &argc, + char ** &argv); + + /** + * Destructor. Calls + * MPI_Finalize() in + * case this class owns the MPI + * process. + */ + ~MPI_InitFinalize(); + + private: + /** + * This flag tells the class + * whether it owns the MPI + * process (i.e., it has been + * constructed using the + * argc/argv input, or it has + * been copied). In the former + * case, the command + * MPI_Finalize() will + * be called at destruction. + */ + const bool owns_mpi; }; namespace internal { #ifdef DEAL_II_COMPILER_SUPPORTS_MPI - /** - * Return the corresponding MPI data - * type id for the argument given. - */ + /** + * Return the corresponding MPI data + * type id for the argument given. + */ inline MPI_Datatype mpi_type_id (const int *) { return MPI_INT; @@ -385,7 +385,7 @@ namespace Utilities { #ifdef DEAL_II_COMPILER_SUPPORTS_MPI T sum; - MPI_Allreduce (const_cast(static_cast(&t)), + MPI_Allreduce (const_cast(static_cast(&t)), &sum, 1, internal::mpi_type_id(&t), MPI_SUM, mpi_communicator); return sum; @@ -403,7 +403,7 @@ namespace Utilities T (&sums)[N]) { #ifdef DEAL_II_COMPILER_SUPPORTS_MPI - MPI_Allreduce (const_cast(static_cast(&values[0])), + MPI_Allreduce (const_cast(static_cast(&values[0])), &sums[0], N, internal::mpi_type_id(values), MPI_SUM, mpi_communicator); #else @@ -422,8 +422,8 @@ namespace Utilities { #ifdef DEAL_II_COMPILER_SUPPORTS_MPI sums.resize (values.size()); - MPI_Allreduce (const_cast(static_cast(&values[0])), - &sums[0], values.size(), internal::mpi_type_id((T*)0), MPI_SUM, + MPI_Allreduce (const_cast(static_cast(&values[0])), + &sums[0], values.size(), internal::mpi_type_id((T *)0), MPI_SUM, mpi_communicator); #else (void)mpi_communicator; @@ -439,7 +439,7 @@ namespace Utilities { #ifdef DEAL_II_COMPILER_SUPPORTS_MPI T sum; - MPI_Allreduce (const_cast(static_cast(&t)), + MPI_Allreduce (const_cast(static_cast(&t)), &sum, 1, internal::mpi_type_id(&t), MPI_MAX, mpi_communicator); return sum; @@ -457,7 +457,7 @@ namespace Utilities T (&maxima)[N]) { #ifdef DEAL_II_COMPILER_SUPPORTS_MPI - MPI_Allreduce (const_cast(static_cast(&values[0])), + MPI_Allreduce (const_cast(static_cast(&values[0])), &maxima[0], N, internal::mpi_type_id(values), MPI_MAX, mpi_communicator); #else @@ -476,8 +476,8 @@ namespace Utilities { #ifdef DEAL_II_COMPILER_SUPPORTS_MPI maxima.resize (values.size()); - MPI_Allreduce (const_cast(static_cast(&values[0])), - &maxima[0], values.size(), internal::mpi_type_id((T*)0), MPI_MAX, + MPI_Allreduce (const_cast(static_cast(&values[0])), + &maxima[0], values.size(), internal::mpi_type_id((T *)0), MPI_MAX, mpi_communicator); #else (void)mpi_communicator; diff --git a/deal.II/include/deal.II/base/multithread_info.h b/deal.II/include/deal.II/base/multithread_info.h index 6d421b1959..e0d74cae3a 100644 --- a/deal.II/include/deal.II/base/multithread_info.h +++ b/deal.II/include/deal.II/base/multithread_info.h @@ -39,82 +39,82 @@ DEAL_II_NAMESPACE_OPEN */ class MultithreadInfo { - public: - /** - * The constructor determines the - * number of CPUs in the system. - * At the moment detection of - * CPUs is only implemented on - * Linux computers with the /proc - * filesystem and on Sun - * machines. The number of CPUs - * present is set to one if - * detection failed or if - * detection is not supported. - */ - MultithreadInfo (); - - /** - * The number of CPUs in the - * system. It is one if - * detection is not implemented - * or failed. - * - * If it is one, although you - * are on a multi-processor - * machine, please refer to the - * documentation in - * multithread_info.cc - * near to the error directive. - */ - const unsigned int n_cpus; - - /** - * The number of threads to use as - * a default value for all functions - * that support multithreading. - * At start time this is n_cpus or - * one, if detection of the number - * of CPUs is not possible. - */ - unsigned int n_default_threads; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. Since sometimes - * the size of objects can - * not be determined exactly - * (for example: what is the - * memory consumption of an - * STL std::map type with a - * certain number of - * elements?), this is only - * an estimate. however often - * quite close to the true - * value. - */ - static std::size_t memory_consumption (); - - /** - * Exception - */ - DeclException0(ExcProcNotPresent); - - private: - - /** - * Private function to determine - * the number of CPUs. - * Implementation for Linux, OSF, - * SGI, and Sun machines; if no - * detection of the number of CPUs is - * supported, or if detection - * fails, this function returns - * one. - */ - static unsigned int get_n_cpus(); +public: + /** + * The constructor determines the + * number of CPUs in the system. + * At the moment detection of + * CPUs is only implemented on + * Linux computers with the /proc + * filesystem and on Sun + * machines. The number of CPUs + * present is set to one if + * detection failed or if + * detection is not supported. + */ + MultithreadInfo (); + + /** + * The number of CPUs in the + * system. It is one if + * detection is not implemented + * or failed. + * + * If it is one, although you + * are on a multi-processor + * machine, please refer to the + * documentation in + * multithread_info.cc + * near to the error directive. + */ + const unsigned int n_cpus; + + /** + * The number of threads to use as + * a default value for all functions + * that support multithreading. + * At start time this is n_cpus or + * one, if detection of the number + * of CPUs is not possible. + */ + unsigned int n_default_threads; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. Since sometimes + * the size of objects can + * not be determined exactly + * (for example: what is the + * memory consumption of an + * STL std::map type with a + * certain number of + * elements?), this is only + * an estimate. however often + * quite close to the true + * value. + */ + static std::size_t memory_consumption (); + + /** + * Exception + */ + DeclException0(ExcProcNotPresent); + +private: + + /** + * Private function to determine + * the number of CPUs. + * Implementation for Linux, OSF, + * SGI, and Sun machines; if no + * detection of the number of CPUs is + * supported, or if detection + * fails, this function returns + * one. + */ + static unsigned int get_n_cpus(); }; diff --git a/deal.II/include/deal.II/base/named_data.h b/deal.II/include/deal.II/base/named_data.h index 0623378c7f..6d4c7b3af1 100644 --- a/deal.II/include/deal.II/base/named_data.h +++ b/deal.II/include/deal.II/base/named_data.h @@ -42,130 +42,130 @@ DEAL_II_NAMESPACE_OPEN template class NamedData : public Subscriptor { - public: - /** Standard constructor creating - * an empty object. - */ - NamedData(); - - /** - * Assignment operator, copying - * conversible data from another - * object. - */ - template - NamedData& operator = (const NamedData& other); - - /** - * \name Adding members - */ +public: + /** Standard constructor creating + * an empty object. + */ + NamedData(); + + /** + * Assignment operator, copying + * conversible data from another + * object. + */ + template + NamedData &operator = (const NamedData &other); + + /** + * \name Adding members + */ //@{ - /** - * Add a new data item to the end - * of the collection. - */ - void add(DATA& v, const std::string& name); - - /** - * Add a new constant data item - * to the end of the collection - * and make the collection - * constant. - */ - void add(const DATA& v, const std::string& name); - - /** - * Merge the data of another - * NamedData to the end of this - * object. - * - * If the other object had - * #is_constant set, so will have - * this object after merge. - */ - template - void merge(NamedData&); - /** - * Merge the data of another - * NamedData to the end of this - * object. - * - * After this operation, all - * data in this object will be - * treated as const. - */ - template - void merge(const NamedData&); + /** + * Add a new data item to the end + * of the collection. + */ + void add(DATA &v, const std::string &name); + + /** + * Add a new constant data item + * to the end of the collection + * and make the collection + * constant. + */ + void add(const DATA &v, const std::string &name); + + /** + * Merge the data of another + * NamedData to the end of this + * object. + * + * If the other object had + * #is_constant set, so will have + * this object after merge. + */ + template + void merge(NamedData &); + /** + * Merge the data of another + * NamedData to the end of this + * object. + * + * After this operation, all + * data in this object will be + * treated as const. + */ + template + void merge(const NamedData &); //@} - /** - * \name Accessing and querying - * contents - */ + /** + * \name Accessing and querying + * contents + */ //@{ /// Number of stored data objects. - unsigned int size() const; - - /** - * @brief Access to stored data - * object by index. - * - * @note This function throws an - * exception, if is_const() - * returns true. In such - * a case, either cast the - * NamedData object to a const - * reference, or use the function - * read() instead of this operator. - */ - DATA& operator() (unsigned int i); + unsigned int size() const; + + /** + * @brief Access to stored data + * object by index. + * + * @note This function throws an + * exception, if is_const() + * returns true. In such + * a case, either cast the + * NamedData object to a const + * reference, or use the function + * read() instead of this operator. + */ + DATA &operator() (unsigned int i); /// Read-only access to stored data object by index. - const DATA& operator() (unsigned int i) const; + const DATA &operator() (unsigned int i) const; /// Read only access for a non-const object. - const DATA& read (unsigned int i) const; + const DATA &read (unsigned int i) const; /// Name of object at index. - const std::string& name(unsigned int i) const; + const std::string &name(unsigned int i) const; /// Find index of a named object - unsigned int find(const std::string& name) const; + unsigned int find(const std::string &name) const; /// Returns true if this object contains constant data - bool is_const () const; + bool is_const () const; /// List names of stored objects - template - void print(OUT& o) const; + template + void print(OUT &o) const; //@} - /** - * Exception indicating that a - * function expected a vector - * to have a certain name, but - * NamedData had a different - * name in that position. - */ - DeclException2(ExcNameMismatch, int, std::string, - << "Name at position " << arg1 << " is not equal to " << arg2); - - /** - * Exception indicating that read - * access to stored data was - * attempted although the - * NamedData object contains - * const data and #is_constant - * was true. - */ - DeclException0(ExcConstantObject); - - private: - /// True if the object is to be treated constant - bool is_constant; - /// The actual data stored - std::vector data; - - /// Names for the data - std::vector names; + /** + * Exception indicating that a + * function expected a vector + * to have a certain name, but + * NamedData had a different + * name in that position. + */ + DeclException2(ExcNameMismatch, int, std::string, + << "Name at position " << arg1 << " is not equal to " << arg2); + + /** + * Exception indicating that read + * access to stored data was + * attempted although the + * NamedData object contains + * const data and #is_constant + * was true. + */ + DeclException0(ExcConstantObject); + +private: + /// True if the object is to be treated constant + bool is_constant; + /// The actual data stored + std::vector data; + + /// Names for the data + std::vector names; }; @@ -179,56 +179,56 @@ class NamedData : public Subscriptor */ class NamedSelection { - public: - /** - * Add a new name to be searched - * for in NamedData. - * - * @note Names will be added to - * the end of the current list. - */ - void add (const std::string& name); - - /** - * Create the index vector - * pointing into the NamedData - * object. - */ - template - void initialize(const NamedData& data); - - /** - * The number of names in this - * object. This function may be - * used whether initialize() was - * called before or not. - */ - unsigned int size() const; - - /** - * Return the corresponding index - * in the NamedData object - * supplied to the last - * initialize(). It is an error - * if initialize() has not been - * called before. - * - * Indices are in the same order - * as the calls to add(). - */ - unsigned int operator() (unsigned int i) const; - - private: - /** - * The selected names. - */ - std::vector names; - /** - * The index map generated by - * initialize() and accessed by - * operator(). - */ - std::vector indices; +public: + /** + * Add a new name to be searched + * for in NamedData. + * + * @note Names will be added to + * the end of the current list. + */ + void add (const std::string &name); + + /** + * Create the index vector + * pointing into the NamedData + * object. + */ + template + void initialize(const NamedData &data); + + /** + * The number of names in this + * object. This function may be + * used whether initialize() was + * called before or not. + */ + unsigned int size() const; + + /** + * Return the corresponding index + * in the NamedData object + * supplied to the last + * initialize(). It is an error + * if initialize() has not been + * called before. + * + * Indices are in the same order + * as the calls to add(). + */ + unsigned int operator() (unsigned int i) const; + +private: + /** + * The selected names. + */ + std::vector names; + /** + * The index map generated by + * initialize() and accessed by + * operator(). + */ + std::vector indices; }; @@ -237,15 +237,15 @@ class NamedSelection template inline NamedData::NamedData() - : - is_constant(false) + : + is_constant(false) {} template inline void -NamedData::add(DATA& v, const std::string& n) +NamedData::add(DATA &v, const std::string &n) { Assert(!is_constant, ExcConstantObject()); names.push_back(n); @@ -256,10 +256,10 @@ NamedData::add(DATA& v, const std::string& n) template inline void -NamedData::add(const DATA& v, const std::string& n) +NamedData::add(const DATA &v, const std::string &n) { Assert(!is_constant, ExcConstantObject()); - DATA& aux = const_cast(v); + DATA &aux = const_cast(v); data.push_back(aux); names.push_back(n); is_constant = true; @@ -270,11 +270,11 @@ template template inline void -NamedData::merge(NamedData& other) +NamedData::merge(NamedData &other) { Assert(!is_constant, ExcConstantObject()); - for (unsigned int i=0;i template inline void -NamedData::merge(const NamedData& other) +NamedData::merge(const NamedData &other) { Assert(!is_constant, ExcConstantObject()); - for (unsigned int i=0;i::merge(const NamedData& other) template template inline -NamedData& -NamedData::operator= (const NamedData& other) +NamedData & +NamedData::operator= (const NamedData &other) { is_constant = false; merge(other); @@ -334,7 +334,7 @@ NamedData::is_const() const template inline -DATA& +DATA & NamedData::operator() (unsigned int i) { Assert(!is_constant, ExcConstantObject()); @@ -345,7 +345,7 @@ NamedData::operator() (unsigned int i) template inline -const DATA& +const DATA & NamedData::operator() (unsigned int i) const { AssertIndexRange(i, size()); @@ -355,7 +355,7 @@ NamedData::operator() (unsigned int i) const template inline -const DATA& +const DATA & NamedData::read (unsigned int i) const { AssertIndexRange(i, size()); @@ -365,7 +365,7 @@ NamedData::read (unsigned int i) const template inline -const std::string& +const std::string & NamedData::name(unsigned int i) const { AssertIndexRange(i, size()); @@ -376,10 +376,10 @@ NamedData::name(unsigned int i) const template inline unsigned int -NamedData::find (const std::string& name) const +NamedData::find (const std::string &name) const { const std::vector::const_iterator - i = std::find(names.begin(), names.end(), name); + i = std::find(names.begin(), names.end(), name); if (i == names.end()) return deal_II_numbers::invalid_unsigned_int; return i - names.begin(); @@ -390,10 +390,10 @@ template template inline void -NamedData::print(OUT& o) const +NamedData::print(OUT &o) const { o << "NamedData:"; - for (unsigned int i=0;i::print(OUT& o) const inline void -NamedSelection::add(const std::string& s) +NamedSelection::add(const std::string &s) { names.push_back(s); } @@ -409,10 +409,10 @@ NamedSelection::add(const std::string& s) template inline void -NamedSelection::initialize(const NamedData& data) +NamedSelection::initialize(const NamedData &data) { indices.resize(names.size()); - for (unsigned int i=0;idouble. In other - * words, if you give a very - * large number of type - * long double, this - * function may return - * false even if the - * number is finite with respect - * to type long - * double. - */ + /** + * Return @p true if the given + * value is a finite floating + * point number, i.e. is neither + * plus or minus infinity nor NaN + * (not a number). + * + * Note that the argument type of + * this function is + * double. In other + * words, if you give a very + * large number of type + * long double, this + * function may return + * false even if the + * number is finite with respect + * to type long + * double. + */ bool is_finite (const double x); - /** - * Return @p true if real and - * imaginary parts of the given - * complex number are finite. - */ + /** + * Return @p true if real and + * imaginary parts of the given + * complex number are finite. + */ bool is_finite (const std::complex &x); - /** - * Return @p true if real and - * imaginary parts of the given - * complex number are finite. - */ + /** + * Return @p true if real and + * imaginary parts of the given + * complex number are finite. + */ bool is_finite (const std::complex &x); - /** - * Return @p true if real and - * imaginary parts of the given - * complex number are finite. - * - * Again may not work correctly if - * real or imaginary parts are very - * large numbers that are infinite in - * terms of double, but - * finite with respect to - * long double. - */ + /** + * Return @p true if real and + * imaginary parts of the given + * complex number are finite. + * + * Again may not work correctly if + * real or imaginary parts are very + * large numbers that are infinite in + * terms of double, but + * finite with respect to + * long double. + */ bool is_finite (const std::complex &x); - /** - * A structure that, together with - * its partial specializations - * NumberTraits > , - * provides traits and member - * functions that make it possible - * to write templates that work on - * both real number types and - * complex number types. This - * template is mostly used to - * implement linear algebra classes - * such as vectors and matrices - * that work for both real and - * complex numbers. - * - * @author Wolfgang Bangerth, 2007 - */ + /** + * A structure that, together with + * its partial specializations + * NumberTraits > , + * provides traits and member + * functions that make it possible + * to write templates that work on + * both real number types and + * complex number types. This + * template is mostly used to + * implement linear algebra classes + * such as vectors and matrices + * that work for both real and + * complex numbers. + * + * @author Wolfgang Bangerth, 2007 + */ template struct NumberTraits { - /** - * A flag that specifies whether the - * template type given to this class is - * complex or real. Since the general - * template is selected for non-complex - * types, the answer is - * false. - */ - static const bool is_complex = false; - - /** - * For this data type, typedef the - * corresponding real type. Since the - * general template is selected for all - * data types that are not - * specializations of std::complex, - * the underlying type must be - * real-values, so the real_type is - * equal to the underlying type. - */ - typedef number real_type; - - /** - * Return the complex-conjugate of the - * given number. Since the general - * template is selected if number is - * not a complex data type, this - * function simply returns the given - * number. - */ - static - const number & conjugate (const number &x); - - /** - * Return the square of the absolute - * value of the given number. Since the - * general template is chosen for types - * not equal to std::complex, this - * function simply returns the square - * of the given number. - */ - static - real_type abs_square (const number &x); - - /** - * Return the absolute value of a - * number. - */ - static - real_type abs (const number &x); + /** + * A flag that specifies whether the + * template type given to this class is + * complex or real. Since the general + * template is selected for non-complex + * types, the answer is + * false. + */ + static const bool is_complex = false; + + /** + * For this data type, typedef the + * corresponding real type. Since the + * general template is selected for all + * data types that are not + * specializations of std::complex, + * the underlying type must be + * real-values, so the real_type is + * equal to the underlying type. + */ + typedef number real_type; + + /** + * Return the complex-conjugate of the + * given number. Since the general + * template is selected if number is + * not a complex data type, this + * function simply returns the given + * number. + */ + static + const number &conjugate (const number &x); + + /** + * Return the square of the absolute + * value of the given number. Since the + * general template is chosen for types + * not equal to std::complex, this + * function simply returns the square + * of the given number. + */ + static + real_type abs_square (const number &x); + + /** + * Return the absolute value of a + * number. + */ + static + real_type abs (const number &x); }; - /** - * Specialization of the general - * NumberTraits class that provides the - * relevant information if the underlying - * data type is std::complex. - * - * @author Wolfgang Bangerth, 2007 - */ + /** + * Specialization of the general + * NumberTraits class that provides the + * relevant information if the underlying + * data type is std::complex. + * + * @author Wolfgang Bangerth, 2007 + */ template struct NumberTraits > { - /** - * A flag that specifies whether the - * template type given to this class is - * complex or real. Since this - * specialization of the general - * template is selected for complex - * types, the answer is - * true. - */ - static const bool is_complex = true; - - /** - * For this data type, typedef the - * corresponding real type. Since this - * specialization of the template is - * selected for number types - * std::complex, the real type is - * equal to the type used to store the - * two components of the complex - * number. - */ - typedef number real_type; - - /** - * Return the complex-conjugate of the - * given number. - */ - static - std::complex conjugate (const std::complex &x); - - /** - * Return the square of the absolute - * value of the given number. Since - * this specialization of the general - * template is chosen for types equal - * to std::complex, this function - * returns the product of a number and - * its complex conjugate. - */ - static - real_type abs_square (const std::complex &x); - - - /** - * Return the absolute value of a - * complex number. - */ - static - real_type abs (const std::complex &x); + /** + * A flag that specifies whether the + * template type given to this class is + * complex or real. Since this + * specialization of the general + * template is selected for complex + * types, the answer is + * true. + */ + static const bool is_complex = true; + + /** + * For this data type, typedef the + * corresponding real type. Since this + * specialization of the template is + * selected for number types + * std::complex, the real type is + * equal to the type used to store the + * two components of the complex + * number. + */ + typedef number real_type; + + /** + * Return the complex-conjugate of the + * given number. + */ + static + std::complex conjugate (const std::complex &x); + + /** + * Return the square of the absolute + * value of the given number. Since + * this specialization of the general + * template is chosen for types equal + * to std::complex, this function + * returns the product of a number and + * its complex conjugate. + */ + static + real_type abs_square (const std::complex &x); + + + /** + * Return the absolute value of a + * complex number. + */ + static + real_type abs (const std::complex &x); }; } diff --git a/deal.II/include/deal.II/base/parallel.h b/deal.II/include/deal.II/base/parallel.h index 285322472f..0afd44ae30 100644 --- a/deal.II/include/deal.II/base/parallel.h +++ b/deal.II/include/deal.II/base/parallel.h @@ -40,93 +40,93 @@ namespace parallel { namespace internal { - /** - * Convert a function object of type F - * into an object that can be applied to - * all elements of a range of synchronous - * iterators. - */ + /** + * Convert a function object of type F + * into an object that can be applied to + * all elements of a range of synchronous + * iterators. + */ template struct Body { - /** - * Constructor. Take and package the - * given function object. - */ - Body (const F &f) - : - f (f) - {} - - template - void - operator () (const Range &range) const - { - for (typename Range::const_iterator p=range.begin(); - p != range.end(); ++p) - apply (f, p.iterators); - } - - private: - /** - * The stored function object. - */ - const F f; - - /** - * Apply F to a set of iterators with - * two elements. - */ - template - static - void - apply (const F &f, - const std_cxx1x::tuple &p) - { - *std_cxx1x::get<1>(p) = f (*std_cxx1x::get<0>(p)); - } - - /** - * Apply F to a set of iterators with - * three elements. - */ - template - static - void - apply (const F &f, - const std_cxx1x::tuple &p) - { - *std_cxx1x::get<2>(p) = f (*std_cxx1x::get<0>(p), - *std_cxx1x::get<1>(p)); - } - - /** - * Apply F to a set of iterators with - * three elements. - */ - template - static - void - apply (const F &f, - const std_cxx1x::tuple &p) - { - *std_cxx1x::get<3>(p) = f (*std_cxx1x::get<0>(p), - *std_cxx1x::get<1>(p), - *std_cxx1x::get<2>(p)); - } + /** + * Constructor. Take and package the + * given function object. + */ + Body (const F &f) + : + f (f) + {} + + template + void + operator () (const Range &range) const + { + for (typename Range::const_iterator p=range.begin(); + p != range.end(); ++p) + apply (f, p.iterators); + } + + private: + /** + * The stored function object. + */ + const F f; + + /** + * Apply F to a set of iterators with + * two elements. + */ + template + static + void + apply (const F &f, + const std_cxx1x::tuple &p) + { + *std_cxx1x::get<1>(p) = f (*std_cxx1x::get<0>(p)); + } + + /** + * Apply F to a set of iterators with + * three elements. + */ + template + static + void + apply (const F &f, + const std_cxx1x::tuple &p) + { + *std_cxx1x::get<2>(p) = f (*std_cxx1x::get<0>(p), + *std_cxx1x::get<1>(p)); + } + + /** + * Apply F to a set of iterators with + * three elements. + */ + template + static + void + apply (const F &f, + const std_cxx1x::tuple &p) + { + *std_cxx1x::get<3>(p) = f (*std_cxx1x::get<0>(p), + *std_cxx1x::get<1>(p), + *std_cxx1x::get<2>(p)); + } }; - /** - * Take a function object and create a - * Body object from it. We do this in - * this helper function since - * alternatively we would have to specify - * the actual data type of F -- which for - * function objects is often - * extraordinarily complicated. - */ + /** + * Take a function object and create a + * Body object from it. We do this in + * this helper function since + * alternatively we would have to specify + * the actual data type of F -- which for + * function objects is often + * extraordinarily complicated. + */ template Body make_body(const F &f) { @@ -134,42 +134,42 @@ namespace parallel } } - /** - * An algorithm that performs the action - * *out++ = predicate(*in++) - * where the in iterator - * ranges over the given input range. - * - * This algorithm does pretty much what - * std::transform does. The difference is - * that the function can run in parallel - * when deal.II is configured to use - * multiple threads. - * - * If running in parallel, the iterator range - * is split into several chunks that are - * each packaged up as a task and given to - * the Threading Building Blocks scheduler - * to work on as compute resources are - * available. The function returns once all - * chunks have been worked on. The last - * argument denotes the minimum number of - * elements of the iterator range per - * task; the number must be - * large enough to amortize the startup - * cost of new tasks, and small enough to - * ensure that tasks can be - * reasonably load balanced. - * - * For a discussion of the kind of - * problems to which this function - * is applicable, see the - * @ref threads "Parallel computing with multiple processors" - * module. - */ + /** + * An algorithm that performs the action + * *out++ = predicate(*in++) + * where the in iterator + * ranges over the given input range. + * + * This algorithm does pretty much what + * std::transform does. The difference is + * that the function can run in parallel + * when deal.II is configured to use + * multiple threads. + * + * If running in parallel, the iterator range + * is split into several chunks that are + * each packaged up as a task and given to + * the Threading Building Blocks scheduler + * to work on as compute resources are + * available. The function returns once all + * chunks have been worked on. The last + * argument denotes the minimum number of + * elements of the iterator range per + * task; the number must be + * large enough to amortize the startup + * cost of new tasks, and small enough to + * ensure that tasks can be + * reasonably load balanced. + * + * For a discussion of the kind of + * problems to which this function + * is applicable, see the + * @ref threads "Parallel computing with multiple processors" + * module. + */ template + typename OutputIterator, + typename Predicate> void transform (const InputIterator &begin_in, const InputIterator &end_in, OutputIterator out, @@ -177,8 +177,8 @@ namespace parallel const unsigned int grainsize) { #if DEAL_II_USE_MT == 0 - // make sure we don't get compiler - // warnings about unused arguments + // make sure we don't get compiler + // warnings about unused arguments (void) grainsize; for (OutputIterator in = begin_in; in != end_in;) @@ -198,45 +198,45 @@ namespace parallel - /** - * An algorithm that performs the action - * *out++ = predicate(*in1++, *in2++) - * where the in1 iterator - * ranges over the given input - * range, using the parallel for - * operator of tbb. - * - * This algorithm does pretty much what - * std::transform does. The difference is - * that the function can run in parallel - * when deal.II is configured to use - * multiple threads. - * - * If running in parallel, the iterator range - * is split into several chunks that are - * each packaged up as a task and given to - * the Threading Building Blocks scheduler - * to work on as compute resources are - * available. The function returns once all - * chunks have been worked on. The last - * argument denotes the minimum number of - * elements of the iterator range per - * task; the number must be - * large enough to amortize the startup - * cost of new tasks, and small enough to - * ensure that tasks can be - * reasonably load balanced. - * - * For a discussion of the kind of - * problems to which this function - * is applicable, see the - * @ref threads "Parallel computing with multiple processors" - * module. - */ + /** + * An algorithm that performs the action + * *out++ = predicate(*in1++, *in2++) + * where the in1 iterator + * ranges over the given input + * range, using the parallel for + * operator of tbb. + * + * This algorithm does pretty much what + * std::transform does. The difference is + * that the function can run in parallel + * when deal.II is configured to use + * multiple threads. + * + * If running in parallel, the iterator range + * is split into several chunks that are + * each packaged up as a task and given to + * the Threading Building Blocks scheduler + * to work on as compute resources are + * available. The function returns once all + * chunks have been worked on. The last + * argument denotes the minimum number of + * elements of the iterator range per + * task; the number must be + * large enough to amortize the startup + * cost of new tasks, and small enough to + * ensure that tasks can be + * reasonably load balanced. + * + * For a discussion of the kind of + * problems to which this function + * is applicable, see the + * @ref threads "Parallel computing with multiple processors" + * module. + */ template + typename InputIterator2, + typename OutputIterator, + typename Predicate> void transform (const InputIterator1 &begin_in1, const InputIterator1 &end_in1, InputIterator2 in2, @@ -245,16 +245,16 @@ namespace parallel const unsigned int grainsize) { #if DEAL_II_USE_MT == 0 - // make sure we don't get compiler - // warnings about unused arguments + // make sure we don't get compiler + // warnings about unused arguments (void) grainsize; for (OutputIterator in1 = begin_in1; in1 != end_in1;) *out++ = predicate (*in1++, *in2++); #else typedef - std_cxx1x::tuple - Iterators; + std_cxx1x::tuple + Iterators; typedef SynchronousIterators SyncIterators; Iterators x_begin (begin_in1, in2, out); Iterators x_end (end_in1, InputIterator2(), OutputIterator()); @@ -268,44 +268,44 @@ namespace parallel - /** - * An algorithm that performs the action - * *out++ = predicate(*in1++, *in2++, *in3++) - * where the in1 iterator - * ranges over the given input range. - * - * This algorithm does pretty much what - * std::transform does. The difference is - * that the function can run in parallel - * when deal.II is configured to use - * multiple threads. - * - * If running in parallel, the iterator range - * is split into several chunks that are - * each packaged up as a task and given to - * the Threading Building Blocks scheduler - * to work on as compute resources are - * available. The function returns once all - * chunks have been worked on. The last - * argument denotes the minimum number of - * elements of the iterator range per - * task; the number must be - * large enough to amortize the startup - * cost of new tasks, and small enough to - * ensure that tasks can be - * reasonably load balanced. - * - * For a discussion of the kind of - * problems to which this function - * is applicable, see the - * @ref threads "Parallel computing with multiple processors" - * module. - */ + /** + * An algorithm that performs the action + * *out++ = predicate(*in1++, *in2++, *in3++) + * where the in1 iterator + * ranges over the given input range. + * + * This algorithm does pretty much what + * std::transform does. The difference is + * that the function can run in parallel + * when deal.II is configured to use + * multiple threads. + * + * If running in parallel, the iterator range + * is split into several chunks that are + * each packaged up as a task and given to + * the Threading Building Blocks scheduler + * to work on as compute resources are + * available. The function returns once all + * chunks have been worked on. The last + * argument denotes the minimum number of + * elements of the iterator range per + * task; the number must be + * large enough to amortize the startup + * cost of new tasks, and small enough to + * ensure that tasks can be + * reasonably load balanced. + * + * For a discussion of the kind of + * problems to which this function + * is applicable, see the + * @ref threads "Parallel computing with multiple processors" + * module. + */ template + typename InputIterator2, + typename InputIterator3, + typename OutputIterator, + typename Predicate> void transform (const InputIterator1 &begin_in1, const InputIterator1 &end_in1, InputIterator2 in2, @@ -315,16 +315,16 @@ namespace parallel const unsigned int grainsize) { #if DEAL_II_USE_MT == 0 - // make sure we don't get compiler - // warnings about unused arguments + // make sure we don't get compiler + // warnings about unused arguments (void) grainsize; for (OutputIterator in1 = begin_in1; in1 != end_in1;) *out++ = predicate (*in1++, *in2++, *in3++); #else typedef - std_cxx1x::tuple - Iterators; + std_cxx1x::tuple + Iterators; typedef SynchronousIterators SyncIterators; Iterators x_begin (begin_in1, in2, in3, out); Iterators x_end (end_in1, InputIterator2(), @@ -341,10 +341,10 @@ namespace parallel namespace internal { #if DEAL_II_USE_MT == 1 - /** - * Take a range argument and call the - * given function with its begin and end. - */ + /** + * Take a range argument and call the + * given function with its begin and end. + */ template void apply_to_subranges (const tbb::blocked_range &range, const Function &f) @@ -355,106 +355,106 @@ namespace parallel } - /** - * This function applies the given function - * argument @p f to all elements in the range - * [begin,end) and may do so - * in parallel. - * - * However, in many cases it is not - * efficient to call a function on each - * element, so this function calls the - * given function object on sub-ranges. In - * other words: if the given range - * [begin,end) is smaller than - * grainsize or if multithreading is not - * enabled, then we call - * f(begin,end); otherwise, we - * may execute, possibly in %parallel, a - * sequence of calls f(b,e) - * where [b,e) are - * subintervals of [begin,end) - * and the collection of calls we do to - * f(.,.) will happen on - * disjoint subintervals that collectively - * cover the original interval - * [begin,end). - * - * Oftentimes, the called function will of - * course have to get additional - * information, such as the object to work - * on for a given value of the iterator - * argument. This can be achieved by - * binding certain arguments. For - * example, here is an implementation of a - * matrix-vector multiplication $y=Ax$ for - * a full matrix $A$ and vectors $x,y$: - * @code - * void matrix_vector_product (const FullMatrix &A, - * const Vector &x, - * Vector &y) - * { - * parallel::apply_to_subranges - * (0, A.n_rows(), - * std_cxx1x::bind (&mat_vec_on_subranges, - * std_cxx1x::_1, std_cxx1x::_2, - * std_cxx1x::cref(A), - * std_cxx1x::cref(x), - * std_cxx1x::ref(y)), - * 50); - * } - * - * void mat_vec_on_subranges (const unsigned int begin_row, - * const unsigned int end_row, - * const FullMatrix &A, - * const Vector &x, - * Vector &y) - * { - * for (unsigned int row=begin_row; row!=end_row; ++row) - * for (unsigned int col=0; colstd_cxx1x::bind function to - * convert - * mat_vec_on_subranged from a - * function that takes 5 arguments to one - * taking 2 by binding the remaining - * arguments (the modifiers - * std_cxx1x::ref and - * std_cxx1x::cref make sure - * that the enclosed variables are actually - * passed by reference and constant - * reference, rather than by value). The - * resulting function object requires only - * two arguments, begin_row and end_row, - * with all other arguments fixed. - * - * The code, if in single-thread mode, will - * call mat_vec_on_subranges - * on the entire range - * [0,n_rows) exactly once. In - * multi-threaded mode, however, it may be - * called multiple times on subranges of - * this interval, possibly allowing more - * than one CPU core to take care of part - * of the work. - * - * The @p grainsize argument (50 in the - * example above) makes sure that subranges - * do not become too small, to avoid - * spending more time on scheduling - * subranges to CPU resources than on doing - * actual work. - * - * For a discussion of the kind of - * problems to which this function - * is applicable, see also the - * @ref threads "Parallel computing with multiple processors" - * module. - */ + /** + * This function applies the given function + * argument @p f to all elements in the range + * [begin,end) and may do so + * in parallel. + * + * However, in many cases it is not + * efficient to call a function on each + * element, so this function calls the + * given function object on sub-ranges. In + * other words: if the given range + * [begin,end) is smaller than + * grainsize or if multithreading is not + * enabled, then we call + * f(begin,end); otherwise, we + * may execute, possibly in %parallel, a + * sequence of calls f(b,e) + * where [b,e) are + * subintervals of [begin,end) + * and the collection of calls we do to + * f(.,.) will happen on + * disjoint subintervals that collectively + * cover the original interval + * [begin,end). + * + * Oftentimes, the called function will of + * course have to get additional + * information, such as the object to work + * on for a given value of the iterator + * argument. This can be achieved by + * binding certain arguments. For + * example, here is an implementation of a + * matrix-vector multiplication $y=Ax$ for + * a full matrix $A$ and vectors $x,y$: + * @code + * void matrix_vector_product (const FullMatrix &A, + * const Vector &x, + * Vector &y) + * { + * parallel::apply_to_subranges + * (0, A.n_rows(), + * std_cxx1x::bind (&mat_vec_on_subranges, + * std_cxx1x::_1, std_cxx1x::_2, + * std_cxx1x::cref(A), + * std_cxx1x::cref(x), + * std_cxx1x::ref(y)), + * 50); + * } + * + * void mat_vec_on_subranges (const unsigned int begin_row, + * const unsigned int end_row, + * const FullMatrix &A, + * const Vector &x, + * Vector &y) + * { + * for (unsigned int row=begin_row; row!=end_row; ++row) + * for (unsigned int col=0; colstd_cxx1x::bind function to + * convert + * mat_vec_on_subranged from a + * function that takes 5 arguments to one + * taking 2 by binding the remaining + * arguments (the modifiers + * std_cxx1x::ref and + * std_cxx1x::cref make sure + * that the enclosed variables are actually + * passed by reference and constant + * reference, rather than by value). The + * resulting function object requires only + * two arguments, begin_row and end_row, + * with all other arguments fixed. + * + * The code, if in single-thread mode, will + * call mat_vec_on_subranges + * on the entire range + * [0,n_rows) exactly once. In + * multi-threaded mode, however, it may be + * called multiple times on subranges of + * this interval, possibly allowing more + * than one CPU core to take care of part + * of the work. + * + * The @p grainsize argument (50 in the + * example above) makes sure that subranges + * do not become too small, to avoid + * spending more time on scheduling + * subranges to CPU resources than on doing + * actual work. + * + * For a discussion of the kind of + * problems to which this function + * is applicable, see also the + * @ref threads "Parallel computing with multiple processors" + * module. + */ template void apply_to_subranges (const RangeType &begin, const typename identity::type &end, @@ -462,8 +462,8 @@ namespace parallel const unsigned int grainsize) { #if DEAL_II_USE_MT == 0 - // make sure we don't get compiler - // warnings about unused arguments + // make sure we don't get compiler + // warnings about unused arguments (void) grainsize; f (begin, end); @@ -508,36 +508,36 @@ namespace parallel */ struct ParallelForInteger { - /** - * Destructor. Made virtual to ensure that derived classes also - * have virtual destructors. - */ - virtual ~ParallelForInteger (); - - /** - * This function runs the for loop over the - * given range [lower,upper), - * possibly in parallel when end-begin is - * larger than the minimum parallel grain - * size. This function is marked const because - * it any operation that changes the data of a - * derived class will inherently not be - * thread-safe when several threads work with - * the same data simultaneously. - */ + /** + * Destructor. Made virtual to ensure that derived classes also + * have virtual destructors. + */ + virtual ~ParallelForInteger (); + + /** + * This function runs the for loop over the + * given range [lower,upper), + * possibly in parallel when end-begin is + * larger than the minimum parallel grain + * size. This function is marked const because + * it any operation that changes the data of a + * derived class will inherently not be + * thread-safe when several threads work with + * the same data simultaneously. + */ void apply_parallel (const std::size_t begin, const std::size_t end, const std::size_t minimum_parallel_grain_size) const; - /** - * Virtual function for working on subrange to - * be defined in a derived class. This - * function is marked const because it any - * operation that changes the data of a - * derived class will inherently not be - * thread-safe when several threads work with - * the same data simultaneously. - */ + /** + * Virtual function for working on subrange to + * be defined in a derived class. This + * function is marked const because it any + * operation that changes the data of a + * derived class will inherently not be + * thread-safe when several threads work with + * the same data simultaneously. + */ virtual void apply_to_subrange (const std::size_t, const std::size_t) const = 0; }; @@ -547,194 +547,194 @@ namespace parallel namespace internal { #if DEAL_II_USE_MT == 1 - /** - * A class that conforms to the Body - * requirements of the TBB - * parallel_reduce function. The first - * template argument denotes the type on - * which the reduction is to be done. The - * second denotes the type of the - * function object that shall be called - * for each subrange. - */ + /** + * A class that conforms to the Body + * requirements of the TBB + * parallel_reduce function. The first + * template argument denotes the type on + * which the reduction is to be done. The + * second denotes the type of the + * function object that shall be called + * for each subrange. + */ template + typename Function> struct ReductionOnSubranges { - /** - * A variable that will hold the - * result of the reduction. - */ - ResultType result; - - /** - * Constructor. Take the function - * object to call on each sub-range - * as well as the neutral element - * with respect to the reduction - * operation. - * - * The second argument denotes a - * function object that will be used - * to reduce the result of two - * computations into one number. An - * example if we want to simply - * accumulate integer results would - * be std::plus(). - */ - template - ReductionOnSubranges (const Function &f, - const Reductor &reductor, - const ResultType neutral_element = ResultType()) - : - result (neutral_element), - f (f), - neutral_element (neutral_element), - reductor (reductor) - {} - - /** - * Splitting constructor. See the TBB - * book for more details about this. - */ - ReductionOnSubranges (const ReductionOnSubranges &r, - tbb::split) - : - result (r.neutral_element), - f (r.f), - neutral_element (r.neutral_element), - reductor (r.reductor) - {} - - /** - * Join operation: merge the results - * from computations on different - * sub-intervals. - */ - void join (const ReductionOnSubranges &r) - { - result = reductor(result, r.result); - } - - /** - * Execute the given function on the - * specified range. - */ - template - void operator () (const tbb::blocked_range &range) - { - result = reductor(result, - f (range.begin(), range.end())); - } - - private: - /** - * The function object to call on - * every sub-range. - */ - const Function f; - - /** - * The neutral element with respect - * to the reduction operation. This - * is needed when calling the - * splitting constructor since we - * have to re-set the result variable - * in this case. - */ - const ResultType neutral_element; - - /** - * The function object to be used to - * reduce the result of two calls - * into one number. - */ - const std_cxx1x::function reductor; + /** + * A variable that will hold the + * result of the reduction. + */ + ResultType result; + + /** + * Constructor. Take the function + * object to call on each sub-range + * as well as the neutral element + * with respect to the reduction + * operation. + * + * The second argument denotes a + * function object that will be used + * to reduce the result of two + * computations into one number. An + * example if we want to simply + * accumulate integer results would + * be std::plus(). + */ + template + ReductionOnSubranges (const Function &f, + const Reductor &reductor, + const ResultType neutral_element = ResultType()) + : + result (neutral_element), + f (f), + neutral_element (neutral_element), + reductor (reductor) + {} + + /** + * Splitting constructor. See the TBB + * book for more details about this. + */ + ReductionOnSubranges (const ReductionOnSubranges &r, + tbb::split) + : + result (r.neutral_element), + f (r.f), + neutral_element (r.neutral_element), + reductor (r.reductor) + {} + + /** + * Join operation: merge the results + * from computations on different + * sub-intervals. + */ + void join (const ReductionOnSubranges &r) + { + result = reductor(result, r.result); + } + + /** + * Execute the given function on the + * specified range. + */ + template + void operator () (const tbb::blocked_range &range) + { + result = reductor(result, + f (range.begin(), range.end())); + } + + private: + /** + * The function object to call on + * every sub-range. + */ + const Function f; + + /** + * The neutral element with respect + * to the reduction operation. This + * is needed when calling the + * splitting constructor since we + * have to re-set the result variable + * in this case. + */ + const ResultType neutral_element; + + /** + * The function object to be used to + * reduce the result of two calls + * into one number. + */ + const std_cxx1x::function reductor; }; #endif } - /** - * This function works a lot like the - * apply_to_subranges(), but it allows to - * accumulate numerical results computed on - * each subrange into one number. The type - * of this number is given by the - * ResultType template argument that needs - * to be explicitly specified. - * - * An example of use of this function is to - * compute the value of the expression $x^T - * A x$ for a square matrix $A$ and a - * vector $x$. The sum over rows can be - * parallelized and the whole code might - * look like this: - * @code - * void matrix_norm (const FullMatrix &A, - * const Vector &x) - * { - * return - * std::sqrt - * (parallel::accumulate_from_subranges - * (0, A.n_rows(), - * std_cxx1x::bind (&mat_norm_sqr_on_subranges, - * std_cxx1x::_1, std_cxx1x::_2, - * std_cxx1x::cref(A), - * std_cxx1x::cref(x)), - * 50); - * } - * - * double - * mat_norm_sqr_on_subranges (const unsigned int begin_row, - * const unsigned int end_row, - * const FullMatrix &A, - * const Vector &x) - * { - * double norm_sqr = 0; - * for (unsigned int row=begin_row; row!=end_row; ++row) - * for (unsigned int col=0; colmat_norm_sqr_on_subranges - * is called on the entire range - * [0,A.n_rows()) if this - * range is less than the minimum grainsize - * (above chosen as 50) or if deal.II is - * configured to not use - * multithreading. Otherwise, it may be - * called on subsets of the given range, - * with results from the individual - * subranges accumulated internally. - * - * @warning If ResultType is a floating point - * type, then accumulation is not an - * associative operation. In other words, - * if the given function object is called - * three times on three subranges, - * returning values $a,b,c$, then the - * returned result of this function is - * $(a+b)+c$. However, depending on how the - * three sub-tasks are distributed on - * available CPU resources, the result may - * also be $(a+c)+b$ or any other - * permutation; because floating point - * addition is not associative (as opposed, of - * course, to addition of real %numbers), - * the result of invoking this function - * several times may differ on the order of - * round-off. - * - * For a discussion of the kind of - * problems to which this function - * is applicable, see also the - * @ref threads "Parallel computing with multiple processors" - * module. - */ + /** + * This function works a lot like the + * apply_to_subranges(), but it allows to + * accumulate numerical results computed on + * each subrange into one number. The type + * of this number is given by the + * ResultType template argument that needs + * to be explicitly specified. + * + * An example of use of this function is to + * compute the value of the expression $x^T + * A x$ for a square matrix $A$ and a + * vector $x$. The sum over rows can be + * parallelized and the whole code might + * look like this: + * @code + * void matrix_norm (const FullMatrix &A, + * const Vector &x) + * { + * return + * std::sqrt + * (parallel::accumulate_from_subranges + * (0, A.n_rows(), + * std_cxx1x::bind (&mat_norm_sqr_on_subranges, + * std_cxx1x::_1, std_cxx1x::_2, + * std_cxx1x::cref(A), + * std_cxx1x::cref(x)), + * 50); + * } + * + * double + * mat_norm_sqr_on_subranges (const unsigned int begin_row, + * const unsigned int end_row, + * const FullMatrix &A, + * const Vector &x) + * { + * double norm_sqr = 0; + * for (unsigned int row=begin_row; row!=end_row; ++row) + * for (unsigned int col=0; colmat_norm_sqr_on_subranges + * is called on the entire range + * [0,A.n_rows()) if this + * range is less than the minimum grainsize + * (above chosen as 50) or if deal.II is + * configured to not use + * multithreading. Otherwise, it may be + * called on subsets of the given range, + * with results from the individual + * subranges accumulated internally. + * + * @warning If ResultType is a floating point + * type, then accumulation is not an + * associative operation. In other words, + * if the given function object is called + * three times on three subranges, + * returning values $a,b,c$, then the + * returned result of this function is + * $(a+b)+c$. However, depending on how the + * three sub-tasks are distributed on + * available CPU resources, the result may + * also be $(a+c)+b$ or any other + * permutation; because floating point + * addition is not associative (as opposed, of + * course, to addition of real %numbers), + * the result of invoking this function + * several times may differ on the order of + * round-off. + * + * For a discussion of the kind of + * problems to which this function + * is applicable, see also the + * @ref threads "Parallel computing with multiple processors" + * module. + */ template ResultType accumulate_from_subranges (const Function &f, const RangeType &begin, @@ -742,14 +742,14 @@ namespace parallel const unsigned int grainsize) { #if DEAL_II_USE_MT == 0 - // make sure we don't get compiler - // warnings about unused arguments + // make sure we don't get compiler + // warnings about unused arguments (void) grainsize; return f(begin,end); #else internal::ReductionOnSubranges - reductor (f, std::plus(), 0); + reductor (f, std::plus(), 0); tbb::parallel_reduce (tbb::blocked_range(begin, end, grainsize), reductor, tbb::auto_partitioner()); @@ -764,44 +764,44 @@ namespace internal { namespace Vector { - /** - * If we do computations on vectors in - * parallel (say, we add two vectors to - * get a third, and we do the loop over - * all elements in parallel), then this - * variable determines the minimum number - * of elements for which it is profitable - * to split a range of elements any - * further to distribute to different - * threads. - * - * This variable is available as - * a global writable variable in - * order to allow the testsuite - * to also test the parallel - * case. By default, it is set to - * several thousand elements, - * which is a case that the - * testsuite would not normally - * encounter. As a consequence, - * in the testsuite we set it to - * one -- a value that's hugely - * unprofitable but definitely - * tests parallel operations. - */ + /** + * If we do computations on vectors in + * parallel (say, we add two vectors to + * get a third, and we do the loop over + * all elements in parallel), then this + * variable determines the minimum number + * of elements for which it is profitable + * to split a range of elements any + * further to distribute to different + * threads. + * + * This variable is available as + * a global writable variable in + * order to allow the testsuite + * to also test the parallel + * case. By default, it is set to + * several thousand elements, + * which is a case that the + * testsuite would not normally + * encounter. As a consequence, + * in the testsuite we set it to + * one -- a value that's hugely + * unprofitable but definitely + * tests parallel operations. + */ extern unsigned int minimum_parallel_grain_size; } namespace SparseMatrix { - /** - * Like - * internal::Vector::minimum_parallel_grain_size, - * but now denoting the number of rows of - * a matrix that should be worked on as a - * minimum. - */ + /** + * Like + * internal::Vector::minimum_parallel_grain_size, + * but now denoting the number of rows of + * a matrix that should be worked on as a + * minimum. + */ extern unsigned int minimum_parallel_grain_size; } @@ -852,8 +852,8 @@ namespace parallel const std::size_t minimum_parallel_grain_size) const { #if DEAL_II_USE_MT == 0 - // make sure we don't get compiler - // warnings about unused arguments + // make sure we don't get compiler + // warnings about unused arguments (void) minimum_parallel_grain_size; apply_to_subrange (begin, end); diff --git a/deal.II/include/deal.II/base/parameter_handler.h b/deal.II/include/deal.II/base/parameter_handler.h index 9a81d2a33a..f19849ef96 100644 --- a/deal.II/include/deal.II/base/parameter_handler.h +++ b/deal.II/include/deal.II/base/parameter_handler.h @@ -54,611 +54,611 @@ class LogStream; namespace Patterns { - /** - * Base class to declare common - * interface. The purpose of this - * class is mostly to define the - * interface of patterns, and to - * force derived classes to have a - * clone function. It is thus, - * in the languages of the "Design - * Patterns" book (Gamma et al.), a - * "prototype". - */ + /** + * Base class to declare common + * interface. The purpose of this + * class is mostly to define the + * interface of patterns, and to + * force derived classes to have a + * clone function. It is thus, + * in the languages of the "Design + * Patterns" book (Gamma et al.), a + * "prototype". + */ class PatternBase { - public: - /** - * Make destructor of this and all - * derived classes virtual. - */ - virtual ~PatternBase (); - - /** - * Return true if the given string - * matches the pattern. - */ - virtual bool match (const std::string &test_string) const = 0; - - /** - * Return a string describing the - * pattern. - */ - virtual std::string description () const = 0; - - /** - * Return a pointer to an - * exact copy of the - * object. This is necessary - * since we want to store - * objects of this type in - * containers, were we need - * to copy objects without - * knowledge of their actual - * data type (we only have - * pointers to the base - * class). - * - * Ownership of the objects - * returned by this function - * is passed to the caller of - * this function. - */ - virtual PatternBase * clone () const = 0; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this object. To - * avoid unnecessary - * overhead, we do not force - * derived classes to provide - * this function as a virtual - * overloaded one, but rather - * try to cast the present - * object to one of the known - * derived classes and if - * that fails then take the - * size of this base class - * instead and add 32 byte - * (this value is arbitrary, - * it should account for - * virtual function tables, - * and some possible data - * elements). Since there are - * usually not many thousands - * of objects of this type - * around, and since the - * memory_consumption - * mechanism is used to find - * out where memory in the - * range of many megabytes - * is, this seems like a - * reasonable approximation. - * - * On the other hand, if you - * know that your class - * deviates from this - * assumption significantly, - * you can still overload - * this function. - */ - virtual std::size_t memory_consumption () const; + public: + /** + * Make destructor of this and all + * derived classes virtual. + */ + virtual ~PatternBase (); + + /** + * Return true if the given string + * matches the pattern. + */ + virtual bool match (const std::string &test_string) const = 0; + + /** + * Return a string describing the + * pattern. + */ + virtual std::string description () const = 0; + + /** + * Return a pointer to an + * exact copy of the + * object. This is necessary + * since we want to store + * objects of this type in + * containers, were we need + * to copy objects without + * knowledge of their actual + * data type (we only have + * pointers to the base + * class). + * + * Ownership of the objects + * returned by this function + * is passed to the caller of + * this function. + */ + virtual PatternBase *clone () const = 0; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this object. To + * avoid unnecessary + * overhead, we do not force + * derived classes to provide + * this function as a virtual + * overloaded one, but rather + * try to cast the present + * object to one of the known + * derived classes and if + * that fails then take the + * size of this base class + * instead and add 32 byte + * (this value is arbitrary, + * it should account for + * virtual function tables, + * and some possible data + * elements). Since there are + * usually not many thousands + * of objects of this type + * around, and since the + * memory_consumption + * mechanism is used to find + * out where memory in the + * range of many megabytes + * is, this seems like a + * reasonable approximation. + * + * On the other hand, if you + * know that your class + * deviates from this + * assumption significantly, + * you can still overload + * this function. + */ + virtual std::size_t memory_consumption () const; }; - /** - * Returns pointer to the correct - * derived class based on description. - */ - PatternBase * pattern_factory (const std::string& description); - - /** - * Test for the string being an - * integer. If bounds are given - * to the constructor, then the - * integer given also needs to be - * within the interval specified - * by these bounds. Note that - * unlike common convention in - * the C++ standard library, both - * bounds of this interval are - * inclusive; the reason is that - * in practice in most cases, one - * needs closed intervals, but - * these can only be realized - * with inclusive bounds for - * non-integer values. We thus - * stay consistent by always - * using closed intervals. - * - * If the upper bound given to - * the constructor is smaller - * than the lower bound, then the - * infinite interval is implied, - * i.e. every integer is allowed. - * - * Giving bounds may be useful if - * for example a value can only - * be positive and less than a - * reasonable upper bound (for - * example the number of - * refinement steps to be - * performed), or in many other - * cases. - */ + /** + * Returns pointer to the correct + * derived class based on description. + */ + PatternBase *pattern_factory (const std::string &description); + + /** + * Test for the string being an + * integer. If bounds are given + * to the constructor, then the + * integer given also needs to be + * within the interval specified + * by these bounds. Note that + * unlike common convention in + * the C++ standard library, both + * bounds of this interval are + * inclusive; the reason is that + * in practice in most cases, one + * needs closed intervals, but + * these can only be realized + * with inclusive bounds for + * non-integer values. We thus + * stay consistent by always + * using closed intervals. + * + * If the upper bound given to + * the constructor is smaller + * than the lower bound, then the + * infinite interval is implied, + * i.e. every integer is allowed. + * + * Giving bounds may be useful if + * for example a value can only + * be positive and less than a + * reasonable upper bound (for + * example the number of + * refinement steps to be + * performed), or in many other + * cases. + */ class Integer : public PatternBase { - public: - /** - * Minimal integer value. If - * the numeric_limits class - * is available use this - * information to obtain the - * extremal values, otherwise - * set it so that this class - * understands that all values - * are allowed. - */ - static const int min_int_value; - - /** - * Maximal integer value. If - * the numeric_limits class - * is available use this - * information to obtain the - * extremal values, otherwise - * set it so that this class - * understands that all values - * are allowed. - */ - static const int max_int_value; - - /** - * Constructor. Bounds can be - * specified within which a - * valid parameter has to - * be. If the upper bound is - * smaller than the lower - * bound, then the infinite - * interval is meant. The - * default values are chosen - * such that no bounds are - * enforced on parameters. - */ - Integer (const int lower_bound = min_int_value, - const int upper_bound = max_int_value); - - /** - * Return true if the - * string is an integer and - * its value is within the - * specified range. - */ - virtual bool match (const std::string &test_string) const; - - /** - * Return a description of - * the pattern that valid - * strings are expected to - * match. If bounds were - * specified to the - * constructor, then include - * them into this - * description. - */ - virtual std::string description () const; - - /** - * Return a copy of the - * present object, which is - * newly allocated on the - * heap. Ownership of that - * object is transferred to - * the caller of this - * function. - */ - virtual PatternBase * clone () const; - - /** - * Creates new object if the start of - * description matches - * description_init. Ownership of that - * object is transferred to the caller - * of this function. - */ - static Integer* create (const std::string& description); - - private: - /** - * Value of the lower - * bound. A number that - * satisfies the @ref match - * operation of this class - * must be equal to this - * value or larger, if the - * bounds of the interval for - * a valid range. - */ - const int lower_bound; - - /** - * Value of the upper - * bound. A number that - * satisfies the @ref match - * operation of this class - * must be equal to this - * value or less, if the - * bounds of the interval for - * a valid range. - */ - const int upper_bound; - - /** - * Initial part of description - */ - static const char* description_init; + public: + /** + * Minimal integer value. If + * the numeric_limits class + * is available use this + * information to obtain the + * extremal values, otherwise + * set it so that this class + * understands that all values + * are allowed. + */ + static const int min_int_value; + + /** + * Maximal integer value. If + * the numeric_limits class + * is available use this + * information to obtain the + * extremal values, otherwise + * set it so that this class + * understands that all values + * are allowed. + */ + static const int max_int_value; + + /** + * Constructor. Bounds can be + * specified within which a + * valid parameter has to + * be. If the upper bound is + * smaller than the lower + * bound, then the infinite + * interval is meant. The + * default values are chosen + * such that no bounds are + * enforced on parameters. + */ + Integer (const int lower_bound = min_int_value, + const int upper_bound = max_int_value); + + /** + * Return true if the + * string is an integer and + * its value is within the + * specified range. + */ + virtual bool match (const std::string &test_string) const; + + /** + * Return a description of + * the pattern that valid + * strings are expected to + * match. If bounds were + * specified to the + * constructor, then include + * them into this + * description. + */ + virtual std::string description () const; + + /** + * Return a copy of the + * present object, which is + * newly allocated on the + * heap. Ownership of that + * object is transferred to + * the caller of this + * function. + */ + virtual PatternBase *clone () const; + + /** + * Creates new object if the start of + * description matches + * description_init. Ownership of that + * object is transferred to the caller + * of this function. + */ + static Integer *create (const std::string &description); + + private: + /** + * Value of the lower + * bound. A number that + * satisfies the @ref match + * operation of this class + * must be equal to this + * value or larger, if the + * bounds of the interval for + * a valid range. + */ + const int lower_bound; + + /** + * Value of the upper + * bound. A number that + * satisfies the @ref match + * operation of this class + * must be equal to this + * value or less, if the + * bounds of the interval for + * a valid range. + */ + const int upper_bound; + + /** + * Initial part of description + */ + static const char *description_init; }; - /** - * Test for the string being a - * double. If bounds are - * given to the constructor, then - * the integer given also needs - * to be within the interval - * specified by these - * bounds. Note that unlike - * common convention in the C++ - * standard library, both bounds - * of this interval are - * inclusive; the reason is that - * in practice in most cases, one - * needs closed intervals, but - * these can only be realized - * with inclusive bounds for - * non-integer values. We thus - * stay consistent by always - * using closed intervals. - * - * If the upper bound given to - * the constructor is smaller - * than the lower bound, then the - * infinite interval is implied, - * i.e. every integer is allowed. - * - * Giving bounds may be useful if - * for example a value can only - * be positive and less than a - * reasonable upper bound (for - * example damping parameters are - * frequently only reasonable if - * between zero and one), or in - * many other cases. - */ + /** + * Test for the string being a + * double. If bounds are + * given to the constructor, then + * the integer given also needs + * to be within the interval + * specified by these + * bounds. Note that unlike + * common convention in the C++ + * standard library, both bounds + * of this interval are + * inclusive; the reason is that + * in practice in most cases, one + * needs closed intervals, but + * these can only be realized + * with inclusive bounds for + * non-integer values. We thus + * stay consistent by always + * using closed intervals. + * + * If the upper bound given to + * the constructor is smaller + * than the lower bound, then the + * infinite interval is implied, + * i.e. every integer is allowed. + * + * Giving bounds may be useful if + * for example a value can only + * be positive and less than a + * reasonable upper bound (for + * example damping parameters are + * frequently only reasonable if + * between zero and one), or in + * many other cases. + */ class Double : public PatternBase { - public: - /** - * Minimal double value. If the - * std::numeric_limits - * class is available use this - * information to obtain the - * extremal values, otherwise - * set it so that this class - * understands that all values - * are allowed. - */ - static const double min_double_value; - - /** - * Maximal double value. If the - * numeric_limits class is - * available use this - * information to obtain the - * extremal values, otherwise - * set it so that this class - * understands that all values - * are allowed. - */ - static const double max_double_value; - - /** - * Constructor. Bounds can be - * specified within which a - * valid parameter has to - * be. If the upper bound is - * smaller than the lower - * bound, then the infinite - * interval is meant. The - * default values are chosen - * such that no bounds are - * enforced on parameters. - */ - Double (const double lower_bound = min_double_value, - const double upper_bound = max_double_value); - - /** - * Return true if the - * string is a number and its - * value is within the - * specified range. - */ - virtual bool match (const std::string &test_string) const; - - /** - * Return a description of - * the pattern that valid - * strings are expected to - * match. If bounds were - * specified to the - * constructor, then include - * them into this - * description. - */ - virtual std::string description () const; - - /** - * Return a copy of the - * present object, which is - * newly allocated on the - * heap. Ownership of that - * object is transferred to - * the caller of this - * function. - */ - virtual PatternBase * clone () const; - - /** - * Creates new object if the start of - * description matches - * description_init. Ownership of that - * object is transferred to the caller - * of this function. - */ - static Double* create (const std::string& description); - - private: - /** - * Value of the lower - * bound. A number that - * satisfies the @ref match - * operation of this class - * must be equal to this - * value or larger, if the - * bounds of the interval for - * a valid range. - */ - const double lower_bound; - - /** - * Value of the upper - * bound. A number that - * satisfies the @ref match - * operation of this class - * must be equal to this - * value or less, if the - * bounds of the interval for - * a valid range. - */ - const double upper_bound; - - /** - * Initial part of description - */ - static const char* description_init; + public: + /** + * Minimal double value. If the + * std::numeric_limits + * class is available use this + * information to obtain the + * extremal values, otherwise + * set it so that this class + * understands that all values + * are allowed. + */ + static const double min_double_value; + + /** + * Maximal double value. If the + * numeric_limits class is + * available use this + * information to obtain the + * extremal values, otherwise + * set it so that this class + * understands that all values + * are allowed. + */ + static const double max_double_value; + + /** + * Constructor. Bounds can be + * specified within which a + * valid parameter has to + * be. If the upper bound is + * smaller than the lower + * bound, then the infinite + * interval is meant. The + * default values are chosen + * such that no bounds are + * enforced on parameters. + */ + Double (const double lower_bound = min_double_value, + const double upper_bound = max_double_value); + + /** + * Return true if the + * string is a number and its + * value is within the + * specified range. + */ + virtual bool match (const std::string &test_string) const; + + /** + * Return a description of + * the pattern that valid + * strings are expected to + * match. If bounds were + * specified to the + * constructor, then include + * them into this + * description. + */ + virtual std::string description () const; + + /** + * Return a copy of the + * present object, which is + * newly allocated on the + * heap. Ownership of that + * object is transferred to + * the caller of this + * function. + */ + virtual PatternBase *clone () const; + + /** + * Creates new object if the start of + * description matches + * description_init. Ownership of that + * object is transferred to the caller + * of this function. + */ + static Double *create (const std::string &description); + + private: + /** + * Value of the lower + * bound. A number that + * satisfies the @ref match + * operation of this class + * must be equal to this + * value or larger, if the + * bounds of the interval for + * a valid range. + */ + const double lower_bound; + + /** + * Value of the upper + * bound. A number that + * satisfies the @ref match + * operation of this class + * must be equal to this + * value or less, if the + * bounds of the interval for + * a valid range. + */ + const double upper_bound; + + /** + * Initial part of description + */ + static const char *description_init; + }; + + /** + * Test for the string being one + * of a sequence of values given + * like a regular expression. For + * example, if the string given + * to the constructor is + * "red|blue|black", then the + * @ref match function returns + * true exactly if the string + * is either "red" or "blue" or + * "black". Spaces around the + * pipe signs do not matter and + * are eliminated. + */ + class Selection : public PatternBase + { + public: + /** + * Constructor. Take the + * given parameter as the + * specification of valid + * strings. + */ + Selection (const std::string &seq); + + /** + * Return true if the + * string is an element of + * the description list + * passed to the constructor. + */ + virtual bool match (const std::string &test_string) const; + + /** + * Return a description of + * the pattern that valid + * strings are expected to + * match. Here, this is the + * list of valid strings + * passed to the constructor. + */ + virtual std::string description () const; + + /** + * Return a copy of the + * present object, which is + * newly allocated on the + * heap. Ownership of that + * object is transferred to + * the caller of this + * function. + */ + virtual PatternBase *clone () const; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this object. + */ + std::size_t memory_consumption () const; + + /** + * Creates new object if the start of + * description matches + * description_init. Ownership of that + * object is transferred to the caller + * of this function. + */ + static Selection *create (const std::string &description); + + private: + /** + * List of valid strings as + * passed to the + * constructor. We don't make + * this string constant, as + * we process it somewhat in + * the constructor. + */ + std::string sequence; + + /** + * Initial part of description + */ + static const char *description_init; }; - /** - * Test for the string being one - * of a sequence of values given - * like a regular expression. For - * example, if the string given - * to the constructor is - * "red|blue|black", then the - * @ref match function returns - * true exactly if the string - * is either "red" or "blue" or - * "black". Spaces around the - * pipe signs do not matter and - * are eliminated. - */ - class Selection : public PatternBase - { - public: - /** - * Constructor. Take the - * given parameter as the - * specification of valid - * strings. - */ - Selection (const std::string &seq); - - /** - * Return true if the - * string is an element of - * the description list - * passed to the constructor. - */ - virtual bool match (const std::string &test_string) const; - - /** - * Return a description of - * the pattern that valid - * strings are expected to - * match. Here, this is the - * list of valid strings - * passed to the constructor. - */ - virtual std::string description () const; - - /** - * Return a copy of the - * present object, which is - * newly allocated on the - * heap. Ownership of that - * object is transferred to - * the caller of this - * function. - */ - virtual PatternBase * clone () const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this object. - */ - std::size_t memory_consumption () const; - - /** - * Creates new object if the start of - * description matches - * description_init. Ownership of that - * object is transferred to the caller - * of this function. - */ - static Selection* create (const std::string& description); - - private: - /** - * List of valid strings as - * passed to the - * constructor. We don't make - * this string constant, as - * we process it somewhat in - * the constructor. - */ - std::string sequence; - - /** - * Initial part of description - */ - static const char* description_init; - }; + /** + * This pattern matches a list of + * comma-separated values each of which + * have to match a pattern given to the + * constructor. With two additional + * parameters, the number of elements this + * list has to have can be specified. If + * none is specified, the list may have + * zero or more entries. + */ + class List : public PatternBase + { + public: + /** + * Maximal integer value. If + * the numeric_limits class + * is available use this + * information to obtain the + * extremal values, otherwise + * set it so that this class + * understands that all values + * are allowed. + */ + static const unsigned int max_int_value; + + /** + * Constructor. Take the + * given parameter as the + * specification of valid + * elements of the list. + * + * The two other arguments can + * be used to denote minimal + * and maximal allowable + * lengths of the list. + */ + List (const PatternBase &base_pattern, + const unsigned int min_elements = 0, + const unsigned int max_elements = max_int_value); + + /** + * Destructor. + */ + virtual ~List (); + + /** + * Return true if the + * string is a comma-separated + * list of strings each of + * which match the pattern + * given to the constructor. + */ + virtual bool match (const std::string &test_string) const; + + /** + * Return a description of + * the pattern that valid + * strings are expected to + * match. + */ + virtual std::string description () const; + + /** + * Return a copy of the + * present object, which is + * newly allocated on the + * heap. Ownership of that + * object is transferred to + * the caller of this + * function. + */ + virtual PatternBase *clone () const; + + /** + * Creates new object if the start of + * description matches + * description_init. Ownership of that + * object is transferred to the caller + * of this function. + */ + static List *create (const std::string &description); + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this object. + */ + std::size_t memory_consumption () const; + + /** @addtogroup Exceptions + * @{ */ + + /** + * Exception. + */ + DeclException2 (ExcInvalidRange, + int, int, + << "The values " << arg1 << " and " << arg2 + << " do not form a valid range."); + //@} + private: + /** + * Copy of the pattern that + * each element of the list has + * to satisfy. + */ + PatternBase *pattern; + + /** + * Minimum number of elements + * the list must have. + */ + const unsigned int min_elements; + + /** + * Maximum number of elements + * the list must have. + */ + const unsigned int max_elements; - /** - * This pattern matches a list of - * comma-separated values each of which - * have to match a pattern given to the - * constructor. With two additional - * parameters, the number of elements this - * list has to have can be specified. If - * none is specified, the list may have - * zero or more entries. - */ - class List : public PatternBase - { - public: - /** - * Maximal integer value. If - * the numeric_limits class - * is available use this - * information to obtain the - * extremal values, otherwise - * set it so that this class - * understands that all values - * are allowed. - */ - static const unsigned int max_int_value; - - /** - * Constructor. Take the - * given parameter as the - * specification of valid - * elements of the list. - * - * The two other arguments can - * be used to denote minimal - * and maximal allowable - * lengths of the list. - */ - List (const PatternBase &base_pattern, - const unsigned int min_elements = 0, - const unsigned int max_elements = max_int_value); - - /** - * Destructor. - */ - virtual ~List (); - - /** - * Return true if the - * string is a comma-separated - * list of strings each of - * which match the pattern - * given to the constructor. - */ - virtual bool match (const std::string &test_string) const; - - /** - * Return a description of - * the pattern that valid - * strings are expected to - * match. - */ - virtual std::string description () const; - - /** - * Return a copy of the - * present object, which is - * newly allocated on the - * heap. Ownership of that - * object is transferred to - * the caller of this - * function. - */ - virtual PatternBase * clone () const; - - /** - * Creates new object if the start of - * description matches - * description_init. Ownership of that - * object is transferred to the caller - * of this function. - */ - static List* create (const std::string& description); - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this object. - */ - std::size_t memory_consumption () const; - - /** @addtogroup Exceptions - * @{ */ - - /** - * Exception. - */ - DeclException2 (ExcInvalidRange, - int, int, - << "The values " << arg1 << " and " << arg2 - << " do not form a valid range."); - //@} - private: - /** - * Copy of the pattern that - * each element of the list has - * to satisfy. - */ - PatternBase *pattern; - - /** - * Minimum number of elements - * the list must have. - */ - const unsigned int min_elements; - - /** - * Maximum number of elements - * the list must have. - */ - const unsigned int max_elements; - - /** - * Initial part of description - */ - static const char* description_init; + /** + * Initial part of description + */ + static const char *description_init; }; @@ -741,7 +741,7 @@ namespace Patterns * the caller of this * function. */ - virtual PatternBase * clone () const; + virtual PatternBase *clone () const; /** * Creates new object if the start of @@ -750,7 +750,7 @@ namespace Patterns * object is transferred to the caller * of this function. */ - static Map* create (const std::string& description); + static Map *create (const std::string &description); /** * Determine an estimate for @@ -766,9 +766,9 @@ namespace Patterns * Exception. */ DeclException2 (ExcInvalidRange, - int, int, - << "The values " << arg1 << " and " << arg2 - << " do not form a valid range."); + int, int, + << "The values " << arg1 << " and " << arg2 + << " do not form a valid range."); //@} private: /** @@ -794,383 +794,383 @@ namespace Patterns /** * Initial part of description */ - static const char* description_init; + static const char *description_init; }; - /** - * This class is much like the - * Selection class, but it - * allows the input to be a - * comma-separated list of values - * which each have to be given in - * the constructor - * argument. Alternatively, it - * could be viewed as a - * specialization of the List - * class. For example, if the - * string to the constructor was - * "ucd|gmv|eps", then the - * following would be legal input: - * eps, gmv. You may give an - * arbitrarily long list of values, - * where there may be as many - * spaces around commas as you - * like. However, commas are not - * allowed inside the values given - * to the constructor. - */ + /** + * This class is much like the + * Selection class, but it + * allows the input to be a + * comma-separated list of values + * which each have to be given in + * the constructor + * argument. Alternatively, it + * could be viewed as a + * specialization of the List + * class. For example, if the + * string to the constructor was + * "ucd|gmv|eps", then the + * following would be legal input: + * eps, gmv. You may give an + * arbitrarily long list of values, + * where there may be as many + * spaces around commas as you + * like. However, commas are not + * allowed inside the values given + * to the constructor. + */ class MultipleSelection : public PatternBase { - public: - /** - * Constructor. Take the - * given parameter as the - * specification of valid - * strings. - */ - MultipleSelection (const std::string &seq); - - /** - * Return true if the - * string is an element of - * the description list - * passed to the constructor. - */ - virtual bool match (const std::string &test_string) const; - - /** - * Return a description of - * the pattern that valid - * strings are expected to - * match. Here, this is the - * list of valid strings - * passed to the constructor. - */ - virtual std::string description () const; - - /** - * Return a copy of the - * present object, which is - * newly allocated on the - * heap. Ownership of that - * object is transferred to - * the caller of this - * function. - */ - virtual PatternBase * clone () const; - - /** - * Creates new object if the start of - * description matches - * description_init. Ownership of that - * object is transferred to the caller - * of this function. - */ - static MultipleSelection* create (const std::string& description); - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this object. - */ - std::size_t memory_consumption () const; - - /** @addtogroup Exceptions - * @{ */ - - /** - * Exception. - */ - DeclException1 (ExcCommasNotAllowed, - int, - << "A comma was found at position " << arg1 - << " of your input string, but commas are not allowed here."); - //@} - private: - /** - * List of valid strings as - * passed to the - * constructor. We don't make - * this string constant, as - * we process it somewhat in - * the constructor. - */ - std::string sequence; - - /** - * Initial part of description - */ - static const char* description_init; + public: + /** + * Constructor. Take the + * given parameter as the + * specification of valid + * strings. + */ + MultipleSelection (const std::string &seq); + + /** + * Return true if the + * string is an element of + * the description list + * passed to the constructor. + */ + virtual bool match (const std::string &test_string) const; + + /** + * Return a description of + * the pattern that valid + * strings are expected to + * match. Here, this is the + * list of valid strings + * passed to the constructor. + */ + virtual std::string description () const; + + /** + * Return a copy of the + * present object, which is + * newly allocated on the + * heap. Ownership of that + * object is transferred to + * the caller of this + * function. + */ + virtual PatternBase *clone () const; + + /** + * Creates new object if the start of + * description matches + * description_init. Ownership of that + * object is transferred to the caller + * of this function. + */ + static MultipleSelection *create (const std::string &description); + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this object. + */ + std::size_t memory_consumption () const; + + /** @addtogroup Exceptions + * @{ */ + + /** + * Exception. + */ + DeclException1 (ExcCommasNotAllowed, + int, + << "A comma was found at position " << arg1 + << " of your input string, but commas are not allowed here."); + //@} + private: + /** + * List of valid strings as + * passed to the + * constructor. We don't make + * this string constant, as + * we process it somewhat in + * the constructor. + */ + std::string sequence; + + /** + * Initial part of description + */ + static const char *description_init; }; - /** - * Test for the string being - * either "true" or "false". This - * is mapped to the Selection - * class. - */ + /** + * Test for the string being + * either "true" or "false". This + * is mapped to the Selection + * class. + */ class Bool : public Selection { - public: - /** - * Constructor. - */ - Bool (); - - /** - * Return a description of - * the pattern that valid - * strings are expected to - * match. - */ - virtual std::string description () const; - - /** - * Return a copy of the - * present object, which is - * newly allocated on the - * heap. Ownership of that - * object is transferred to - * the caller of this - * function. - */ - virtual PatternBase * clone () const; - - /** - * Creates new object if the start of - * description matches - * description_init. Ownership of that - * object is transferred to the caller - * of this function. - */ - static Bool* create (const std::string& description); - - private: - /** - * Initial part of description - */ - static const char* description_init; + public: + /** + * Constructor. + */ + Bool (); + + /** + * Return a description of + * the pattern that valid + * strings are expected to + * match. + */ + virtual std::string description () const; + + /** + * Return a copy of the + * present object, which is + * newly allocated on the + * heap. Ownership of that + * object is transferred to + * the caller of this + * function. + */ + virtual PatternBase *clone () const; + + /** + * Creates new object if the start of + * description matches + * description_init. Ownership of that + * object is transferred to the caller + * of this function. + */ + static Bool *create (const std::string &description); + + private: + /** + * Initial part of description + */ + static const char *description_init; }; - /** - * Always returns true when testing a - * string. - */ + /** + * Always returns true when testing a + * string. + */ class Anything : public PatternBase { - public: - /** - * Constructor. (Allow for at - * least one non-virtual - * function in this class, as - * otherwise sometimes no - * virtual table is emitted.) - */ - Anything (); - - /** - * Return true if the - * string matches its - * constraints, i.e. always. - */ - virtual bool match (const std::string &test_string) const; - - /** - * Return a description of - * the pattern that valid - * strings are expected to - * match. Here, this is the - * string "[Anything]". - */ - virtual std::string description () const; - - /** - * Return a copy of the - * present object, which is - * newly allocated on the - * heap. Ownership of that - * object is transferred to - * the caller of this - * function. - */ - virtual PatternBase * clone () const; - - /** - * Creates new object if the start of - * description matches - * description_init. Ownership of that - * object is transferred to the caller - * of this function. - */ - static Anything* create (const std::string& description); - - private: - /** - * Initial part of description - */ - static const char* description_init; + public: + /** + * Constructor. (Allow for at + * least one non-virtual + * function in this class, as + * otherwise sometimes no + * virtual table is emitted.) + */ + Anything (); + + /** + * Return true if the + * string matches its + * constraints, i.e. always. + */ + virtual bool match (const std::string &test_string) const; + + /** + * Return a description of + * the pattern that valid + * strings are expected to + * match. Here, this is the + * string "[Anything]". + */ + virtual std::string description () const; + + /** + * Return a copy of the + * present object, which is + * newly allocated on the + * heap. Ownership of that + * object is transferred to + * the caller of this + * function. + */ + virtual PatternBase *clone () const; + + /** + * Creates new object if the start of + * description matches + * description_init. Ownership of that + * object is transferred to the caller + * of this function. + */ + static Anything *create (const std::string &description); + + private: + /** + * Initial part of description + */ + static const char *description_init; }; - /** - * A pattern that can be used to indicate - * when a parameter is intended to be the - * name of a file. By itself, this class - * does not check whether the string that - * is given in a parameter file actually - * corresponds to an existing file (it - * could, for example, be the name of a - * file to which you want to write - * output). Functionally, the class is - * therefore equivalent to the Anything - * class. However, it allows to specify the - * intent of a parameter. The flag - * given to the constructor also allows to - * specify whether the file is supposed to - * be an input or output file. - * - * The reason for the existence of this - * class is to support graphical user - * interfaces for editing parameter - * files. These may open a file selection - * dialog if the filename is supposed to - * represent an input file. - */ + /** + * A pattern that can be used to indicate + * when a parameter is intended to be the + * name of a file. By itself, this class + * does not check whether the string that + * is given in a parameter file actually + * corresponds to an existing file (it + * could, for example, be the name of a + * file to which you want to write + * output). Functionally, the class is + * therefore equivalent to the Anything + * class. However, it allows to specify the + * intent of a parameter. The flag + * given to the constructor also allows to + * specify whether the file is supposed to + * be an input or output file. + * + * The reason for the existence of this + * class is to support graphical user + * interfaces for editing parameter + * files. These may open a file selection + * dialog if the filename is supposed to + * represent an input file. + */ class FileName : public PatternBase { - public: - /** - * Files can be used for input - * or output. This can be - * specified in the constructor - * by choosing the flag type. - */ - enum FileType {input = 0, output = 1}; - - /** - * Constructor. The type of the file - * can be specified by choosing the - * flag. - */ - FileName (const FileType type = input); - - /** - * Return true if the - * string matches its - * constraints, i.e. always. - */ - virtual bool match (const std::string &test_string) const; - - /** - * Return a description of - * the pattern that valid - * strings are expected to - * match. Here, this is the - * string "[Filename]". - */ - virtual std::string description () const; - - /** - * Return a copy of the - * present object, which is - * newly allocated on the - * heap. Ownership of that - * object is transferred to - * the caller of this - * function. - */ - virtual PatternBase * clone () const; - - /** - * file type flag - */ - FileType file_type; - - /** - * Creates new object if the start of - * description matches - * description_init. Ownership of that - * object is transferred to the caller - * of this function. - */ - static FileName* create (const std::string& description); - - private: - /** - * Initial part of description - */ - static const char* description_init; + public: + /** + * Files can be used for input + * or output. This can be + * specified in the constructor + * by choosing the flag type. + */ + enum FileType {input = 0, output = 1}; + + /** + * Constructor. The type of the file + * can be specified by choosing the + * flag. + */ + FileName (const FileType type = input); + + /** + * Return true if the + * string matches its + * constraints, i.e. always. + */ + virtual bool match (const std::string &test_string) const; + + /** + * Return a description of + * the pattern that valid + * strings are expected to + * match. Here, this is the + * string "[Filename]". + */ + virtual std::string description () const; + + /** + * Return a copy of the + * present object, which is + * newly allocated on the + * heap. Ownership of that + * object is transferred to + * the caller of this + * function. + */ + virtual PatternBase *clone () const; + + /** + * file type flag + */ + FileType file_type; + + /** + * Creates new object if the start of + * description matches + * description_init. Ownership of that + * object is transferred to the caller + * of this function. + */ + static FileName *create (const std::string &description); + + private: + /** + * Initial part of description + */ + static const char *description_init; }; - /** - * A pattern that can be used to indicate - * when a parameter is intended to be the - * name of a directory. By itself, this - * class does not check whether the string - * that is given in a parameter file - * actually corresponds to an existing - * directory. Functionally, the class is - * therefore equivalent to the Anything - * class. However, it allows to specify the - * intent of a parameter. - * - * The reason for the existence of this - * class is to support graphical user - * interfaces for editing parameter - * files. These may open a file selection - * dialog to select or create a directory. - */ + /** + * A pattern that can be used to indicate + * when a parameter is intended to be the + * name of a directory. By itself, this + * class does not check whether the string + * that is given in a parameter file + * actually corresponds to an existing + * directory. Functionally, the class is + * therefore equivalent to the Anything + * class. However, it allows to specify the + * intent of a parameter. + * + * The reason for the existence of this + * class is to support graphical user + * interfaces for editing parameter + * files. These may open a file selection + * dialog to select or create a directory. + */ class DirectoryName : public PatternBase { - public: - /** - * Constructor. - */ - DirectoryName (); - - /** - * Return true if the - * string matches its - * constraints, i.e. always. - */ - virtual bool match (const std::string &test_string) const; - - /** - * Return a description of - * the pattern that valid - * strings are expected to - * match. Here, this is the - * string "[Filename]". - */ - virtual std::string description () const; - - /** - * Return a copy of the - * present object, which is - * newly allocated on the - * heap. Ownership of that - * object is transferred to - * the caller of this - * function. - */ - virtual PatternBase * clone () const; - - /** - * Creates new object if the start of - * description matches - * description_init. Ownership of that - * object is transferred to the caller - * of this function. - */ - static DirectoryName* create (const std::string& description); - - private: - /** - * Initial part of description - */ - static const char* description_init; + public: + /** + * Constructor. + */ + DirectoryName (); + + /** + * Return true if the + * string matches its + * constraints, i.e. always. + */ + virtual bool match (const std::string &test_string) const; + + /** + * Return a description of + * the pattern that valid + * strings are expected to + * match. Here, this is the + * string "[Filename]". + */ + virtual std::string description () const; + + /** + * Return a copy of the + * present object, which is + * newly allocated on the + * heap. Ownership of that + * object is transferred to + * the caller of this + * function. + */ + virtual PatternBase *clone () const; + + /** + * Creates new object if the start of + * description matches + * description_init. Ownership of that + * object is transferred to the caller + * of this function. + */ + static DirectoryName *create (const std::string &description); + + private: + /** + * Initial part of description + */ + static const char *description_init; }; } @@ -1785,639 +1785,640 @@ namespace Patterns */ class ParameterHandler : public Subscriptor { - private: - /** - * Inhibit automatic - * CopyConstructor. - */ - ParameterHandler (const ParameterHandler&); - - /** - * Inhibit automatic - * assignment operator. - */ - ParameterHandler& operator= (const ParameterHandler&); +private: + /** + * Inhibit automatic + * CopyConstructor. + */ + ParameterHandler (const ParameterHandler &); + + /** + * Inhibit automatic + * assignment operator. + */ + ParameterHandler &operator= (const ParameterHandler &); + +public: + /** + * List of possible output + * formats. + * + * The formats down the list with + * prefix Short and bit + * 6 and 7 set reproduce the old + * behavior of not writing + * comments or original values to + * the files. + */ + enum OutputStyle + { + /** + * Write human readable + * output suitable to be + * read by ParameterHandler + * again. + */ + Text = 1, + /** + * Write parameters as a + * LaTeX table. + */ + LaTeX = 2, + /** + * Write out declared parameters + * with description and possible + * values. + */ + Description = 3, - public: - /** - * List of possible output - * formats. - * - * The formats down the list with - * prefix Short and bit - * 6 and 7 set reproduce the old - * behavior of not writing - * comments or original values to - * the files. - */ - enum OutputStyle { - /** - * Write human readable - * output suitable to be - * read by ParameterHandler - * again. - */ - Text = 1, - /** - * Write parameters as a - * LaTeX table. - */ - LaTeX = 2, - /** - * Write out declared parameters - * with description and possible - * values. - */ - Description = 3, - - /** - * Write out everything as - * an XML - * file. - * - * See the general - * documentation of this - * class for an example of - * output. - */ - XML = 4, - - /** - * Write out everything as - * a JSON - * file. - */ - JSON = 5, - - /** - * Write input for - * ParameterHandler without - * comments or changed - * default values. - */ - ShortText = 193 - }; + /** + * Write out everything as + * an XML + * file. + * + * See the general + * documentation of this + * class for an example of + * output. + */ + XML = 4, + + /** + * Write out everything as + * a JSON + * file. + */ + JSON = 5, + /** + * Write input for + * ParameterHandler without + * comments or changed + * default values. + */ + ShortText = 193 + }; - /** - * Constructor. - */ - ParameterHandler (); - - /** - * Destructor. Declare this only - * to have a virtual destructor, - * which is safer as we have - * virtual functions. It - * actually does nothing - * spectacular. - */ - virtual ~ParameterHandler (); - - /** - * Read input from a stream until - * stream returns eof - * condition or error. - * - * Return whether the read was - * successful. - */ - virtual bool read_input (std::istream &input); - - /** - * Read input from a file the - * name of which is given. The - * PathSearch class "PARAMETERS" - * is used to find the file. - * - * Return whether the read was - * successful. - * - * Unless optional is - * true, this function - * will automatically generate - * the requested file with - * default values if the file did - * not exist. This file will not - * contain additional comments if - * write_stripped_file - * is true. - */ - virtual bool read_input (const std::string &filename, - const bool optional = false, - const bool write_stripped_file = false); - - /** - * Read input from a string in - * memory. The lines in memory - * have to be separated by @\n - * characters. - * - * Return whether the read was - * successful. - */ - virtual bool read_input_from_string (const char *s); - - /** - * Read a parameter file in XML - * format. This could be from a file - * originally written by the - * print_parameters() function using the - * XML output style and then modified by - * hand as necessary; or from a file - * written using this method and then - * modified by the graphical parameter - * GUI (see the general documentation of - * this class). - * - * Return whether the read was - * successful. - */ - virtual bool read_input_from_xml (std::istream &input); - - /** - * Clear all contents. - */ - void clear (); - - - /** - * Declare a new entry with name - * entry, default and for - * which any input has to match - * the pattern (default: any - * pattern). - * - * The last parameter defaulting - * to an empty string is used to - * add a documenting text to each - * entry which will be printed as - * a comment when this class is - * asked to write out all - * declarations to a stream using - * the print_parameters() - * function. - * - * The function generates an - * exception if the default value - * doesn't match the given - * pattern. An entry can be - * declared more than once - * without generating an error, - * for example to override an - * earlier default value. - */ - void declare_entry (const std::string &entry, - const std::string &default_value, - const Patterns::PatternBase &pattern = Patterns::Anything(), - const std::string &documentation = std::string()); - - /** - * Enter a subsection; if not yet - * existent, declare it. - */ - void enter_subsection (const std::string &subsection); - - /** - * Leave present subsection. - * Return false if there is - * no subsection to leave; true - * otherwise. - */ - bool leave_subsection (); - - /** - * Return value of entry - * entry_string. If the - * entry was changed, then the - * changed value is returned, - * otherwise the default - * value. If the value of an - * undeclared entry is required, - * an exception will be thrown. - */ - std::string get (const std::string &entry_string) const; - - /** - * Return value of entry - * entry_string as long - * int. (A long int is chosen so - * that even very large unsigned values - * can be returned by this function). - */ - long int get_integer (const std::string &entry_string) const; - - /** - * Return value of entry - * entry_name as - * double. - */ - double get_double (const std::string &entry_name) const; - - /** - * Return value of entry - * entry_name as bool. - * The entry may be "true" or "yes" - * for true, "false" or - * "no" for false respectively. - */ - bool get_bool (const std::string &entry_name) const; - - /** - * Change the value presently stored for - * entry_name to the one given - * in the second argument. - * - * The parameter must already exist in - * the present subsection. - * - * The function throws an - * exception of type - * ExcValueDoesNotMatchPattern - * if the new value does not - * conform to the pattern for - * this entry. - */ - void set (const std::string &entry_name, - const std::string &new_value); - - /** - * Same as above, but an overload where - * the second argument is a character - * pointer. This is necessary, since - * otherwise the call to - * set("abc","def") will be - * mapped to the function taking one - * string and a bool as arguments, which - * is certainly not what is most often - * intended. - * - * The function throws an - * exception of type - * ExcValueDoesNotMatchPattern - * if the new value does not - * conform to the pattern for - * this entry. - */ - void set (const std::string &entry_name, - const char *new_value); - - /** - * Change the value presently stored for - * entry_name to the one given - * in the second argument. - * - * The parameter must already exist in - * the present subsection. - * - * The function throws an - * exception of type - * ExcValueDoesNotMatchPattern - * if the new value does not - * conform to the pattern for - * this entry. - */ - void set (const std::string &entry_name, - const long int &new_value); - - /** - * Change the value presently stored for - * entry_name to the one given - * in the second argument. - * - * The parameter must already exist in - * the present subsection. - * - * For internal purposes, the new value - * needs to be converted to a - * string. This is done using 16 digits - * of accuracy, so the set value and the - * one you can get back out using - * get_double() may differ in the 16th - * digit. - * - * The function throws an - * exception of type - * ExcValueDoesNotMatchPattern - * if the new value does not - * conform to the pattern for - * this entry. - */ - void set (const std::string &entry_name, - const double &new_value); - - /** - * Change the value presently stored for - * entry_name to the one given - * in the second argument. - * - * The parameter must already exist in - * the present subsection. - * - * The function throws an - * exception of type - * ExcValueDoesNotMatchPattern - * if the new value does not - * conform to the pattern for - * this entry. - */ - void set (const std::string &entry_name, - const bool &new_value); - - - /** - * Print all parameters with the - * given style to - * out. Presently only - * Text and LaTeX are - * implemented. - * - * In Text format, the output - * is formatted in such a way - * that it is possible to use it - * for later input again. This is - * most useful to record the - * parameters for a specific run, - * since if you output the - * parameters using this function - * into a log file, you can - * always recover the results by - * simply copying the output to - * your input file. - * - * Besides the name and value of - * each entry, the output also - * contains the default value of - * entries if it is different - * from the actual value, as well - * as the documenting string - * given to the - * declare_entry() function - * if available. - * - * In Text format, the - * output contains the same information - * but in a format so that the - * resulting file can be input into - * a latex document such as a manual - * for the code for which this object - * handles run-time parameters. - * The various sections of parameters - * are then represented by latex - * section and subsection commands - * as well as by nested enumerations. - * - * In addition, all parameter names - * are listed with @\index - * statements in two indices called - * prmindex (where the - * name of each parameter is listed - * in the index) and prmindexfull - * where parameter names are listed - * sorted by the section in which - * they exist. By default, the LaTeX - * program ignores these @\index - * commands, but they can be used to - * generate an index by using the - * following commands in the preamble - * of the latex file: - * @code - * \usepackage{imakeidx} - * \makeindex[name=prmindex, title=Index of run-time parameter entries] - * \makeindex[name=prmindexfull, title=Index of run-time parameters with section names] - * @endcode - * and at the end of the file - * this: - * @code - * \printindex[prmindex] - * \printindex[prmindexfull] - * @endcode - */ - std::ostream & print_parameters (std::ostream &out, - const OutputStyle style); - - /** - * Print out the parameters of - * the present subsection as - * given by the - * subsection_path member - * variable. This variable is - * controlled by entering and - * leaving subsections through - * the enter_subsection() and - * leave_subsection() - * functions. - * - * In most cases, you will not - * want to use this function - * directly, but have it called - * recursively by the previous - * function. - */ - void print_parameters_section (std::ostream &out, - const OutputStyle style, - const unsigned int indent_level); - - /** - * Print parameters to a - * logstream. This function - * allows to print all parameters - * into a log-file. Sections - * will be indented in the usual - * log-file style. - */ - void log_parameters (LogStream& out); - - /** - * Log parameters in the present - * subsection. The subsection is - * determined by the - * subsection_path member - * variable. This variable is - * controlled by entering and - * leaving subsections through - * the enter_subsection() and - * leave_subsection() - * functions. - * - * In most cases, you will not - * want to use this function - * directly, but have it called - * recursively by the previous - * function. - */ - void log_parameters_section (LogStream& out); - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. - */ - std::size_t memory_consumption () const; - /** - * Write the data of this object to a - * stream for the purpose of - * serialization. - */ - template - void save (Archive & ar, const unsigned int version) const; - - /** - * Read the data of this object from a - * stream for the purpose of - * serialization. - */ - template - void load (Archive & ar, const unsigned int version); - - BOOST_SERIALIZATION_SPLIT_MEMBER() - - /** - * Test for equality. - */ - bool operator == (const ParameterHandler & prm2) const; - - /** @addtogroup Exceptions - * @{ */ - - /** - * Exception - */ - DeclException1 (ExcEntryAlreadyExists, - std::string, - << "The following entry already exists: " << arg1); - /** - * Exception - */ - DeclException2 (ExcValueDoesNotMatchPattern, - std::string, std::string, - << "The string <" << arg1 - << "> does not match the given pattern <" << arg2 << ">"); - /** - * Exception - */ - DeclException0 (ExcAlreadyAtTopLevel); - /** - * Exception - */ - DeclException1 (ExcEntryUndeclared, - std::string, - << "You can't ask for entry <" << arg1 << "> you have not yet declared"); - /** - * Exception - */ - DeclException1 (ExcConversionError, - std::string, - << "Error when trying to convert the following string: " << arg1); - //@} - private: - /** - * The separator used when - * accessing elements of a path - * into the parameter tree. - */ - static const char path_separator = '.'; - - /** - * The complete tree of sections - * and entries. See the general - * documentation of this class - * for a description how data is - * stored in this variable. - * - * The variable is a pointer so - * that we can use an incomplete - * type, rather than having to - * include all of the - * property_tree stuff from - * boost. This works around a - * problem with gcc 4.5. - */ - std::auto_ptr entries; - - /** - * A list of patterns that are - * used to describe the - * parameters of this object. The - * are indexed by nodes in the - * property tree. - */ - std::vector > patterns; - - /** - * Mangle a string so that it - * doesn't contain any special - * characters or spaces. - */ - static std::string mangle (const std::string &s); - - /** - * Unmangle a string into its - * original form. - */ - static std::string demangle (const std::string &s); - - /** - * Return whether a given node is - * a parameter node or a - * subsection node. - */ - static bool is_parameter_node (const boost::property_tree::ptree &); - - /** - * Path of presently selected - * subsections; empty list means - * top level - */ - std::vector subsection_path; + /** + * Constructor. + */ + ParameterHandler (); + + /** + * Destructor. Declare this only + * to have a virtual destructor, + * which is safer as we have + * virtual functions. It + * actually does nothing + * spectacular. + */ + virtual ~ParameterHandler (); + + /** + * Read input from a stream until + * stream returns eof + * condition or error. + * + * Return whether the read was + * successful. + */ + virtual bool read_input (std::istream &input); + + /** + * Read input from a file the + * name of which is given. The + * PathSearch class "PARAMETERS" + * is used to find the file. + * + * Return whether the read was + * successful. + * + * Unless optional is + * true, this function + * will automatically generate + * the requested file with + * default values if the file did + * not exist. This file will not + * contain additional comments if + * write_stripped_file + * is true. + */ + virtual bool read_input (const std::string &filename, + const bool optional = false, + const bool write_stripped_file = false); + + /** + * Read input from a string in + * memory. The lines in memory + * have to be separated by @\n + * characters. + * + * Return whether the read was + * successful. + */ + virtual bool read_input_from_string (const char *s); + + /** + * Read a parameter file in XML + * format. This could be from a file + * originally written by the + * print_parameters() function using the + * XML output style and then modified by + * hand as necessary; or from a file + * written using this method and then + * modified by the graphical parameter + * GUI (see the general documentation of + * this class). + * + * Return whether the read was + * successful. + */ + virtual bool read_input_from_xml (std::istream &input); + + /** + * Clear all contents. + */ + void clear (); + + + /** + * Declare a new entry with name + * entry, default and for + * which any input has to match + * the pattern (default: any + * pattern). + * + * The last parameter defaulting + * to an empty string is used to + * add a documenting text to each + * entry which will be printed as + * a comment when this class is + * asked to write out all + * declarations to a stream using + * the print_parameters() + * function. + * + * The function generates an + * exception if the default value + * doesn't match the given + * pattern. An entry can be + * declared more than once + * without generating an error, + * for example to override an + * earlier default value. + */ + void declare_entry (const std::string &entry, + const std::string &default_value, + const Patterns::PatternBase &pattern = Patterns::Anything(), + const std::string &documentation = std::string()); + + /** + * Enter a subsection; if not yet + * existent, declare it. + */ + void enter_subsection (const std::string &subsection); + + /** + * Leave present subsection. + * Return false if there is + * no subsection to leave; true + * otherwise. + */ + bool leave_subsection (); + + /** + * Return value of entry + * entry_string. If the + * entry was changed, then the + * changed value is returned, + * otherwise the default + * value. If the value of an + * undeclared entry is required, + * an exception will be thrown. + */ + std::string get (const std::string &entry_string) const; + + /** + * Return value of entry + * entry_string as long + * int. (A long int is chosen so + * that even very large unsigned values + * can be returned by this function). + */ + long int get_integer (const std::string &entry_string) const; + + /** + * Return value of entry + * entry_name as + * double. + */ + double get_double (const std::string &entry_name) const; + + /** + * Return value of entry + * entry_name as bool. + * The entry may be "true" or "yes" + * for true, "false" or + * "no" for false respectively. + */ + bool get_bool (const std::string &entry_name) const; + + /** + * Change the value presently stored for + * entry_name to the one given + * in the second argument. + * + * The parameter must already exist in + * the present subsection. + * + * The function throws an + * exception of type + * ExcValueDoesNotMatchPattern + * if the new value does not + * conform to the pattern for + * this entry. + */ + void set (const std::string &entry_name, + const std::string &new_value); + + /** + * Same as above, but an overload where + * the second argument is a character + * pointer. This is necessary, since + * otherwise the call to + * set("abc","def") will be + * mapped to the function taking one + * string and a bool as arguments, which + * is certainly not what is most often + * intended. + * + * The function throws an + * exception of type + * ExcValueDoesNotMatchPattern + * if the new value does not + * conform to the pattern for + * this entry. + */ + void set (const std::string &entry_name, + const char *new_value); + + /** + * Change the value presently stored for + * entry_name to the one given + * in the second argument. + * + * The parameter must already exist in + * the present subsection. + * + * The function throws an + * exception of type + * ExcValueDoesNotMatchPattern + * if the new value does not + * conform to the pattern for + * this entry. + */ + void set (const std::string &entry_name, + const long int &new_value); + + /** + * Change the value presently stored for + * entry_name to the one given + * in the second argument. + * + * The parameter must already exist in + * the present subsection. + * + * For internal purposes, the new value + * needs to be converted to a + * string. This is done using 16 digits + * of accuracy, so the set value and the + * one you can get back out using + * get_double() may differ in the 16th + * digit. + * + * The function throws an + * exception of type + * ExcValueDoesNotMatchPattern + * if the new value does not + * conform to the pattern for + * this entry. + */ + void set (const std::string &entry_name, + const double &new_value); + + /** + * Change the value presently stored for + * entry_name to the one given + * in the second argument. + * + * The parameter must already exist in + * the present subsection. + * + * The function throws an + * exception of type + * ExcValueDoesNotMatchPattern + * if the new value does not + * conform to the pattern for + * this entry. + */ + void set (const std::string &entry_name, + const bool &new_value); + + + /** + * Print all parameters with the + * given style to + * out. Presently only + * Text and LaTeX are + * implemented. + * + * In Text format, the output + * is formatted in such a way + * that it is possible to use it + * for later input again. This is + * most useful to record the + * parameters for a specific run, + * since if you output the + * parameters using this function + * into a log file, you can + * always recover the results by + * simply copying the output to + * your input file. + * + * Besides the name and value of + * each entry, the output also + * contains the default value of + * entries if it is different + * from the actual value, as well + * as the documenting string + * given to the + * declare_entry() function + * if available. + * + * In Text format, the + * output contains the same information + * but in a format so that the + * resulting file can be input into + * a latex document such as a manual + * for the code for which this object + * handles run-time parameters. + * The various sections of parameters + * are then represented by latex + * section and subsection commands + * as well as by nested enumerations. + * + * In addition, all parameter names + * are listed with @\index + * statements in two indices called + * prmindex (where the + * name of each parameter is listed + * in the index) and prmindexfull + * where parameter names are listed + * sorted by the section in which + * they exist. By default, the LaTeX + * program ignores these @\index + * commands, but they can be used to + * generate an index by using the + * following commands in the preamble + * of the latex file: + * @code + * \usepackage{imakeidx} + * \makeindex[name=prmindex, title=Index of run-time parameter entries] + * \makeindex[name=prmindexfull, title=Index of run-time parameters with section names] + * @endcode + * and at the end of the file + * this: + * @code + * \printindex[prmindex] + * \printindex[prmindexfull] + * @endcode + */ + std::ostream &print_parameters (std::ostream &out, + const OutputStyle style); + + /** + * Print out the parameters of + * the present subsection as + * given by the + * subsection_path member + * variable. This variable is + * controlled by entering and + * leaving subsections through + * the enter_subsection() and + * leave_subsection() + * functions. + * + * In most cases, you will not + * want to use this function + * directly, but have it called + * recursively by the previous + * function. + */ + void print_parameters_section (std::ostream &out, + const OutputStyle style, + const unsigned int indent_level); + + /** + * Print parameters to a + * logstream. This function + * allows to print all parameters + * into a log-file. Sections + * will be indented in the usual + * log-file style. + */ + void log_parameters (LogStream &out); + + /** + * Log parameters in the present + * subsection. The subsection is + * determined by the + * subsection_path member + * variable. This variable is + * controlled by entering and + * leaving subsections through + * the enter_subsection() and + * leave_subsection() + * functions. + * + * In most cases, you will not + * want to use this function + * directly, but have it called + * recursively by the previous + * function. + */ + void log_parameters_section (LogStream &out); + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. + */ + std::size_t memory_consumption () const; + + /** + * Write the data of this object to a + * stream for the purpose of + * serialization. + */ + template + void save (Archive &ar, const unsigned int version) const; + + /** + * Read the data of this object from a + * stream for the purpose of + * serialization. + */ + template + void load (Archive &ar, const unsigned int version); + + BOOST_SERIALIZATION_SPLIT_MEMBER() + + /** + * Test for equality. + */ + bool operator == (const ParameterHandler &prm2) const; + + /** @addtogroup Exceptions + * @{ */ + + /** + * Exception + */ + DeclException1 (ExcEntryAlreadyExists, + std::string, + << "The following entry already exists: " << arg1); + /** + * Exception + */ + DeclException2 (ExcValueDoesNotMatchPattern, + std::string, std::string, + << "The string <" << arg1 + << "> does not match the given pattern <" << arg2 << ">"); + /** + * Exception + */ + DeclException0 (ExcAlreadyAtTopLevel); + /** + * Exception + */ + DeclException1 (ExcEntryUndeclared, + std::string, + << "You can't ask for entry <" << arg1 << "> you have not yet declared"); + /** + * Exception + */ + DeclException1 (ExcConversionError, + std::string, + << "Error when trying to convert the following string: " << arg1); + //@} +private: + /** + * The separator used when + * accessing elements of a path + * into the parameter tree. + */ + static const char path_separator = '.'; + + /** + * The complete tree of sections + * and entries. See the general + * documentation of this class + * for a description how data is + * stored in this variable. + * + * The variable is a pointer so + * that we can use an incomplete + * type, rather than having to + * include all of the + * property_tree stuff from + * boost. This works around a + * problem with gcc 4.5. + */ + std::auto_ptr entries; + + /** + * A list of patterns that are + * used to describe the + * parameters of this object. The + * are indexed by nodes in the + * property tree. + */ + std::vector > patterns; + + /** + * Mangle a string so that it + * doesn't contain any special + * characters or spaces. + */ + static std::string mangle (const std::string &s); + + /** + * Unmangle a string into its + * original form. + */ + static std::string demangle (const std::string &s); + + /** + * Return whether a given node is + * a parameter node or a + * subsection node. + */ + static bool is_parameter_node (const boost::property_tree::ptree &); + + /** + * Path of presently selected + * subsections; empty list means + * top level + */ + std::vector subsection_path; + + /** + * Return the string that + * identifies the current path + * into the property tree. This + * is only a path, i.e. it is not + * terminated by the + * path_separator character. + */ + std::string get_current_path () const; + + /** + * Given the name of an entry as + * argument, the function + * computes a full path into the + * parameter tree using the + * current subsection. + */ + std::string get_current_full_path (const std::string &name) const; - /** - * Return the string that - * identifies the current path - * into the property tree. This - * is only a path, i.e. it is not - * terminated by the - * path_separator character. - */ - std::string get_current_path () const; - - /** - * Given the name of an entry as - * argument, the function - * computes a full path into the - * parameter tree using the - * current subsection. - */ - std::string get_current_full_path (const std::string &name) const; - - /** - * Scan one line of input. - * lineno is the number of - * the line presently scanned - * (for the logs if there are - * messages). Return false if - * line contained stuff that - * could not be understood, the - * uppermost subsection was to be - * left by an END or end - * statement, a value for a - * non-declared entry was given - * or the entry value did not - * match the regular - * expression. true otherwise. - * - * The function modifies its - * argument, but also takes it by - * value, so the caller's - * variable is not changed. - */ - bool scan_line (std::string line, - const unsigned int lineno); - - friend class MultipleParameterLoop; + /** + * Scan one line of input. + * lineno is the number of + * the line presently scanned + * (for the logs if there are + * messages). Return false if + * line contained stuff that + * could not be understood, the + * uppermost subsection was to be + * left by an END or end + * statement, a value for a + * non-declared entry was given + * or the entry value did not + * match the regular + * expression. true otherwise. + * + * The function modifies its + * argument, but also takes it by + * value, so the caller's + * variable is not changed. + */ + bool scan_line (std::string line, + const unsigned int lineno); + + friend class MultipleParameterLoop; }; @@ -2634,210 +2635,210 @@ class ParameterHandler : public Subscriptor */ class MultipleParameterLoop : public ParameterHandler { +public: + /** + * This is the class the helper class or the + * problem class has to be derived of. + */ + class UserClass + { + public: + /** + * Destructor. It doesn't actually do + * anything, but is declared to force + * derived classes to have a virtual + * destructor. + */ + virtual ~UserClass (); + + /** + * create_new must provide a clean + * object, either by creating a new one + * or by cleaning an old one. + */ + virtual void create_new (const unsigned int run_no) = 0; + + /** + * This should declare parameters and call + * the declare_parameters function of the + * problem class. + */ + virtual void declare_parameters (ParameterHandler &prm) = 0; + + /** + * Get the parameters and run any + * necessary action. + */ + virtual void run (ParameterHandler &prm) = 0; + }; + + /** + * Constructor + */ + MultipleParameterLoop (); + + /** + * Destructor. Declare this only to have + * a virtual destructor, which is safer + * as we have virtual functions. + * It actually does nothing spectacular. + */ + virtual ~MultipleParameterLoop (); + + virtual bool read_input (std::istream &Input); + virtual bool read_input (const std::string &FileName, + const bool optional = false, + const bool write_stripped_file = false); + + /** + * Read input from a string in memory. The + * lines in memory have to be separated by + * @\n characters. + */ + virtual bool read_input_from_string (const char *s); + + /** + * run the central loop. + */ + void loop (UserClass &uc); + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. + */ + std::size_t memory_consumption () const; + +private: + + /** + * An object in the list of entries with + * multiple values. + */ + class Entry + { public: - /** - * This is the class the helper class or the - * problem class has to be derived of. - */ - class UserClass + /** + * Declare what a multiple entry + * is: a variant * entry (in + * curly braces {, }) or an + * array (in double curly braces + * {{, }}). + */ + enum MultipleEntryType { - public: - /** - * Destructor. It doesn't actually do - * anything, but is declared to force - * derived classes to have a virtual - * destructor. - */ - virtual ~UserClass (); - - /** - * create_new must provide a clean - * object, either by creating a new one - * or by cleaning an old one. - */ - virtual void create_new (const unsigned int run_no) = 0; - - /** - * This should declare parameters and call - * the declare_parameters function of the - * problem class. - */ - virtual void declare_parameters (ParameterHandler &prm) = 0; - - /** - * Get the parameters and run any - * necessary action. - */ - virtual void run (ParameterHandler &prm) = 0; + variant, array }; - /** - * Constructor - */ - MultipleParameterLoop (); - - /** - * Destructor. Declare this only to have - * a virtual destructor, which is safer - * as we have virtual functions. - * It actually does nothing spectacular. - */ - virtual ~MultipleParameterLoop (); - - virtual bool read_input (std::istream &Input); - virtual bool read_input (const std::string &FileName, - const bool optional = false, - const bool write_stripped_file = false); - - /** - * Read input from a string in memory. The - * lines in memory have to be separated by - * @\n characters. - */ - virtual bool read_input_from_string (const char *s); - - /** - * run the central loop. - */ - void loop (UserClass &uc); - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. - */ + /** + * Constructor + */ + Entry () : type (array) {} + + /** + * Construct an object with given subsection + * path, name and value. The splitting up + * into the different variants is done + * later by split_different_values. + */ + Entry (const std::vector &Path, + const std::string &Name, + const std::string &Value); + + /** + * Split the entry value into the different + * branches. + */ + void split_different_values (); + + /** + * Path to variant entry. + */ + std::vector subsection_path; + + /** + * Name of entry. + */ + std::string entry_name; + + /** + * Original variant value. + */ + std::string entry_value; + + /** + * List of entry values constructed out of + * what was given in the input file (that + * is stored in EntryValue. + */ + std::vector different_values; + + /** + * Store whether this entry is a variant + * entry or an array. + */ + MultipleEntryType type; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. + */ std::size_t memory_consumption () const; + }; - private: + /** + * List of variant entry values. + */ + std::vector multiple_choices; - /** - * An object in the list of entries with - * multiple values. - */ - class Entry - { - public: - /** - * Declare what a multiple entry - * is: a variant * entry (in - * curly braces {, }) or an - * array (in double curly braces - * {{, }}). - */ - enum MultipleEntryType - { - variant, array - }; - - /** - * Constructor - */ - Entry () : type (array) {} - - /** - * Construct an object with given subsection - * path, name and value. The splitting up - * into the different variants is done - * later by split_different_values. - */ - Entry (const std::vector &Path, - const std::string &Name, - const std::string &Value); - - /** - * Split the entry value into the different - * branches. - */ - void split_different_values (); - - /** - * Path to variant entry. - */ - std::vector subsection_path; - - /** - * Name of entry. - */ - std::string entry_name; - - /** - * Original variant value. - */ - std::string entry_value; - - /** - * List of entry values constructed out of - * what was given in the input file (that - * is stored in EntryValue. - */ - std::vector different_values; - - /** - * Store whether this entry is a variant - * entry or an array. - */ - MultipleEntryType type; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. - */ - std::size_t memory_consumption () const; - }; + /** + * Number of branches constructed + * from the different + * combinations of the variants. + * This obviously equals the + * number of runs to be + * performed. + */ + unsigned int n_branches; - /** - * List of variant entry values. - */ - std::vector multiple_choices; - - /** - * Number of branches constructed - * from the different - * combinations of the variants. - * This obviously equals the - * number of runs to be - * performed. - */ - unsigned int n_branches; - - /** - * Initialize the different - * branches, i.e. construct the - * combinations. - */ - void init_branches (); - - /** - * Traverse the section currently - * set by - * enter_subsection()/leave_subsection() - * and see which of the entries - * are variante/array - * entries. Then fill the - * multiple_choices variable - * using this information. - */ - void init_branches_current_section (); - - /** - * Transfer the entry values for one run - * to the entry tree. - */ - void fill_entry_values (const unsigned int run_no); + /** + * Initialize the different + * branches, i.e. construct the + * combinations. + */ + void init_branches (); + + /** + * Traverse the section currently + * set by + * enter_subsection()/leave_subsection() + * and see which of the entries + * are variante/array + * entries. Then fill the + * multiple_choices variable + * using this information. + */ + void init_branches_current_section (); + + /** + * Transfer the entry values for one run + * to the entry tree. + */ + void fill_entry_values (const unsigned int run_no); }; template inline void -ParameterHandler::save (Archive & ar, const unsigned int) const +ParameterHandler::save (Archive &ar, const unsigned int) const { - // Forward to serialization - // function in the base class. - ar & static_cast(*this); + // Forward to serialization + // function in the base class. + ar &static_cast(*this); ar & *entries.get(); @@ -2846,23 +2847,23 @@ ParameterHandler::save (Archive & ar, const unsigned int) const for (unsigned int j=0; jdescription()); - ar & descriptions; + ar &descriptions; } template inline void -ParameterHandler::load (Archive & ar, const unsigned int) +ParameterHandler::load (Archive &ar, const unsigned int) { - // Forward to serialization - // function in the base class. - ar & static_cast(*this); + // Forward to serialization + // function in the base class. + ar &static_cast(*this); ar & *entries.get(); std::vector descriptions; - ar & descriptions; + ar &descriptions; patterns.clear (); for (unsigned int j=0; j::declare_parameters(prm, dim); - * prm.leave_subsection(); - * - * // Create a ParsedFunction - * ParsedFunction my_vector_function(dim); - * - * // Parse an input file. - * prm.read_input(some_input_file); - * - * // Initialize the ParsedFunction object with the given file - * prm.enter_subsection("My vector function"); - * my_vector_function.parse_parameters(prm); - * prm.leave_subsection(); - * - * @endcode - * - * And here is an example of how the input parameter could look like - * (see the documentation of the FunctionParser class for a detailed - * description of the syntax of the function definition): - * - * @code - * - * # A test two dimensional vector function, depending on time - * subsection My vector function - * set Function constants = kappa=.1, lambda=2. - * set Function expression = if(y>.5, kappa*x*(1-x),0); t^2*cos(lambda*pi*x) - * set Variable names = x,y,t - * end - * - * @endcode - * - * @ingroup functions - * @author Luca Heltai, 2006 - */ + /** + * Friendly interface to the FunctionParser class. This class is + * meant as a wrapper for the FunctionParser class. It is used in the + * step-34 tutorial program. + * + * It provides two + * methods to declare and parse a ParameterHandler object and creates + * the Function object declared in the parameter file. This class is + * derived from the AutoDerivativeFunction class, so you don't need + * to specify derivatives. An example of usage of this class is as follows: + * + * @code + * // A parameter handler + * ParameterHandler prm; + * + * // Declare a section for the function we need + * prm.enter_subsection("My vector function"); + * ParsedFunction::declare_parameters(prm, dim); + * prm.leave_subsection(); + * + * // Create a ParsedFunction + * ParsedFunction my_vector_function(dim); + * + * // Parse an input file. + * prm.read_input(some_input_file); + * + * // Initialize the ParsedFunction object with the given file + * prm.enter_subsection("My vector function"); + * my_vector_function.parse_parameters(prm); + * prm.leave_subsection(); + * + * @endcode + * + * And here is an example of how the input parameter could look like + * (see the documentation of the FunctionParser class for a detailed + * description of the syntax of the function definition): + * + * @code + * + * # A test two dimensional vector function, depending on time + * subsection My vector function + * set Function constants = kappa=.1, lambda=2. + * set Function expression = if(y>.5, kappa*x*(1-x),0); t^2*cos(lambda*pi*x) + * set Variable names = x,y,t + * end + * + * @endcode + * + * @ingroup functions + * @author Luca Heltai, 2006 + */ template class ParsedFunction : public AutoDerivativeFunction { - public: - /** - * Construct a vector - * function. The vector - * function which is generated - * has @p n_components - * components (defaults to - * 1). The parameter @p h is - * used to initialize the - * AutoDerivativeFunction class - * from which this class is - * derived. - */ - ParsedFunction (const unsigned int n_components = 1, const double h=1e-8); + public: + /** + * Construct a vector + * function. The vector + * function which is generated + * has @p n_components + * components (defaults to + * 1). The parameter @p h is + * used to initialize the + * AutoDerivativeFunction class + * from which this class is + * derived. + */ + ParsedFunction (const unsigned int n_components = 1, const double h=1e-8); - /** - * Declare parameters needed by - * this class. The additional - * parameter @p n_components is - * used to generate the right - * code according to the number - * of components of the - * function that will parse - * this ParameterHandler. If - * the number of components - * which is parsed does not - * match the number of - * components of this object, - * an assertion is thrown and - * the program is aborted. The - * default behavior for this - * class is to declare the - * following entries: - * - * @code - * - * set Function constants = - * set Function expression = 0 - * set Variable names = x,y,t - * - * @endcode - * - */ - static void declare_parameters(ParameterHandler &prm, - const unsigned int n_components = 1); + /** + * Declare parameters needed by + * this class. The additional + * parameter @p n_components is + * used to generate the right + * code according to the number + * of components of the + * function that will parse + * this ParameterHandler. If + * the number of components + * which is parsed does not + * match the number of + * components of this object, + * an assertion is thrown and + * the program is aborted. The + * default behavior for this + * class is to declare the + * following entries: + * + * @code + * + * set Function constants = + * set Function expression = 0 + * set Variable names = x,y,t + * + * @endcode + * + */ + static void declare_parameters(ParameterHandler &prm, + const unsigned int n_components = 1); - /** - * Parse parameters needed by - * this class. If the number - * of components which is - * parsed does not match the - * number of components of this - * object, an assertion is - * thrown and the program is - * aborted. In order for the - * class to function properly, - * we follow the same - * convenctions declared in the - * FunctionParser class (look - * there for a detailed - * description of the syntax - * for function declarations). - * - * The three variables that can - * be parsed from a parameter - * file are the following: - * - * @code - * - * set Function constants = - * set Function expression = - * set Variable names = - * - * @endcode - * - * Function constants is a - * collection of pairs in the - * form name=value, separated - * by commas, for example: - * - * @code - * - * set Function constants = lambda=1. , alpha=2., gamma=3. - * - * @endcode - * - * These constants can be used - * in the declaration of the - * function expression, which - * follows the convention of - * the FunctionParser - * class. In order to specify - * vector functions, - * semicolons have to be used - * to separate the different - * components, e.g.: - * - * @code - * - * set Function expression = cos(pi*x) ; cos(pi*y) - * - * @endcode - * - * The variable names entry - * can be used to customize - * the name of the variables - * used in the Function. It - * defaults to - * - * @code - * - * set Variable names = x,t - * - * @endcode - * - * for one dimensional problems, - * - * @code - * - * set Variable names = x,y,t - * - * @endcode - * - * for two dimensional problems and - * - * @code - * - * set Variable names = x,y,z,t - * - * @endcode - * - * for three dimensional problems. - * - * The time variable can be - * set according to - * specifications in the - * FunctionTime base class. - */ - void parse_parameters(ParameterHandler &prm); + /** + * Parse parameters needed by + * this class. If the number + * of components which is + * parsed does not match the + * number of components of this + * object, an assertion is + * thrown and the program is + * aborted. In order for the + * class to function properly, + * we follow the same + * convenctions declared in the + * FunctionParser class (look + * there for a detailed + * description of the syntax + * for function declarations). + * + * The three variables that can + * be parsed from a parameter + * file are the following: + * + * @code + * + * set Function constants = + * set Function expression = + * set Variable names = + * + * @endcode + * + * Function constants is a + * collection of pairs in the + * form name=value, separated + * by commas, for example: + * + * @code + * + * set Function constants = lambda=1. , alpha=2., gamma=3. + * + * @endcode + * + * These constants can be used + * in the declaration of the + * function expression, which + * follows the convention of + * the FunctionParser + * class. In order to specify + * vector functions, + * semicolons have to be used + * to separate the different + * components, e.g.: + * + * @code + * + * set Function expression = cos(pi*x) ; cos(pi*y) + * + * @endcode + * + * The variable names entry + * can be used to customize + * the name of the variables + * used in the Function. It + * defaults to + * + * @code + * + * set Variable names = x,t + * + * @endcode + * + * for one dimensional problems, + * + * @code + * + * set Variable names = x,y,t + * + * @endcode + * + * for two dimensional problems and + * + * @code + * + * set Variable names = x,y,z,t + * + * @endcode + * + * for three dimensional problems. + * + * The time variable can be + * set according to + * specifications in the + * FunctionTime base class. + */ + void parse_parameters(ParameterHandler &prm); - /** - * Get one value at the given - * point. - */ - virtual void vector_value (const Point &p, - Vector &values) const; + /** + * Get one value at the given + * point. + */ + virtual void vector_value (const Point &p, + Vector &values) const; - /** - * Return the value of the - * function at the given - * point. Unless there is only - * one component (i.e. the - * function is scalar), you - * should state the component - * you want to have evaluated; - * it defaults to zero, - * i.e. the first component. - */ - virtual double value (const Point< dim > & p, - const unsigned int component = 0) const; + /** + * Return the value of the + * function at the given + * point. Unless there is only + * one component (i.e. the + * function is scalar), you + * should state the component + * you want to have evaluated; + * it defaults to zero, + * i.e. the first component. + */ + virtual double value (const Point< dim > &p, + const unsigned int component = 0) const; - /** - * Set the time to a specific - * value for time-dependent - * functions. - * - * We need to overwrite this to - * set the time also in the - * accessor - * FunctionParser. - */ - virtual void set_time(const double newtime); + /** + * Set the time to a specific + * value for time-dependent + * functions. + * + * We need to overwrite this to + * set the time also in the + * accessor + * FunctionParser. + */ + virtual void set_time(const double newtime); - private: - /** - * The object with which we do - * computations. - */ - FunctionParser function_object; + private: + /** + * The object with which we do + * computations. + */ + FunctionParser function_object; }; } diff --git a/deal.II/include/deal.II/base/partitioner.h b/deal.II/include/deal.II/base/partitioner.h index 101c00544c..fff826924b 100644 --- a/deal.II/include/deal.II/base/partitioner.h +++ b/deal.II/include/deal.II/base/partitioner.h @@ -28,351 +28,351 @@ namespace Utilities { namespace MPI { - /** - * This class defines a model for the partitioning of a vector (or, - * in fact, any linear data structure) among processors using - * MPI. - * - * The partitioner stores the global vector size and the locally - * owned range as a half-open interval [@p lower, @p - * upper). Furthermore, it includes a structure for the - * point-to-point communication patterns. It allows the inclusion of - * ghost indices (i.e. indices that a current processor needs to - * have access to, but are owned by another process) through an - * IndexSet. In addition, it also stores the other processors' ghost - * indices belonging to the current processor, which are the indices - * where other processors might require information from. In a - * sense, these import indices form the dual of the ghost - * indices. This information is gathered once when constructing the - * partitioner, which obviates subsequent global communcation steps - * when exchanging data. - * - * The partitioner includes a mechanism for converting global to - * local and local to global indices. The storage convention for the - * partitioner is as follows. The local range is associated with - * local indices [0,@p local_size), and ghost indices are stored - * consecutively in [@p local_size, @p local_size + @p - * n_ghost_indices). The ghost indices are sorted according to their - * global index. - * - * - * @author Katharina Kormann, Martin Kronbichler, 2010, 2011 - */ - class Partitioner - { - public: - /** - * Empty Constructor. - */ - Partitioner (); - - /** - * Constructor with size argument. Creates an - * MPI_COMM_SELF structure where there is no - * real parallel layout. - */ - Partitioner (const unsigned int size); - - /** - * Constructor with index set arguments. This - * constructor creates a distributed layout - * based on a given communicators, an - * IndexSet describing the locally owned - * range and another one for describing ghost - * indices that are owned by other - * processors, but we need to have read or - * write access to. - */ - Partitioner (const IndexSet &locally_owned_indices, - const IndexSet &ghost_indices_in, - const MPI_Comm communicator_in); - - /** - * Constructor with one index set argument. This - * constructor creates a distributed layout - * based on a given communicator, and an IndexSet - * describing the locally owned range. It - * allows to set the ghost indices at a later - * time. Apart from this, it is similar to the - * other constructor with two index sets. - */ - Partitioner (const IndexSet &locally_owned_indices, - const MPI_Comm communicator_in); - - /** - * Sets the locally owned indices. Used in the - * constructor. - */ - void set_owned_indices (const IndexSet &locally_owned_indices); - - /** - * Allows to set the ghost indices after the - * constructor has been called. - */ - void set_ghost_indices (const IndexSet &ghost_indices); - - /** - * Returns the global size. - */ - types::global_dof_index size() const; - - /** - * Returns the local size, i.e. - * local_range().second minus - * local_range().first. - */ - unsigned int local_size() const; - - /** - * Returns an IndexSet representation of the - * local range. This class only supports - * contiguous local ranges, so the IndexSet - * actually only consists of one single range - * of data, and is equivalent to the result of - * local_range(). - */ - const IndexSet & locally_owned_range() const; - - /** - * Returns the local range. The returned pair - * consists of the index of the first element - * and the index of the element one past the - * last locally owned one. - */ - std::pair - local_range() const; - - /** - * Returns true if the given global index is - * in the local range of this processor. - */ - bool in_local_range (const types::global_dof_index global_index) const; - - /** - * Returns the local index corresponding to - * the given global index. If the given global - * index is neither locally owned nor a ghost, - * an exception is thrown. - * - * Note that the local index for locally owned - * indices is between 0 and local_size()-1, - * and the local index for ghosts is between - * local_size() and - * local_size()+n_ghost_indices()-1. - */ - unsigned int - global_to_local (const types::global_dof_index global_index) const; - - /** - * Returns the global index corresponding to - * the given local index. - * - * Note that the local index for locally owned - * indices is between 0 and local_size()-1, - * and the local index for ghosts is between - * local_size() and - * local_size()+n_ghost_indices()-1. - */ - types::global_dof_index - local_to_global (const unsigned int local_index) const; - - /** - * Returns whether the given global index is a - * ghost index on the present - * processor. Returns false for indices that - * are owned locally and for indices not - * present at all. - */ - bool is_ghost_entry (const types::global_dof_index global_index) const; - - /** - * Returns an IndexSet representation of all - * ghost indices. - */ - const IndexSet& ghost_indices() const; - - /** - * Returns the number of ghost indices. Same - * as ghost_indices().n_elements(), but cached - * for simpler access. - */ - unsigned int n_ghost_indices() const; - - /** - * Returns a list of processors (first entry) - * and the number of degrees of freedom for - * the individual processor on the ghost - * elements present (second entry). - */ - const std::vector >& - ghost_targets() const; - - /** - * The set of (local) indices that we are - * importing during compress(), i.e., others' - * ghosts that belong to the local - * range. Similar structure as in an IndexSet, - * but tailored to be iterated over, and some - * indices may be duplicates. - */ - const std::vector >& - import_indices() const; - - /** - * Number of import indices, i.e., indices - * that are ghosts on other processors and we - * will receive data from. - */ - unsigned int n_import_indices() const; - - /** - * Returns a list of processors (first entry) - * and the number of degrees of freedom for - * all the processors that data is obtained - * from (second entry), i.e., locally owned - * indices that are ghosts on other - * processors. - */ - const std::vector >& - import_targets() const; - - /** - * Checks whether the given - * partitioner is compatible with the - * partitioner used for this - * vector. Two partitioners are - * compatible if the have the same - * local size and the same ghost - * indices. They do not necessarily - * need to be the same data - * field. This is a local operation - * only, i.e., if only some - * processors decide that the - * partitioning is not compatible, - * only these processors will return - * @p false, whereas the other - * processors will return @p true. - */ - bool is_compatible (const Partitioner &part) const; - - /** - * Returns the MPI ID of the calling - * processor. Cached to have simple access. - */ - unsigned int this_mpi_process () const; - - /** - * Returns the total number of MPI processor - * participating in the given - * partitioner. Cached to have simple access. - */ - unsigned int n_mpi_processes () const; - - /** - * Returns the MPI communicator underlying the - * partitioner object. - */ - MPI_Comm get_communicator() const; - - /** - * Computes the memory consumption of this - * structure. - */ - std::size_t memory_consumption() const; - - /** - * Exception - */ - DeclException2 (ExcIndexNotPresent, - unsigned int, unsigned int, - << "Global index " << arg1 - << " neither owned nor ghost on proc " << arg2); - - private: /** + /** + * This class defines a model for the partitioning of a vector (or, + * in fact, any linear data structure) among processors using + * MPI. + * + * The partitioner stores the global vector size and the locally + * owned range as a half-open interval [@p lower, @p + * upper). Furthermore, it includes a structure for the + * point-to-point communication patterns. It allows the inclusion of + * ghost indices (i.e. indices that a current processor needs to + * have access to, but are owned by another process) through an + * IndexSet. In addition, it also stores the other processors' ghost + * indices belonging to the current processor, which are the indices + * where other processors might require information from. In a + * sense, these import indices form the dual of the ghost + * indices. This information is gathered once when constructing the + * partitioner, which obviates subsequent global communcation steps + * when exchanging data. + * + * The partitioner includes a mechanism for converting global to + * local and local to global indices. The storage convention for the + * partitioner is as follows. The local range is associated with + * local indices [0,@p local_size), and ghost indices are stored + * consecutively in [@p local_size, @p local_size + @p + * n_ghost_indices). The ghost indices are sorted according to their + * global index. + * + * + * @author Katharina Kormann, Martin Kronbichler, 2010, 2011 + */ + class Partitioner + { + public: + /** + * Empty Constructor. + */ + Partitioner (); + + /** + * Constructor with size argument. Creates an + * MPI_COMM_SELF structure where there is no + * real parallel layout. + */ + Partitioner (const unsigned int size); + + /** + * Constructor with index set arguments. This + * constructor creates a distributed layout + * based on a given communicators, an + * IndexSet describing the locally owned + * range and another one for describing ghost + * indices that are owned by other + * processors, but we need to have read or + * write access to. + */ + Partitioner (const IndexSet &locally_owned_indices, + const IndexSet &ghost_indices_in, + const MPI_Comm communicator_in); + + /** + * Constructor with one index set argument. This + * constructor creates a distributed layout + * based on a given communicator, and an IndexSet + * describing the locally owned range. It + * allows to set the ghost indices at a later + * time. Apart from this, it is similar to the + * other constructor with two index sets. + */ + Partitioner (const IndexSet &locally_owned_indices, + const MPI_Comm communicator_in); + + /** + * Sets the locally owned indices. Used in the + * constructor. + */ + void set_owned_indices (const IndexSet &locally_owned_indices); + + /** + * Allows to set the ghost indices after the + * constructor has been called. + */ + void set_ghost_indices (const IndexSet &ghost_indices); + + /** + * Returns the global size. + */ + types::global_dof_index size() const; + + /** + * Returns the local size, i.e. + * local_range().second minus + * local_range().first. + */ + unsigned int local_size() const; + + /** + * Returns an IndexSet representation of the + * local range. This class only supports + * contiguous local ranges, so the IndexSet + * actually only consists of one single range + * of data, and is equivalent to the result of + * local_range(). + */ + const IndexSet &locally_owned_range() const; + + /** + * Returns the local range. The returned pair + * consists of the index of the first element + * and the index of the element one past the + * last locally owned one. + */ + std::pair + local_range() const; + + /** + * Returns true if the given global index is + * in the local range of this processor. + */ + bool in_local_range (const types::global_dof_index global_index) const; + + /** + * Returns the local index corresponding to + * the given global index. If the given global + * index is neither locally owned nor a ghost, + * an exception is thrown. + * + * Note that the local index for locally owned + * indices is between 0 and local_size()-1, + * and the local index for ghosts is between + * local_size() and + * local_size()+n_ghost_indices()-1. + */ + unsigned int + global_to_local (const types::global_dof_index global_index) const; + + /** + * Returns the global index corresponding to + * the given local index. + * + * Note that the local index for locally owned + * indices is between 0 and local_size()-1, + * and the local index for ghosts is between + * local_size() and + * local_size()+n_ghost_indices()-1. + */ + types::global_dof_index + local_to_global (const unsigned int local_index) const; + + /** + * Returns whether the given global index is a + * ghost index on the present + * processor. Returns false for indices that + * are owned locally and for indices not + * present at all. + */ + bool is_ghost_entry (const types::global_dof_index global_index) const; + + /** + * Returns an IndexSet representation of all + * ghost indices. + */ + const IndexSet &ghost_indices() const; + + /** + * Returns the number of ghost indices. Same + * as ghost_indices().n_elements(), but cached + * for simpler access. + */ + unsigned int n_ghost_indices() const; + + /** + * Returns a list of processors (first entry) + * and the number of degrees of freedom for + * the individual processor on the ghost + * elements present (second entry). + */ + const std::vector > & + ghost_targets() const; + + /** + * The set of (local) indices that we are + * importing during compress(), i.e., others' + * ghosts that belong to the local + * range. Similar structure as in an IndexSet, + * but tailored to be iterated over, and some + * indices may be duplicates. + */ + const std::vector > & + import_indices() const; + + /** + * Number of import indices, i.e., indices + * that are ghosts on other processors and we + * will receive data from. + */ + unsigned int n_import_indices() const; + + /** + * Returns a list of processors (first entry) + * and the number of degrees of freedom for + * all the processors that data is obtained + * from (second entry), i.e., locally owned + * indices that are ghosts on other + * processors. + */ + const std::vector > & + import_targets() const; + + /** + * Checks whether the given + * partitioner is compatible with the + * partitioner used for this + * vector. Two partitioners are + * compatible if the have the same + * local size and the same ghost + * indices. They do not necessarily + * need to be the same data + * field. This is a local operation + * only, i.e., if only some + * processors decide that the + * partitioning is not compatible, + * only these processors will return + * @p false, whereas the other + * processors will return @p true. + */ + bool is_compatible (const Partitioner &part) const; + + /** + * Returns the MPI ID of the calling + * processor. Cached to have simple access. + */ + unsigned int this_mpi_process () const; + + /** + * Returns the total number of MPI processor + * participating in the given + * partitioner. Cached to have simple access. + */ + unsigned int n_mpi_processes () const; + + /** + * Returns the MPI communicator underlying the + * partitioner object. + */ + MPI_Comm get_communicator() const; + + /** + * Computes the memory consumption of this + * structure. + */ + std::size_t memory_consumption() const; + + /** + * Exception + */ + DeclException2 (ExcIndexNotPresent, + unsigned int, unsigned int, + << "Global index " << arg1 + << " neither owned nor ghost on proc " << arg2); + + private: /** * The global size of the vector over all * processors */ - const types::global_dof_index global_size; - - /** - * The range of the vector that is stored - * locally. - */ - IndexSet locally_owned_range_data; - - /** - * The range of the vector that is stored - * locally. Extracted from locally_owned_range - * for performance reasons. - */ - std::pair local_range_data; - - /** - * The set of indices to which we need to have - * read access but that are not locally owned - */ - IndexSet ghost_indices_data; - - /** - * Caches the number of ghost indices. It - * would be expensive to use @p - * ghost_indices.n_elements() to compute this. - */ - unsigned int n_ghost_indices_data; - - /** - * Contains information which processors my - * ghost indices belong to and how many those - * indices are - */ - std::vector > ghost_targets_data; - - /** - * The set of (local) indices that we are - * importing during compress(), i.e., others' - * ghosts that belong to the local - * range. Similar structure as in an IndexSet, - * but tailored to be iterated over, and some - * indices may be duplicates. - */ - std::vector > import_indices_data; - - /** - * Caches the number of ghost indices. It - * would be expensive to compute it by - * iterating over the import indices and - * accumulate them. - */ - unsigned int n_import_indices_data; - - /** - * The set of processors and length of data - * field which send us their ghost data - */ - std::vector > import_targets_data; - - /** - * The ID of the current processor in the MPI - * network - */ - unsigned int my_pid; - - /** - * The total number of processors active in - * the problem - */ - unsigned int n_procs; - - /** - * The MPI communicator involved in the - * problem - */ - const MPI_Comm communicator; - }; - - - -/*----------------------- Inline functions ----------------------------------*/ + const types::global_dof_index global_size; + + /** + * The range of the vector that is stored + * locally. + */ + IndexSet locally_owned_range_data; + + /** + * The range of the vector that is stored + * locally. Extracted from locally_owned_range + * for performance reasons. + */ + std::pair local_range_data; + + /** + * The set of indices to which we need to have + * read access but that are not locally owned + */ + IndexSet ghost_indices_data; + + /** + * Caches the number of ghost indices. It + * would be expensive to use @p + * ghost_indices.n_elements() to compute this. + */ + unsigned int n_ghost_indices_data; + + /** + * Contains information which processors my + * ghost indices belong to and how many those + * indices are + */ + std::vector > ghost_targets_data; + + /** + * The set of (local) indices that we are + * importing during compress(), i.e., others' + * ghosts that belong to the local + * range. Similar structure as in an IndexSet, + * but tailored to be iterated over, and some + * indices may be duplicates. + */ + std::vector > import_indices_data; + + /** + * Caches the number of ghost indices. It + * would be expensive to compute it by + * iterating over the import indices and + * accumulate them. + */ + unsigned int n_import_indices_data; + + /** + * The set of processors and length of data + * field which send us their ghost data + */ + std::vector > import_targets_data; + + /** + * The ID of the current processor in the MPI + * network + */ + unsigned int my_pid; + + /** + * The total number of processors active in + * the problem + */ + unsigned int n_procs; + + /** + * The MPI communicator involved in the + * problem + */ + const MPI_Comm communicator; + }; + + + + /*----------------------- Inline functions ----------------------------------*/ #ifndef DOXYGEN @@ -385,7 +385,7 @@ namespace Utilities inline - const IndexSet& Partitioner::locally_owned_range() const + const IndexSet &Partitioner::locally_owned_range() const { return locally_owned_range_data; } @@ -424,8 +424,8 @@ namespace Utilities bool Partitioner::is_ghost_entry (const types::global_dof_index global_index) const { - // if the index is in the global range, it is - // trivially not a ghost + // if the index is in the global range, it is + // trivially not a ghost if (in_local_range(global_index) == true) return false; else @@ -446,11 +446,11 @@ namespace Utilities return (local_size() + ghost_indices_data.index_within_set (global_index)); else - // should only end up here in - // optimized mode, when we use this - // large number to trigger a segfault - // when using this method for array - // access + // should only end up here in + // optimized mode, when we use this + // large number to trigger a segfault + // when using this method for array + // access return numbers::invalid_unsigned_int; } @@ -470,7 +470,7 @@ namespace Utilities inline - const IndexSet& Partitioner::ghost_indices() const + const IndexSet &Partitioner::ghost_indices() const { return ghost_indices_data; } @@ -487,7 +487,7 @@ namespace Utilities inline - const std::vector >& + const std::vector > & Partitioner::ghost_targets() const { return ghost_targets_data; @@ -495,7 +495,7 @@ namespace Utilities inline - const std::vector >& + const std::vector > & Partitioner::import_indices() const { return import_indices_data; @@ -513,7 +513,7 @@ namespace Utilities inline - const std::vector >& + const std::vector > & Partitioner::import_targets() const { return import_targets_data; @@ -525,8 +525,8 @@ namespace Utilities bool Partitioner::is_compatible (const Partitioner &part) const { - // is the partitioner points to the same - // memory location as the calling processor + // is the partitioner points to the same + // memory location as the calling processor if (&part == this) return true; else @@ -540,11 +540,11 @@ namespace Utilities unsigned int Partitioner::this_mpi_process() const { - // return the id from the variable stored in - // this class instead of - // Utilities::MPI::this_mpi_process() in order - // to make this query also work when MPI is - // not initialized. + // return the id from the variable stored in + // this class instead of + // Utilities::MPI::this_mpi_process() in order + // to make this query also work when MPI is + // not initialized. return my_pid; } @@ -554,11 +554,11 @@ namespace Utilities unsigned int Partitioner::n_mpi_processes() const { - // return the number of MPI processes from the - // variable stored in this class instead of - // Utilities::MPI::n_mpi_processes() in order - // to make this query also work when MPI is - // not initialized. + // return the number of MPI processes from the + // variable stored in this class instead of + // Utilities::MPI::n_mpi_processes() in order + // to make this query also work when MPI is + // not initialized. return n_procs; } diff --git a/deal.II/include/deal.II/base/path_search.h b/deal.II/include/deal.II/base/path_search.h index 9b5e758ee4..c9c3ea70e3 100644 --- a/deal.II/include/deal.II/base/path_search.h +++ b/deal.II/include/deal.II/base/path_search.h @@ -83,221 +83,221 @@ DEAL_II_NAMESPACE_OPEN */ class PathSearch { - public: - /** - * Position for adding a new item - * to a list. - */ - enum Position - { - /// Add new item at end of list - back, - /// Add new item at front of list - front, - /// Add in path list after empty element - after_none - }; - - /** - * Constructor. The first argument is a - * string identifying the class of files - * to be searched for. - * - * The debug argument determines - * the verbosity of this class. - */ - PathSearch (const std::string& cls, - const unsigned int debug=0); - - /** - * Find a file in the class - * specified by the constructor - * and return its complete path - * name (including a possible - * suffix). - * - * File search works by actually - * trying to open the file. If @p - * fopen is successful with the - * provided @p open_mode, then - * the file is found, otherwise - * the search continues. - * - * @warning Be careful with @p - * open_mode! In particular, use - * "w" with great care! - * If the file does not exist, it - * cannot be found. If it does - * exist, the @p fopen function - * will truncate it to zero - * length. - * - * @param filename The base - * name of the file to be found, - * without path components and - * suffix. - * @param open_mode The mode - * handed over to the @p fopen - * function. - */ - std::string find (const std::string& filename, - const char* open_mode = "r"); - - /** - * Find a file in the class - * specified by the constructor - * and return its complete path - * name. Do not use the standard - * suffix list, but only try to - * apply the suffix given. - * - * File search works by actually - * trying to open the file. If @p - * fopen is successful with the - * provided @p open_mode, then - * the file is found, otherwise - * the search continues. - * - * @warning Be careful with @p - * open_mode! In particular, use - * "w" with great care! - * If the file does not exist, it - * cannot be found. If it does - * exist, the @p fopen function - * will truncate it to zero - * length. - * - * @param filename The base - * name of the file to be found, - * without path components and - * suffix. - * @param suffix The suffix to be - * used for opening. - * @param open_mode The mode - * handed over to the @p fopen - * function. - */ - std::string find (const std::string& filename, - const std::string& suffix, - const char* open_mode = "r"); - - /** - * Show the paths and suffixes - * used for this object. - */ - template - void show(STREAM& stream) const; - - /** - * Add a new class. - */ - static void add_class (const std::string& cls); - - /** - * Add a path to the current - * class. See - * PathSearch::Position for - * possible position arguments. - */ - void add_path (const std::string& path, Position pos = back); - - /** - * Add a path to the current - * class. See - * PathSearch::Position for - * possible position arguments. - */ - void add_suffix (const std::string& suffix, Position pos = back); - - /** - * This class was not - * registered in the path - * search mechanism. - * @ingroup Exceptions - */ - DeclException1(ExcNoClass, - std::string, - << "The class " - << arg1 - << " must be registered before referring it in PathSearch"); - /** - * The PathSearch class could not - * find a file with this name in - * its path list. - * @ingroup Exceptions - */ - DeclException2(ExcFileNotFound, - std::string, std::string, - << "The file \"" << arg1 - << "\" was not found in the path for files of class " - << arg2); - - private: - /** - * Type of values in the class - * maps. - */ - typedef std::map >::value_type map_type; - - /** - * Initialize the static list - * objects for further use. - */ - static void initialize_classes(); - - /** - * Get path list for a certain - * class. Used to set up - * #my_path_list in constructor. - */ - static std::vector& get_path_list(const std::string& cls); - - /** - * Get suffix list for a certain - * class. Used to set up - * #my_suffix_list in constructor. - */ - static std::vector& get_suffix_list(const std::string& cls); - - /** - * The file class handled by this object. - */ - const std::string cls; - - /** - * All path lists for all - * classes, such that we can - * build them only once. - */ - static std::map > path_lists; - - /** - * List of suffixes for each - * class. - */ - static std::map > suffix_lists; - - /** - * Path list for the class this - * object belongs to. - */ - std::vector& my_path_list; - - /** - * Suffix list for the class this - * object belongs to. - */ - std::vector& my_suffix_list; - - /** - * Debug flag. No output if zero. - */ - const unsigned int debug; - - /** - * The empty string. - */ - static std::string empty; +public: + /** + * Position for adding a new item + * to a list. + */ + enum Position + { + /// Add new item at end of list + back, + /// Add new item at front of list + front, + /// Add in path list after empty element + after_none + }; + + /** + * Constructor. The first argument is a + * string identifying the class of files + * to be searched for. + * + * The debug argument determines + * the verbosity of this class. + */ + PathSearch (const std::string &cls, + const unsigned int debug=0); + + /** + * Find a file in the class + * specified by the constructor + * and return its complete path + * name (including a possible + * suffix). + * + * File search works by actually + * trying to open the file. If @p + * fopen is successful with the + * provided @p open_mode, then + * the file is found, otherwise + * the search continues. + * + * @warning Be careful with @p + * open_mode! In particular, use + * "w" with great care! + * If the file does not exist, it + * cannot be found. If it does + * exist, the @p fopen function + * will truncate it to zero + * length. + * + * @param filename The base + * name of the file to be found, + * without path components and + * suffix. + * @param open_mode The mode + * handed over to the @p fopen + * function. + */ + std::string find (const std::string &filename, + const char *open_mode = "r"); + + /** + * Find a file in the class + * specified by the constructor + * and return its complete path + * name. Do not use the standard + * suffix list, but only try to + * apply the suffix given. + * + * File search works by actually + * trying to open the file. If @p + * fopen is successful with the + * provided @p open_mode, then + * the file is found, otherwise + * the search continues. + * + * @warning Be careful with @p + * open_mode! In particular, use + * "w" with great care! + * If the file does not exist, it + * cannot be found. If it does + * exist, the @p fopen function + * will truncate it to zero + * length. + * + * @param filename The base + * name of the file to be found, + * without path components and + * suffix. + * @param suffix The suffix to be + * used for opening. + * @param open_mode The mode + * handed over to the @p fopen + * function. + */ + std::string find (const std::string &filename, + const std::string &suffix, + const char *open_mode = "r"); + + /** + * Show the paths and suffixes + * used for this object. + */ + template + void show(STREAM &stream) const; + + /** + * Add a new class. + */ + static void add_class (const std::string &cls); + + /** + * Add a path to the current + * class. See + * PathSearch::Position for + * possible position arguments. + */ + void add_path (const std::string &path, Position pos = back); + + /** + * Add a path to the current + * class. See + * PathSearch::Position for + * possible position arguments. + */ + void add_suffix (const std::string &suffix, Position pos = back); + + /** + * This class was not + * registered in the path + * search mechanism. + * @ingroup Exceptions + */ + DeclException1(ExcNoClass, + std::string, + << "The class " + << arg1 + << " must be registered before referring it in PathSearch"); + /** + * The PathSearch class could not + * find a file with this name in + * its path list. + * @ingroup Exceptions + */ + DeclException2(ExcFileNotFound, + std::string, std::string, + << "The file \"" << arg1 + << "\" was not found in the path for files of class " + << arg2); + +private: + /** + * Type of values in the class + * maps. + */ + typedef std::map >::value_type map_type; + + /** + * Initialize the static list + * objects for further use. + */ + static void initialize_classes(); + + /** + * Get path list for a certain + * class. Used to set up + * #my_path_list in constructor. + */ + static std::vector &get_path_list(const std::string &cls); + + /** + * Get suffix list for a certain + * class. Used to set up + * #my_suffix_list in constructor. + */ + static std::vector &get_suffix_list(const std::string &cls); + + /** + * The file class handled by this object. + */ + const std::string cls; + + /** + * All path lists for all + * classes, such that we can + * build them only once. + */ + static std::map > path_lists; + + /** + * List of suffixes for each + * class. + */ + static std::map > suffix_lists; + + /** + * Path list for the class this + * object belongs to. + */ + std::vector &my_path_list; + + /** + * Suffix list for the class this + * object belongs to. + */ + std::vector &my_suffix_list; + + /** + * Debug flag. No output if zero. + */ + const unsigned int debug; + + /** + * The empty string. + */ + static std::string empty; }; @@ -307,7 +307,7 @@ class PathSearch template inline void -PathSearch::show(STREAM& out) const +PathSearch::show(STREAM &out) const { out << "DEAL_II_" << cls << "PATH=\""; bool first = true; diff --git a/deal.II/include/deal.II/base/point.h b/deal.II/include/deal.II/base/point.h index b94c66b704..b91947b89f 100644 --- a/deal.II/include/deal.II/base/point.h +++ b/deal.II/include/deal.II/base/point.h @@ -50,145 +50,145 @@ DEAL_II_NAMESPACE_OPEN template class Point : public Tensor<1,dim,Number> { - public: - /** - * Standard constructor. Creates - * an origin. - */ - Point (); - - /** - * Constructor. Initialize all - * entries to zero if - * initialize==true. - */ - explicit Point (const bool initialize); - - /** - * Convert a tensor to a point. Since no - * additional data is inside a point, - * this is ok. - */ - Point (const Tensor<1,dim,Number> &); - - /** - * Constructor for one dimensional - * points. This function is only - * implemented for dim==1 since - * the usage is considered unsafe for - * points with dim!=1. - */ - explicit Point (const Number x); - - /** - * Constructor for two dimensional - * points. This function is only - * implemented for dim==2 since - * the usage is considered unsafe for - * points with dim!=2. - */ - Point (const Number x, const Number y); - - /** - * Constructor for three dimensional - * points. This function is only - * implemented for dim==3 since - * the usage is considered unsafe for - * points with dim!=3. - */ - Point (const Number x, const Number y, const Number z); - - /** - * Return a unit vector in - * coordinate direction i. - */ - static Point unit_vector(const unsigned int i); - - /** - * Read access to the indexth - * coordinate. - */ - Number operator () (const unsigned int index) const; - - /** - * Read and write access to the - * indexth coordinate. - */ - Number & operator () (const unsigned int index); - -/* - * Plus and minus operators are re-implemented from Tensor<1,dim> - * to avoid additional casting. - */ - - /** - * Add two point vectors. If possible, - * use operator += instead - * since this does not need to copy a - * point at least once. - */ - Point operator + (const Tensor<1,dim,Number>&) const; - - /** - * Subtract two point vectors. If - * possible, use operator += - * instead since this does not need to - * copy a point at least once. - */ - Point operator - (const Tensor<1,dim,Number>&) const; - - /** - * The opposite vector. - */ - Point operator - () const; - - /** - * Multiply by a factor. If possible, - * use operator *= instead - * since this does not need to copy a - * point at least once. - * - * There is a commutative complement to this - * function also - */ - Point operator * (const Number) const; - - /** - * Returns the scalar product of two - * vectors. - */ - Number operator * (const Tensor<1,dim,Number> &) const; - - /** - * Divide by a factor. If possible, use - * operator /= instead since - * this does not need to copy a point at - * least once. - */ - Point operator / (const Number) const; - - /** - * Returns the scalar product of this - * point vector with itself, i.e. the - * square, or the square of the norm. - */ - Number square () const; - - /** - * Returns the Euclidian distance of - * this point to the point - * p, i.e. the l_2 norm - * of the difference between the vectors - * representing the two points. - */ - Number distance (const Point &p) const; - - /** - * Read or write the data of this object to or - * from a stream for the purpose of serialization - */ - template - void serialize(Archive & ar, const unsigned int version); +public: + /** + * Standard constructor. Creates + * an origin. + */ + Point (); + + /** + * Constructor. Initialize all + * entries to zero if + * initialize==true. + */ + explicit Point (const bool initialize); + + /** + * Convert a tensor to a point. Since no + * additional data is inside a point, + * this is ok. + */ + Point (const Tensor<1,dim,Number> &); + + /** + * Constructor for one dimensional + * points. This function is only + * implemented for dim==1 since + * the usage is considered unsafe for + * points with dim!=1. + */ + explicit Point (const Number x); + + /** + * Constructor for two dimensional + * points. This function is only + * implemented for dim==2 since + * the usage is considered unsafe for + * points with dim!=2. + */ + Point (const Number x, const Number y); + + /** + * Constructor for three dimensional + * points. This function is only + * implemented for dim==3 since + * the usage is considered unsafe for + * points with dim!=3. + */ + Point (const Number x, const Number y, const Number z); + + /** + * Return a unit vector in + * coordinate direction i. + */ + static Point unit_vector(const unsigned int i); + + /** + * Read access to the indexth + * coordinate. + */ + Number operator () (const unsigned int index) const; + + /** + * Read and write access to the + * indexth coordinate. + */ + Number &operator () (const unsigned int index); + + /* + * Plus and minus operators are re-implemented from Tensor<1,dim> + * to avoid additional casting. + */ + + /** + * Add two point vectors. If possible, + * use operator += instead + * since this does not need to copy a + * point at least once. + */ + Point operator + (const Tensor<1,dim,Number> &) const; + + /** + * Subtract two point vectors. If + * possible, use operator += + * instead since this does not need to + * copy a point at least once. + */ + Point operator - (const Tensor<1,dim,Number> &) const; + + /** + * The opposite vector. + */ + Point operator - () const; + + /** + * Multiply by a factor. If possible, + * use operator *= instead + * since this does not need to copy a + * point at least once. + * + * There is a commutative complement to this + * function also + */ + Point operator * (const Number) const; + + /** + * Returns the scalar product of two + * vectors. + */ + Number operator * (const Tensor<1,dim,Number> &) const; + + /** + * Divide by a factor. If possible, use + * operator /= instead since + * this does not need to copy a point at + * least once. + */ + Point operator / (const Number) const; + + /** + * Returns the scalar product of this + * point vector with itself, i.e. the + * square, or the square of the norm. + */ + Number square () const; + + /** + * Returns the Euclidian distance of + * this point to the point + * p, i.e. the l_2 norm + * of the difference between the vectors + * representing the two points. + */ + Number distance (const Point &p) const; + + /** + * Read or write the data of this object to or + * from a stream for the purpose of serialization + */ + template + void serialize(Archive &ar, const unsigned int version); }; /*------------------------------- Inline functions: Point ---------------------------*/ @@ -205,8 +205,8 @@ Point::Point () template inline Point::Point (const bool initialize) - : - Tensor<1,dim,Number>(initialize) + : + Tensor<1,dim,Number>(initialize) {} @@ -214,8 +214,8 @@ Point::Point (const bool initialize) template inline Point::Point (const Tensor<1,dim,Number> &t) - : - Tensor<1,dim,Number>(t) + : + Tensor<1,dim,Number>(t) {} @@ -224,12 +224,13 @@ template inline Point::Point (const Number x) { - switch(dim) { + switch (dim) + { case 1: this->values[0] = x; default: Assert (dim==1, StandardExceptions::ExcInvalidConstructorCall()); - } + } } @@ -238,13 +239,14 @@ template inline Point::Point (const Number x, const Number y) { - switch(dim) { + switch (dim) + { case 2: this->values[0] = x; this->values[1] = y; default: Assert (dim==2, StandardExceptions::ExcInvalidConstructorCall()); - } + } } @@ -253,14 +255,15 @@ template inline Point::Point (const Number x, const Number y, const Number z) { - switch(dim) { + switch (dim) + { case 3: this->values[0] = x; this->values[1] = y; this->values[2] = z; default: Assert (dim==3, StandardExceptions::ExcInvalidConstructorCall()); - } + } } @@ -345,7 +348,7 @@ inline Number Point::operator * (const Tensor<1,dim,Number> &p) const { - // simply pass down + // simply pass down return Tensor<1,dim,Number>::operator * (p); } @@ -393,11 +396,11 @@ template template inline void -Point::serialize(Archive & ar, const unsigned int) +Point::serialize(Archive &ar, const unsigned int) { - // forward to serialization - // function in the base class - ar & static_cast &>(*this); + // forward to serialization + // function in the base class + ar &static_cast &>(*this); } #endif // DOXYGEN @@ -441,8 +444,8 @@ Point operator * (const double factor, */ template inline -std::ostream & operator << (std::ostream &out, - const Point &p) +std::ostream &operator << (std::ostream &out, + const Point &p) { for (unsigned int i=0; i inline -std::istream & operator >> (std::istream &in, - Point &p) +std::istream &operator >> (std::istream &in, + Point &p) { for (unsigned int i=0; i> p[i]; @@ -479,8 +482,8 @@ std::istream & operator >> (std::istream &in, */ template inline -std::ostream & operator << (std::ostream &out, - const Point<1,Number> &p) +std::ostream &operator << (std::ostream &out, + const Point<1,Number> &p) { out << p[0]; diff --git a/deal.II/include/deal.II/base/polynomial.h b/deal.II/include/deal.II/base/polynomial.h index 3d92dc98c4..8897467e04 100644 --- a/deal.II/include/deal.II/base/polynomial.h +++ b/deal.II/include/deal.II/base/polynomial.h @@ -36,697 +36,697 @@ DEAL_II_NAMESPACE_OPEN namespace Polynomials { -/** - * Base class for all 1D polynomials. A polynomial is represented in - * this class by its coefficients, which are set through the - * constructor or by derived classes. Evaluation of a polynomial - * happens through the Horner scheme which provides both numerical - * stability and a minimal number of numerical operations. - * - * @author Ralf Hartmann, Guido Kanschat, 2000, 2006 - */ + /** + * Base class for all 1D polynomials. A polynomial is represented in + * this class by its coefficients, which are set through the + * constructor or by derived classes. Evaluation of a polynomial + * happens through the Horner scheme which provides both numerical + * stability and a minimal number of numerical operations. + * + * @author Ralf Hartmann, Guido Kanschat, 2000, 2006 + */ template class Polynomial : public Subscriptor { - public: - /** - * Constructor. The - * coefficients of the - * polynomial are passed as - * arguments, and denote the - * polynomial $\sum_i a[i] - * x^i$, i.e. the first element - * of the array denotes the - * constant term, the second - * the linear one, and so - * on. The degree of the - * polynomial represented by - * this object is thus the - * number of elements in the - * coefficient array - * minus one. - */ - Polynomial (const std::vector &coefficients); - - /** - * Constructor creating a zero - * polynomial of degree @p n. - */ - Polynomial (const unsigned int n); - - /** - * Constructor for Lagrange polynomial and its - * point of evaluation. The idea is to - * construct $\prod_{i\neq j} - * \frac{x-x_i}{x_j-x_i}$, where j is the - * evaluation point specified as argument and - * the support points contain all points - * (including x_j, which will internally not - * be stored). - */ + public: + /** + * Constructor. The + * coefficients of the + * polynomial are passed as + * arguments, and denote the + * polynomial $\sum_i a[i] + * x^i$, i.e. the first element + * of the array denotes the + * constant term, the second + * the linear one, and so + * on. The degree of the + * polynomial represented by + * this object is thus the + * number of elements in the + * coefficient array + * minus one. + */ + Polynomial (const std::vector &coefficients); + + /** + * Constructor creating a zero + * polynomial of degree @p n. + */ + Polynomial (const unsigned int n); + + /** + * Constructor for Lagrange polynomial and its + * point of evaluation. The idea is to + * construct $\prod_{i\neq j} + * \frac{x-x_i}{x_j-x_i}$, where j is the + * evaluation point specified as argument and + * the support points contain all points + * (including x_j, which will internally not + * be stored). + */ Polynomial (const std::vector > &lagrange_support_points, const unsigned int evaluation_point); - /** - * Default constructor creating - * an illegal object. - */ - Polynomial (); - - /** - * Return the value of this - * polynomial at the given point. - * - * This function uses the Horner - * scheme for numerical stability - * of the evaluation. - */ - number value (const number x) const; - - /** - * Return the values and the - * derivatives of the - * Polynomial at point x. - * values[i], - * i=0,...,values.size()-1 - * includes the ith - * derivative. The number of - * derivatives to be computed is - * thus determined by the size of - * the array passed. - * - * This function uses the Horner - * scheme for numerical stability - * of the evaluation. - */ - void value (const number x, - std::vector &values) const; - - /** - * Degree of the polynomial. This - * is the degree reflected by the - * number of coefficients - * provided by the - * constructor. Leading non-zero - * coefficients are not treated - * separately. - */ - unsigned int degree () const; - - /** - * Scale the abscissa of the - * polynomial. Given the - * polynomial p(t) and the - * scaling t = ax, then the - * result of this operation is - * the polynomial q, such that - * q(x) = p(t). - * - * The operation is performed in - * place. - */ - void scale (const number factor); - - /** - * Shift the abscissa oft the - * polynomial. Given the - * polynomial p(t) and the - * shift t = x + a, then the - * result of this operation is - * the polynomial q, such that - * q(x) = p(t). - * - * The template parameter allows - * to compute the new - * coefficients with higher - * accuracy, since all - * computations are performed - * with type number2. This - * may be necessary, since this - * operation involves a big - * number of additions. On a Sun - * Sparc Ultra with Solaris 2.8, - * the difference between - * double and long double - * was not significant, though. - * - * The operation is performed in - * place, i.e. the coefficients - * of the present object are - * changed. - */ - template - void shift (const number2 offset); - - /** - * Compute the derivative of a - * polynomial. - */ - Polynomial derivative () const; - - /** - * Compute the primitive of a - * polynomial. the coefficient - * of the zero order term of - * the polynomial is zero. - */ - Polynomial primitive () const; - - /** - * Multiply with a scalar. - */ - Polynomial& operator *= (const double s); - - /** - * Multiply with another polynomial. - */ - Polynomial& operator *= (const Polynomial& p); - - /** - * Add a second polynomial. - */ - Polynomial& operator += (const Polynomial& p); - - /** - * Subtract a second polynomial. - */ - Polynomial& operator -= (const Polynomial& p); - - /** - * Test for equality of two polynomials. - */ - bool operator == (const Polynomial & p) const; - - /** - * Print coefficients. - */ - void print(std::ostream& out) const; - - /** - * Write or read the data of this object to or - * from a stream for the purpose of serialization. - */ - template - void serialize (Archive & ar, const unsigned int version); - - protected: - - /** - * This function performs the - * actual scaling. - */ - static void scale (std::vector &coefficients, - const number factor); - - /** - * This function performs the - * actual shift - */ - template - static void shift (std::vector &coefficients, - const number2 shift); - - /** - * Multiply polynomial by a factor. - */ - static void multiply (std::vector& coefficients, - const number factor); - - /** - * Transforms polynomial form of - * product of linear factors into - * standard form, $\sum_i a_i - * x^i$. Deletes all data structures - * related to the product form. - */ - void transform_into_standard_form (); - - /** - * Coefficients of the polynomial - * $\sum_i a_i x^i$. This vector - * is filled by the constructor - * of this class and may be - * passed down by derived - * classes. - * - * This vector cannot be constant - * since we want to allow copying - * of polynomials. - */ - std::vector coefficients; - - /** - * Stores whether the polynomial is in - * Lagrange product form, i.e., - * constructed as a product $(x-x_0) - * (x-x_1) \ldots (x-x_n)/c$, or not. - */ - bool in_lagrange_product_form; - - /** - * If the polynomial is in Lagrange - * product form, i.e., constructed as a - * product $(x-x_0) (x-x_1) \ldots - * (x-x_n)/c$, store the shifts $x_i$. - */ - std::vector lagrange_support_points; - - /** - * If the polynomial is in Lagrange - * product form, i.e., constructed as a - * product $(x-x_0) (x-x_1) \ldots - * (x-x_n)/c$, store the weight c. - */ - number lagrange_weight; + /** + * Default constructor creating + * an illegal object. + */ + Polynomial (); + + /** + * Return the value of this + * polynomial at the given point. + * + * This function uses the Horner + * scheme for numerical stability + * of the evaluation. + */ + number value (const number x) const; + + /** + * Return the values and the + * derivatives of the + * Polynomial at point x. + * values[i], + * i=0,...,values.size()-1 + * includes the ith + * derivative. The number of + * derivatives to be computed is + * thus determined by the size of + * the array passed. + * + * This function uses the Horner + * scheme for numerical stability + * of the evaluation. + */ + void value (const number x, + std::vector &values) const; + + /** + * Degree of the polynomial. This + * is the degree reflected by the + * number of coefficients + * provided by the + * constructor. Leading non-zero + * coefficients are not treated + * separately. + */ + unsigned int degree () const; + + /** + * Scale the abscissa of the + * polynomial. Given the + * polynomial p(t) and the + * scaling t = ax, then the + * result of this operation is + * the polynomial q, such that + * q(x) = p(t). + * + * The operation is performed in + * place. + */ + void scale (const number factor); + + /** + * Shift the abscissa oft the + * polynomial. Given the + * polynomial p(t) and the + * shift t = x + a, then the + * result of this operation is + * the polynomial q, such that + * q(x) = p(t). + * + * The template parameter allows + * to compute the new + * coefficients with higher + * accuracy, since all + * computations are performed + * with type number2. This + * may be necessary, since this + * operation involves a big + * number of additions. On a Sun + * Sparc Ultra with Solaris 2.8, + * the difference between + * double and long double + * was not significant, though. + * + * The operation is performed in + * place, i.e. the coefficients + * of the present object are + * changed. + */ + template + void shift (const number2 offset); + + /** + * Compute the derivative of a + * polynomial. + */ + Polynomial derivative () const; + + /** + * Compute the primitive of a + * polynomial. the coefficient + * of the zero order term of + * the polynomial is zero. + */ + Polynomial primitive () const; + + /** + * Multiply with a scalar. + */ + Polynomial &operator *= (const double s); + + /** + * Multiply with another polynomial. + */ + Polynomial &operator *= (const Polynomial &p); + + /** + * Add a second polynomial. + */ + Polynomial &operator += (const Polynomial &p); + + /** + * Subtract a second polynomial. + */ + Polynomial &operator -= (const Polynomial &p); + + /** + * Test for equality of two polynomials. + */ + bool operator == (const Polynomial &p) const; + + /** + * Print coefficients. + */ + void print(std::ostream &out) const; + + /** + * Write or read the data of this object to or + * from a stream for the purpose of serialization. + */ + template + void serialize (Archive &ar, const unsigned int version); + + protected: + + /** + * This function performs the + * actual scaling. + */ + static void scale (std::vector &coefficients, + const number factor); + + /** + * This function performs the + * actual shift + */ + template + static void shift (std::vector &coefficients, + const number2 shift); + + /** + * Multiply polynomial by a factor. + */ + static void multiply (std::vector &coefficients, + const number factor); + + /** + * Transforms polynomial form of + * product of linear factors into + * standard form, $\sum_i a_i + * x^i$. Deletes all data structures + * related to the product form. + */ + void transform_into_standard_form (); + + /** + * Coefficients of the polynomial + * $\sum_i a_i x^i$. This vector + * is filled by the constructor + * of this class and may be + * passed down by derived + * classes. + * + * This vector cannot be constant + * since we want to allow copying + * of polynomials. + */ + std::vector coefficients; + + /** + * Stores whether the polynomial is in + * Lagrange product form, i.e., + * constructed as a product $(x-x_0) + * (x-x_1) \ldots (x-x_n)/c$, or not. + */ + bool in_lagrange_product_form; + + /** + * If the polynomial is in Lagrange + * product form, i.e., constructed as a + * product $(x-x_0) (x-x_1) \ldots + * (x-x_n)/c$, store the shifts $x_i$. + */ + std::vector lagrange_support_points; + + /** + * If the polynomial is in Lagrange + * product form, i.e., constructed as a + * product $(x-x_0) (x-x_1) \ldots + * (x-x_n)/c$, store the weight c. + */ + number lagrange_weight; }; -/** - * Class generates Polynomial objects representing a monomial of - * degree n, that is, the function $x^n$. - * - * @author Guido Kanschat, 2004 - */ + /** + * Class generates Polynomial objects representing a monomial of + * degree n, that is, the function $x^n$. + * + * @author Guido Kanschat, 2004 + */ template class Monomial : public Polynomial { - public: - /** - * Constructor, taking the - * degree of the monomial and - * an optional coefficient as - * arguments. - */ - Monomial(const unsigned int n, - const double coefficient = 1.); - - /** - * Return a vector of Monomial - * objects of degree zero - * through degree, which - * then spans the full space of - * polynomials up to the given - * degree. This function may be - * used to initialize the - * TensorProductPolynomials - * and PolynomialSpace - * classes. - */ - static - std::vector > - generate_complete_basis (const unsigned int degree); - - private: - /** - * Needed by constructor. - */ - static std::vector make_vector(unsigned int n, - const double coefficient); + public: + /** + * Constructor, taking the + * degree of the monomial and + * an optional coefficient as + * arguments. + */ + Monomial(const unsigned int n, + const double coefficient = 1.); + + /** + * Return a vector of Monomial + * objects of degree zero + * through degree, which + * then spans the full space of + * polynomials up to the given + * degree. This function may be + * used to initialize the + * TensorProductPolynomials + * and PolynomialSpace + * classes. + */ + static + std::vector > + generate_complete_basis (const unsigned int degree); + + private: + /** + * Needed by constructor. + */ + static std::vector make_vector(unsigned int n, + const double coefficient); }; -/** - * Lagrange polynomials with equidistant interpolation points in - * [0,1]. The polynomial of degree n has got n+1 interpolation - * points. The interpolation points are sorted in ascending - * order. This order gives an index to each interpolation point. A - * Lagrangian polynomial equals to 1 at its `support point', and 0 at - * all other interpolation points. For example, if the degree is 3, - * and the support point is 1, then the polynomial represented by this - * object is cubic and its value is 1 at the point x=1/3, and zero - * at the point x=0, x=2/3, and x=1. All the polynomials - * have polynomial degree equal to degree, but together they span - * the entire space of polynomials of degree less than or equal - * degree. - * - * The Lagrange polynomials are implemented up to degree 10. - * - * @author Ralf Hartmann, 2000 - */ + /** + * Lagrange polynomials with equidistant interpolation points in + * [0,1]. The polynomial of degree n has got n+1 interpolation + * points. The interpolation points are sorted in ascending + * order. This order gives an index to each interpolation point. A + * Lagrangian polynomial equals to 1 at its `support point', and 0 at + * all other interpolation points. For example, if the degree is 3, + * and the support point is 1, then the polynomial represented by this + * object is cubic and its value is 1 at the point x=1/3, and zero + * at the point x=0, x=2/3, and x=1. All the polynomials + * have polynomial degree equal to degree, but together they span + * the entire space of polynomials of degree less than or equal + * degree. + * + * The Lagrange polynomials are implemented up to degree 10. + * + * @author Ralf Hartmann, 2000 + */ class LagrangeEquidistant: public Polynomial { - public: - /** - * Constructor. Takes the degree - * n of the Lagrangian - * polynom and the index - * support_point of the - * support point. Fills the - * coefficients of the base - * class Polynomial. - */ - LagrangeEquidistant (const unsigned int n, - const unsigned int support_point); - - /** - * Return a vector of polynomial - * objects of degree degree, - * which then spans the full - * space of polynomials up to the - * given degree. The polynomials - * are generated by calling the - * constructor of this class with - * the same degree but support - * point running from zero to - * degree. This function may - * be used to initialize the - * TensorProductPolynomials - * and PolynomialSpace - * classes. - */ - static - std::vector > - generate_complete_basis (const unsigned int degree); - - private: - - /** - * Computes the coefficients - * of the base class - * Polynomial. This function - * is static to allow to be - * called in the - * constructor. - */ - static - void - compute_coefficients (const unsigned int n, - const unsigned int support_point, - std::vector& a); + public: + /** + * Constructor. Takes the degree + * n of the Lagrangian + * polynom and the index + * support_point of the + * support point. Fills the + * coefficients of the base + * class Polynomial. + */ + LagrangeEquidistant (const unsigned int n, + const unsigned int support_point); + + /** + * Return a vector of polynomial + * objects of degree degree, + * which then spans the full + * space of polynomials up to the + * given degree. The polynomials + * are generated by calling the + * constructor of this class with + * the same degree but support + * point running from zero to + * degree. This function may + * be used to initialize the + * TensorProductPolynomials + * and PolynomialSpace + * classes. + */ + static + std::vector > + generate_complete_basis (const unsigned int degree); + + private: + + /** + * Computes the coefficients + * of the base class + * Polynomial. This function + * is static to allow to be + * called in the + * constructor. + */ + static + void + compute_coefficients (const unsigned int n, + const unsigned int support_point, + std::vector &a); }; - /** - * Given a set of points along the - * real axis, this function returns - * all Lagrange polynomials for - * interpolation of these - * points. The number of - * polynomials is equal to the - * number of points and the maximum - * degree is one less. - */ + /** + * Given a set of points along the + * real axis, this function returns + * all Lagrange polynomials for + * interpolation of these + * points. The number of + * polynomials is equal to the + * number of points and the maximum + * degree is one less. + */ std::vector > - generate_complete_Lagrange_basis (const std::vector >& points); - - - -/** - * Legendre polynomials of arbitrary degree. - * Constructing a Legendre polynomial of degree p, the coefficients - * will be computed by the three-term recursion formula. - * - * @note The polynomials defined by this class differ in two aspects by what - * is usually referred to as Legendre polynomials: (i) This classes defines - * them on the reference interval $[0,1]$, rather than the commonly used - * interval $[-1,1]$. (ii) The polynomials have been scaled in such a way that - * they are orthonormal, not just orthogonal; consequently, the polynomials do - * not necessarily have boundary values equal to one. - * - * @author Guido Kanschat, 2000 - */ + generate_complete_Lagrange_basis (const std::vector > &points); + + + + /** + * Legendre polynomials of arbitrary degree. + * Constructing a Legendre polynomial of degree p, the coefficients + * will be computed by the three-term recursion formula. + * + * @note The polynomials defined by this class differ in two aspects by what + * is usually referred to as Legendre polynomials: (i) This classes defines + * them on the reference interval $[0,1]$, rather than the commonly used + * interval $[-1,1]$. (ii) The polynomials have been scaled in such a way that + * they are orthonormal, not just orthogonal; consequently, the polynomials do + * not necessarily have boundary values equal to one. + * + * @author Guido Kanschat, 2000 + */ class Legendre : public Polynomial { - public: - /** - * Constructor for polynomial of - * degree p. - */ - Legendre (const unsigned int p); - - /** - * Return a vector of Legendre - * polynomial objects of degrees - * zero through degree, which - * then spans the full space of - * polynomials up to the given - * degree. This function may be - * used to initialize the - * TensorProductPolynomials - * and PolynomialSpace - * classes. - */ - static - std::vector > - generate_complete_basis (const unsigned int degree); - - private: - /** - * Coefficients for the interval $[0,1]$. - */ - static std::vector > > shifted_coefficients; - - /** - * Vector with already computed - * coefficients. For each degree of the - * polynomial, we keep one pointer to - * the list of coefficients; we do so - * rather than keeping a vector of - * vectors in order to simplify - * programming multithread-safe. In - * order to avoid memory leak, we use a - * shared_ptr in order to correctly - * free the memory of the vectors when - * the global destructor is called. - */ - static std::vector > > recursive_coefficients; - - /** - * Compute coefficients recursively. - * The coefficients are stored in a - * static data vector to be available - * when needed next time. Since the - * recursion is performed for the - * interval $[-1,1]$, the polynomials - * are shifted to $[0,1]$ by the - * scale and shift - * functions of Polynomial, - * afterwards. - */ - static void compute_coefficients (const unsigned int p); - - /** - * Get coefficients for - * constructor. This way, it can - * use the non-standard - * constructor of - * Polynomial. - */ - static const std::vector & - get_coefficients (const unsigned int k); + public: + /** + * Constructor for polynomial of + * degree p. + */ + Legendre (const unsigned int p); + + /** + * Return a vector of Legendre + * polynomial objects of degrees + * zero through degree, which + * then spans the full space of + * polynomials up to the given + * degree. This function may be + * used to initialize the + * TensorProductPolynomials + * and PolynomialSpace + * classes. + */ + static + std::vector > + generate_complete_basis (const unsigned int degree); + + private: + /** + * Coefficients for the interval $[0,1]$. + */ + static std::vector > > shifted_coefficients; + + /** + * Vector with already computed + * coefficients. For each degree of the + * polynomial, we keep one pointer to + * the list of coefficients; we do so + * rather than keeping a vector of + * vectors in order to simplify + * programming multithread-safe. In + * order to avoid memory leak, we use a + * shared_ptr in order to correctly + * free the memory of the vectors when + * the global destructor is called. + */ + static std::vector > > recursive_coefficients; + + /** + * Compute coefficients recursively. + * The coefficients are stored in a + * static data vector to be available + * when needed next time. Since the + * recursion is performed for the + * interval $[-1,1]$, the polynomials + * are shifted to $[0,1]$ by the + * scale and shift + * functions of Polynomial, + * afterwards. + */ + static void compute_coefficients (const unsigned int p); + + /** + * Get coefficients for + * constructor. This way, it can + * use the non-standard + * constructor of + * Polynomial. + */ + static const std::vector & + get_coefficients (const unsigned int k); }; -/** - * Lobatto polynomials of arbitrary degree on [0,1]. - * - * These polynomials are the integrated Legendre polynomials on [0,1]. The - * first two polynomials are the standard linear shape functions given by - * $l_0(x) = 1-x$ and $l_1(x) = x$. For $i\geq2$ we use the definition $l_i(x) - * = \frac{1}{\Vert L_{i-1}\Vert_2}\int_0^x L_{i-1}(t)\,dt$, where $L_i$ - * denotes the $i$-th Legendre polynomial on $[0,1]$. The Lobatto polynomials - * $l_0,\ldots,l_k$ form a complete basis of the polynomials space of degree - * $k$. - * - * Calling the constructor with a given index k will generate the - * polynomial with index k. But only for $k\geq 1$ the index equals - * the degree of the polynomial. For k==0 also a polynomial of degree - * 1 is generated. - * - * These polynomials are used for the construction of the shape functions of - * Nédélec elements of arbitrary order. - * - * @author Markus Bürg, 2009 - */ + /** + * Lobatto polynomials of arbitrary degree on [0,1]. + * + * These polynomials are the integrated Legendre polynomials on [0,1]. The + * first two polynomials are the standard linear shape functions given by + * $l_0(x) = 1-x$ and $l_1(x) = x$. For $i\geq2$ we use the definition $l_i(x) + * = \frac{1}{\Vert L_{i-1}\Vert_2}\int_0^x L_{i-1}(t)\,dt$, where $L_i$ + * denotes the $i$-th Legendre polynomial on $[0,1]$. The Lobatto polynomials + * $l_0,\ldots,l_k$ form a complete basis of the polynomials space of degree + * $k$. + * + * Calling the constructor with a given index k will generate the + * polynomial with index k. But only for $k\geq 1$ the index equals + * the degree of the polynomial. For k==0 also a polynomial of degree + * 1 is generated. + * + * These polynomials are used for the construction of the shape functions of + * Nédélec elements of arbitrary order. + * + * @author Markus Bürg, 2009 + */ class Lobatto : public Polynomial { - public: - /** - * Constructor for polynomial of degree - * p. There is an exception - * for p==0, see the general - * documentation. - */ - Lobatto (const unsigned int p = 0); - - /** - * Return the polynomials with index - * 0 up to - * degree. There is an - * exception for p==0, see the - * general documentation. - */ - static std::vector > - generate_complete_basis (const unsigned int p); - - private: - /** - * Compute coefficients recursively. - */ - std::vector compute_coefficients (const unsigned int p); + public: + /** + * Constructor for polynomial of degree + * p. There is an exception + * for p==0, see the general + * documentation. + */ + Lobatto (const unsigned int p = 0); + + /** + * Return the polynomials with index + * 0 up to + * degree. There is an + * exception for p==0, see the + * general documentation. + */ + static std::vector > + generate_complete_basis (const unsigned int p); + + private: + /** + * Compute coefficients recursively. + */ + std::vector compute_coefficients (const unsigned int p); }; -/** - * Hierarchical polynomials of arbitrary degree on [0,1]. - * - * When Constructing a Hierarchical polynomial of degree p, - * the coefficients will be computed by a recursion formula. The - * coefficients are stored in a static data vector to be available - * when needed next time. - * - * These hierarchical polynomials are based on those of Demkowicz, Oden, - * Rachowicz, and Hardy (CMAME 77 (1989) 79-112, Sec. 4). The first two - * polynomials are the standard linear shape functions given by - * $\phi_{0}(x) = 1 - x$ and $\phi_{1}(x) = x$. For $l \geq 2$ - * we use the definitions $\phi_{l}(x) = (2x-1)^l - 1, l = 2,4,6,...$ - * and $\phi_{l}(x) = (2x-1)^l - (2x-1), l = 3,5,7,...$. These satisfy the - * recursion relations $\phi_{l}(x) = (2x-1)\phi_{l-1}, l=3,5,7,...$ and - * $\phi_{l}(x) = (2x-1)\phi_{l-1} + \phi_{2}, l=4,6,8,...$. - * - * The degrees of freedom are the values at the vertices and the - * derivatives at the midpoint. Currently, we do not scale the - * polynomials in any way, although better conditioning of the - * element stiffness matrix could possibly be achieved with scaling. - * - * Calling the constructor with a given index p will generate the - * following: if p==0, then the resulting polynomial is the linear - * function associated with the left vertex, if p==1 the one - * associated with the right vertex. For higher values of p, you - * get the polynomial of degree p that is orthogonal to all - * previous ones. Note that for p==0 you therefore do not - * get a polynomial of degree zero, but one of degree one. This is to - * allow generating a complete basis for polynomial spaces, by just - * iterating over the indices given to the constructor. - * - * On the other hand, the function generate_complete_basis() creates - * a complete basis of given degree. In order to be consistent with - * the concept of a polynomial degree, if the given argument is zero, - * it does not return the linear polynomial described above, but - * rather a constant polynomial. - * - * @author Brian Carnes, 2002 - */ + /** + * Hierarchical polynomials of arbitrary degree on [0,1]. + * + * When Constructing a Hierarchical polynomial of degree p, + * the coefficients will be computed by a recursion formula. The + * coefficients are stored in a static data vector to be available + * when needed next time. + * + * These hierarchical polynomials are based on those of Demkowicz, Oden, + * Rachowicz, and Hardy (CMAME 77 (1989) 79-112, Sec. 4). The first two + * polynomials are the standard linear shape functions given by + * $\phi_{0}(x) = 1 - x$ and $\phi_{1}(x) = x$. For $l \geq 2$ + * we use the definitions $\phi_{l}(x) = (2x-1)^l - 1, l = 2,4,6,...$ + * and $\phi_{l}(x) = (2x-1)^l - (2x-1), l = 3,5,7,...$. These satisfy the + * recursion relations $\phi_{l}(x) = (2x-1)\phi_{l-1}, l=3,5,7,...$ and + * $\phi_{l}(x) = (2x-1)\phi_{l-1} + \phi_{2}, l=4,6,8,...$. + * + * The degrees of freedom are the values at the vertices and the + * derivatives at the midpoint. Currently, we do not scale the + * polynomials in any way, although better conditioning of the + * element stiffness matrix could possibly be achieved with scaling. + * + * Calling the constructor with a given index p will generate the + * following: if p==0, then the resulting polynomial is the linear + * function associated with the left vertex, if p==1 the one + * associated with the right vertex. For higher values of p, you + * get the polynomial of degree p that is orthogonal to all + * previous ones. Note that for p==0 you therefore do not + * get a polynomial of degree zero, but one of degree one. This is to + * allow generating a complete basis for polynomial spaces, by just + * iterating over the indices given to the constructor. + * + * On the other hand, the function generate_complete_basis() creates + * a complete basis of given degree. In order to be consistent with + * the concept of a polynomial degree, if the given argument is zero, + * it does not return the linear polynomial described above, but + * rather a constant polynomial. + * + * @author Brian Carnes, 2002 + */ class Hierarchical : public Polynomial { - public: - /** - * Constructor for polynomial of - * degree p. There is an - * exception for p==0, see - * the general documentation. - */ - Hierarchical (const unsigned int p); - - /** - * Return a vector of - * Hierarchical polynomial - * objects of degrees zero through - * degree, which then spans - * the full space of polynomials - * up to the given degree. Note - * that there is an exception if - * the given degree equals - * zero, see the general - * documentation of this class. - * - * This function may be - * used to initialize the - * TensorProductPolynomials, - * AnisotropicPolynomials, - * and PolynomialSpace - * classes. - */ - static - std::vector > - generate_complete_basis (const unsigned int degree); - - private: - /** - * Compute coefficients recursively. - */ - static void compute_coefficients (const unsigned int p); - - /** - * Get coefficients for - * constructor. This way, it can - * use the non-standard - * constructor of - * Polynomial. - */ - static const std::vector & - get_coefficients (const unsigned int p); - - /** - * Vector with already computed - * coefficients. For each degree of the - * polynomial, we keep one pointer to - * the list of coefficients; we do so - * rather than keeping a vector of - * vectors in order to simplify - * programming multithread-safe. In - * order to avoid memory leak, we use a - * shared_ptr in order to correctly - * free the memory of the vectors when - * the global destructor is called. - */ + public: + /** + * Constructor for polynomial of + * degree p. There is an + * exception for p==0, see + * the general documentation. + */ + Hierarchical (const unsigned int p); + + /** + * Return a vector of + * Hierarchical polynomial + * objects of degrees zero through + * degree, which then spans + * the full space of polynomials + * up to the given degree. Note + * that there is an exception if + * the given degree equals + * zero, see the general + * documentation of this class. + * + * This function may be + * used to initialize the + * TensorProductPolynomials, + * AnisotropicPolynomials, + * and PolynomialSpace + * classes. + */ + static + std::vector > + generate_complete_basis (const unsigned int degree); + + private: + /** + * Compute coefficients recursively. + */ + static void compute_coefficients (const unsigned int p); + + /** + * Get coefficients for + * constructor. This way, it can + * use the non-standard + * constructor of + * Polynomial. + */ + static const std::vector & + get_coefficients (const unsigned int p); + + /** + * Vector with already computed + * coefficients. For each degree of the + * polynomial, we keep one pointer to + * the list of coefficients; we do so + * rather than keeping a vector of + * vectors in order to simplify + * programming multithread-safe. In + * order to avoid memory leak, we use a + * shared_ptr in order to correctly + * free the memory of the vectors when + * the global destructor is called. + */ static std::vector > > recursive_coefficients; - }; + }; -/** - * Polynomials for Hermite interpolation condition. - * - * This is the set of polynomials of degree at least three, such that - * the following interpolation conditions are met: the polynomials and - * their first derivatives vanish at the values x=0 and - * x=1, with the exceptions p0(0)=1, - * p1(1)=1, p'2(0)=1, - * p'3(1)=1. - * - * For degree three, we obtain the standard four Hermitian - * interpolation polynomials, see for instance Wikipedia. - * For higher degrees, these are augmented - * first, by the polynomial of degree four with vanishing values and - * derivatives at x=0 and x=1, then by the product of - * this fourth order polynomial with Legendre polynomials of - * increasing order. The implementation is - * @f{align*}{ - * p_0(x) &= 2x^3-3x^2+1 \\ - * p_1(x) &= -2x^2+3x^2 \\ - * p_2(x) &= x^3-2x^2+x \\ - * p_3(x) &= x^3-x^2 \\ - * p_4(x) &= 16x^2(x-1)^2 \\ - * \ldots & \ldots \\ - * p_k(x) &= x^2(x-1)^2 L_{k-4}(x) - * @f} - * - * @author Guido Kanschat - * @date 2012 - */ + /** + * Polynomials for Hermite interpolation condition. + * + * This is the set of polynomials of degree at least three, such that + * the following interpolation conditions are met: the polynomials and + * their first derivatives vanish at the values x=0 and + * x=1, with the exceptions p0(0)=1, + * p1(1)=1, p'2(0)=1, + * p'3(1)=1. + * + * For degree three, we obtain the standard four Hermitian + * interpolation polynomials, see for instance Wikipedia. + * For higher degrees, these are augmented + * first, by the polynomial of degree four with vanishing values and + * derivatives at x=0 and x=1, then by the product of + * this fourth order polynomial with Legendre polynomials of + * increasing order. The implementation is + * @f{align*}{ + * p_0(x) &= 2x^3-3x^2+1 \\ + * p_1(x) &= -2x^2+3x^2 \\ + * p_2(x) &= x^3-2x^2+x \\ + * p_3(x) &= x^3-x^2 \\ + * p_4(x) &= 16x^2(x-1)^2 \\ + * \ldots & \ldots \\ + * p_k(x) &= x^2(x-1)^2 L_{k-4}(x) + * @f} + * + * @author Guido Kanschat + * @date 2012 + */ class HermiteInterpolation : public Polynomial { - public: - /** - * Constructor for polynomial - * with index p. See - * the class documentation on - * the definition of the - * sequence of polynomials. - */ + public: + /** + * Constructor for polynomial + * with index p. See + * the class documentation on + * the definition of the + * sequence of polynomials. + */ HermiteInterpolation (const unsigned int p); - /** - * Return the polynomials with index - * 0 up to - * p+1 in a space of - * degree up to - * p. Here, p - * has to be at least 3. - */ - static std::vector > - generate_complete_basis (const unsigned int p); + /** + * Return the polynomials with index + * 0 up to + * p+1 in a space of + * degree up to + * p. Here, p + * has to be at least 3. + */ + static std::vector > + generate_complete_basis (const unsigned int p); }; } @@ -774,7 +774,7 @@ namespace Polynomials { Assert (coefficients.size() > 0, ExcEmptyObject()); - // Horner scheme + // Horner scheme const unsigned int m=coefficients.size(); number value = coefficients.back(); for (int k=m-2; k>=0; --k) @@ -783,7 +783,7 @@ namespace Polynomials } else { - // direct evaluation of Lagrange polynomial + // direct evaluation of Lagrange polynomial const unsigned int m = lagrange_support_points.size(); number value = 1.; for (unsigned int j=0; j inline void - Polynomial::serialize (Archive & ar, const unsigned int) + Polynomial::serialize (Archive &ar, const unsigned int) { - // forward to serialization - // function in the base class. - ar & static_cast(*this); - ar & coefficients; - ar & in_lagrange_product_form; - ar & lagrange_support_points; - ar & lagrange_weight; + // forward to serialization + // function in the base class. + ar &static_cast(*this); + ar &coefficients; + ar &in_lagrange_product_form; + ar &lagrange_support_points; + ar &lagrange_weight; } } diff --git a/deal.II/include/deal.II/base/polynomial_space.h b/deal.II/include/deal.II/base/polynomial_space.h index eca0b00355..7fa0ca79dc 100644 --- a/deal.II/include/deal.II/base/polynomial_space.h +++ b/deal.II/include/deal.II/base/polynomial_space.h @@ -83,182 +83,182 @@ DEAL_II_NAMESPACE_OPEN template class PolynomialSpace { - public: - /** - * Access to the dimension of - * this object, for checking and - * automatic setting of dimension - * in other classes. - */ - static const unsigned int dimension = dim; - - /** - * Constructor. pols is a - * vector of pointers to - * one-dimensional polynomials - * and will be copied into a - * private member variable. The static - * type of the template argument - * pols needs to be - * convertible to - * Polynomials::Polynomial@, - * i.e. should usually be a - * derived class of - * Polynomials::Polynomial@. - */ - template - PolynomialSpace (const std::vector &pols); - - /** - * Prints the list of the indices - * to out. - */ - template - void output_indices(STREAM &out) const; - - /** - * Sets the ordering of the - * polynomials. Requires - * renumber.size()==n(). - * Stores a copy of - * renumber. - */ - void set_numbering(const std::vector &renumber); - - /** - * Computes the value and the - * first and second derivatives - * of each polynomial at - * unit_point. - * - * The size of the vectors must - * either be equal 0 or equal - * n(). In the first case, - * the function will not compute - * these values, i.e. you - * indicate what you want to have - * computed by resizing those - * vectors which you want filled. - * - * If you need values or - * derivatives of all polynomials - * then use this function, rather - * than using any of the - * compute_value(), - * compute_grad() or - * compute_grad_grad() - * functions, see below, in a - * loop over all polynomials. - */ - void compute (const Point &unit_point, - std::vector &values, - std::vector > &grads, - std::vector > &grad_grads) const; - - /** - * Computes the value of the - * ith polynomial at - * unit_point. - * - * Consider using compute() instead. - */ - double compute_value (const unsigned int i, - const Point &p) const; - - /** - * Computes the gradient of the - * ith polynomial at - * unit_point. - * - * Consider using compute() instead. - */ - Tensor<1,dim> compute_grad (const unsigned int i, - const Point &p) const; - - /** - * Computes the second derivative - * (grad_grad) of the ith - * polynomial at - * unit_point. - * - * Consider using compute() instead. - */ - Tensor<2,dim> compute_grad_grad (const unsigned int i, - const Point &p) const; - - /** - * Return the number of - * polynomials spanning the space - * represented by this - * class. Here, if N is the - * number of one-dimensional - * polynomials given, then the - * result of this function is - * N in 1d, N(N+1)/2 in - * 2d, and N(N+1)(N+2)/6 in - * 3d. - */ - unsigned int n () const; - - /** - * Degree of the space. This is - * by definition the number of - * polynomials given to the - * constructor, NOT the maximal - * degree of a polynomial in this - * vector. The latter value is - * never checked and therefore - * left to the application. - */ - unsigned int degree () const; - - /** - * Static function used in the - * constructor to compute the - * number of polynomials. - */ - static unsigned int compute_n_pols (const unsigned int n); - - protected: - - /** - * Compute numbers in x, y and z - * direction. Given an index - * n in the d-dimensional - * polynomial space, compute the - * indices i,j,k such that - * pn(x,y,z) = - * pi(x)pj(y)pk(z). - */ - void compute_index (const unsigned int n, - unsigned int (&index)[dim]) const; - - private: - /** - * Copy of the vector pols of - * polynomials given to the - * constructor. - */ - const std::vector > polynomials; - - /** - * Store the precomputed value - * which the n() function - * returns. - */ - const unsigned int n_pols; - - /** - * Index map for reordering the - * polynomials. - */ - std::vector index_map; - - /** - * Index map for reordering the - * polynomials. - */ - std::vector index_map_inverse; +public: + /** + * Access to the dimension of + * this object, for checking and + * automatic setting of dimension + * in other classes. + */ + static const unsigned int dimension = dim; + + /** + * Constructor. pols is a + * vector of pointers to + * one-dimensional polynomials + * and will be copied into a + * private member variable. The static + * type of the template argument + * pols needs to be + * convertible to + * Polynomials::Polynomial@, + * i.e. should usually be a + * derived class of + * Polynomials::Polynomial@. + */ + template + PolynomialSpace (const std::vector &pols); + + /** + * Prints the list of the indices + * to out. + */ + template + void output_indices(STREAM &out) const; + + /** + * Sets the ordering of the + * polynomials. Requires + * renumber.size()==n(). + * Stores a copy of + * renumber. + */ + void set_numbering(const std::vector &renumber); + + /** + * Computes the value and the + * first and second derivatives + * of each polynomial at + * unit_point. + * + * The size of the vectors must + * either be equal 0 or equal + * n(). In the first case, + * the function will not compute + * these values, i.e. you + * indicate what you want to have + * computed by resizing those + * vectors which you want filled. + * + * If you need values or + * derivatives of all polynomials + * then use this function, rather + * than using any of the + * compute_value(), + * compute_grad() or + * compute_grad_grad() + * functions, see below, in a + * loop over all polynomials. + */ + void compute (const Point &unit_point, + std::vector &values, + std::vector > &grads, + std::vector > &grad_grads) const; + + /** + * Computes the value of the + * ith polynomial at + * unit_point. + * + * Consider using compute() instead. + */ + double compute_value (const unsigned int i, + const Point &p) const; + + /** + * Computes the gradient of the + * ith polynomial at + * unit_point. + * + * Consider using compute() instead. + */ + Tensor<1,dim> compute_grad (const unsigned int i, + const Point &p) const; + + /** + * Computes the second derivative + * (grad_grad) of the ith + * polynomial at + * unit_point. + * + * Consider using compute() instead. + */ + Tensor<2,dim> compute_grad_grad (const unsigned int i, + const Point &p) const; + + /** + * Return the number of + * polynomials spanning the space + * represented by this + * class. Here, if N is the + * number of one-dimensional + * polynomials given, then the + * result of this function is + * N in 1d, N(N+1)/2 in + * 2d, and N(N+1)(N+2)/6 in + * 3d. + */ + unsigned int n () const; + + /** + * Degree of the space. This is + * by definition the number of + * polynomials given to the + * constructor, NOT the maximal + * degree of a polynomial in this + * vector. The latter value is + * never checked and therefore + * left to the application. + */ + unsigned int degree () const; + + /** + * Static function used in the + * constructor to compute the + * number of polynomials. + */ + static unsigned int compute_n_pols (const unsigned int n); + +protected: + + /** + * Compute numbers in x, y and z + * direction. Given an index + * n in the d-dimensional + * polynomial space, compute the + * indices i,j,k such that + * pn(x,y,z) = + * pi(x)pj(y)pk(z). + */ + void compute_index (const unsigned int n, + unsigned int (&index)[dim]) const; + +private: + /** + * Copy of the vector pols of + * polynomials given to the + * constructor. + */ + const std::vector > polynomials; + + /** + * Store the precomputed value + * which the n() function + * returns. + */ + const unsigned int n_pols; + + /** + * Index map for reordering the + * polynomials. + */ + std::vector index_map; + + /** + * Index map for reordering the + * polynomials. + */ + std::vector index_map_inverse; }; @@ -281,16 +281,16 @@ void PolynomialSpace<3>::compute_index(const unsigned int n, template template PolynomialSpace::PolynomialSpace (const std::vector &pols) - : - polynomials (pols.begin(), pols.end()), - n_pols (compute_n_pols(polynomials.size())), - index_map(n_pols), - index_map_inverse(n_pols) + : + polynomials (pols.begin(), pols.end()), + n_pols (compute_n_pols(polynomials.size())), + index_map(n_pols), + index_map_inverse(n_pols) { - // per default set this index map - // to identity. This map can be - // changed by the user through the - // set_numbering function + // per default set this index map + // to identity. This map can be + // changed by the user through the + // set_numbering function for (unsigned int i=0; i class PolynomialsABF { - public: - /** - * Constructor. Creates all basis - * functions for Raviart-Thomas polynomials - * of given degree. - * - * @arg k: the degree of the - * Raviart-Thomas-space, which is the degree - * of the largest tensor product - * polynomial space - * Qk contained. - */ - PolynomialsABF (const unsigned int k); - - /** - * Destructor deleting the polynomials. - */ - ~PolynomialsABF (); - - /** - * Computes the value and the - * first and second derivatives - * of each Raviart-Thomas - * polynomial at @p unit_point. - * - * The size of the vectors must - * either be zero or equal - * n(). In the - * first case, the function will - * not compute these values. - * - * If you need values or - * derivatives of all tensor - * product polynomials then use - * this function, rather than - * using any of the - * compute_value, - * compute_grad or - * compute_grad_grad - * functions, see below, in a - * loop over all tensor product - * polynomials. - */ - void compute (const Point &unit_point, - std::vector > &values, - std::vector > &grads, - std::vector > &grad_grads) const; - - /** - * Returns the number of ABF polynomials. - */ - unsigned int n () const; - - /** - * Returns the degree of the ABF - * space, which is two less than - * the highest polynomial degree. - */ - unsigned int degree () const; - - /** - * Return the name of the space , - * which is ABF. - */ - std::string name () const; - - /** - * Return the number of - * polynomials in the space - * RT(degree) without - * requiring to build an object - * of PolynomialsABF. This is - * required by the FiniteElement - * classes. - */ - static unsigned int compute_n_pols(unsigned int degree); - - private: - /** - * The degree of this object as - * given to the constructor. - */ - const unsigned int my_degree; - - /** - * An object representing the - * polynomial space for a single - * component. We can re-use it by - * rotating the coordinates of - * the evaluation point. - */ - AnisotropicPolynomials* polynomial_space; - - /** - * Number of Raviart-Thomas - * polynomials. - */ - unsigned int n_pols; - - /** - * A mutex that guards the - * following scratch arrays. - */ - mutable Threads::Mutex mutex; - - /** - * Auxiliary memory. - */ - mutable std::vector p_values; - - /** - * Auxiliary memory. - */ - mutable std::vector > p_grads; - - /** - * Auxiliary memory. - */ - mutable std::vector > p_grad_grads; +public: + /** + * Constructor. Creates all basis + * functions for Raviart-Thomas polynomials + * of given degree. + * + * @arg k: the degree of the + * Raviart-Thomas-space, which is the degree + * of the largest tensor product + * polynomial space + * Qk contained. + */ + PolynomialsABF (const unsigned int k); + + /** + * Destructor deleting the polynomials. + */ + ~PolynomialsABF (); + + /** + * Computes the value and the + * first and second derivatives + * of each Raviart-Thomas + * polynomial at @p unit_point. + * + * The size of the vectors must + * either be zero or equal + * n(). In the + * first case, the function will + * not compute these values. + * + * If you need values or + * derivatives of all tensor + * product polynomials then use + * this function, rather than + * using any of the + * compute_value, + * compute_grad or + * compute_grad_grad + * functions, see below, in a + * loop over all tensor product + * polynomials. + */ + void compute (const Point &unit_point, + std::vector > &values, + std::vector > &grads, + std::vector > &grad_grads) const; + + /** + * Returns the number of ABF polynomials. + */ + unsigned int n () const; + + /** + * Returns the degree of the ABF + * space, which is two less than + * the highest polynomial degree. + */ + unsigned int degree () const; + + /** + * Return the name of the space , + * which is ABF. + */ + std::string name () const; + + /** + * Return the number of + * polynomials in the space + * RT(degree) without + * requiring to build an object + * of PolynomialsABF. This is + * required by the FiniteElement + * classes. + */ + static unsigned int compute_n_pols(unsigned int degree); + +private: + /** + * The degree of this object as + * given to the constructor. + */ + const unsigned int my_degree; + + /** + * An object representing the + * polynomial space for a single + * component. We can re-use it by + * rotating the coordinates of + * the evaluation point. + */ + AnisotropicPolynomials *polynomial_space; + + /** + * Number of Raviart-Thomas + * polynomials. + */ + unsigned int n_pols; + + /** + * A mutex that guards the + * following scratch arrays. + */ + mutable Threads::Mutex mutex; + + /** + * Auxiliary memory. + */ + mutable std::vector p_values; + + /** + * Auxiliary memory. + */ + mutable std::vector > p_grads; + + /** + * Auxiliary memory. + */ + mutable std::vector > p_grad_grads; }; diff --git a/deal.II/include/deal.II/base/polynomials_adini.h b/deal.II/include/deal.II/base/polynomials_adini.h index 17798dcae7..a840d1f5c4 100644 --- a/deal.II/include/deal.II/base/polynomials_adini.h +++ b/deal.II/include/deal.II/base/polynomials_adini.h @@ -36,117 +36,117 @@ DEAL_II_NAMESPACE_OPEN class PolynomialsAdini { - public: - /** - * Constructor for - * the polynomials of - * the described space - */ - PolynomialsAdini (); - /** - * Computes the value and the - * first and second derivatives - * of each polynomial at - * unit_point. - * - * The size of the vectors must - * either be equal 0 or equal - * n(). In the first case, - * the function will not compute - * these values, i.e. you - * indicate what you want to have - * computed by resizing those - * vectors which you want filled. - * - * If you need values or - * derivatives of all polynomials - * then use this function, rather - * than using any of the - * compute_value(), - * compute_grad() or - * compute_grad_grad() - * functions, see below, in a - * loop over all polynomials. - */ - - void compute (const Point<2> &unit_point, - std::vector &values, - std::vector > &grads, - std::vector< Tensor<2,2> > &grad_grads) const; - - /** - * Computes the value of the - * ith polynomial at - * unit_point. - * - * Consider using compute() instead. - */ - - double compute_value (const unsigned int i, - const Point<2> &p) const; - - /** - * Computes the gradient of the - * ith polynomial at - * unit_point. - * - * Consider using compute() instead. - */ - - Tensor<1,2> compute_grad (const unsigned int i, - const Point<2> &p) const; - /** - * Computes the second derivative - * (grad_grad) of the ith - * polynomial at - * unit_point. - * - * Consider using compute() instead. - */ - - Tensor<2,2> compute_grad_grad (const unsigned int i, const Point<2> &p) const; - Tensor<2,2> compute_grad_grad_2 (const unsigned int i, const Point<2> &p) const; - - private: - /** - * Store the coefficients of the - * polynominals in the order - * $1,x,y,x^2,y^2,xy,x^3,y^3,xy^2,x^2y,x^3y,xy^3$ - */ - Table<2, double> coef; - - /** - * Store the coefficients of the x-derivative - * of the polynominals in the order - * $1,x,y,x^2,y^2,xy,x^3,y^3,xy^2,x^2y,x^3y,xy^3$ - */ - - Table<2, double> dx; - /** - * Store the coefficients of the y-derivative - * of the polynominals in the order - * $1,x,y,x^2,y^2,xy,x^3,y^3,xy^2,x^2y,x^3y,xy^3$ - */ - - Table<2, double> dy; - /** - * Store the coefficients of the second x-derivative - * of the polynominals in the order - * $1,x,y,x^2,y^2,xy,x^3,y^3,xy^2,x^2y,x^3y,xy^3$ - */ - Table<2, double> dxx; - /** - * Store the coefficients of the second y-derivative - * of the polynominals in the order - * $1,x,y,x^2,y^2,xy,x^3,y^3,xy^2,x^2y,x^3y,xy^3$ - */ - Table<2, double> dyy; - /** - * Store the coefficients of the second mixed derivative - * of the polynominals in the order - * $1,x,y,x^2,y^2,xy,x^3,y^3,xy^2,x^2y,x^3y,xy^3$ - */ - Table<2, double> dxy; +public: + /** + * Constructor for + * the polynomials of + * the described space + */ + PolynomialsAdini (); + /** + * Computes the value and the + * first and second derivatives + * of each polynomial at + * unit_point. + * + * The size of the vectors must + * either be equal 0 or equal + * n(). In the first case, + * the function will not compute + * these values, i.e. you + * indicate what you want to have + * computed by resizing those + * vectors which you want filled. + * + * If you need values or + * derivatives of all polynomials + * then use this function, rather + * than using any of the + * compute_value(), + * compute_grad() or + * compute_grad_grad() + * functions, see below, in a + * loop over all polynomials. + */ + + void compute (const Point<2> &unit_point, + std::vector &values, + std::vector > &grads, + std::vector< Tensor<2,2> > &grad_grads) const; + + /** + * Computes the value of the + * ith polynomial at + * unit_point. + * + * Consider using compute() instead. + */ + + double compute_value (const unsigned int i, + const Point<2> &p) const; + + /** + * Computes the gradient of the + * ith polynomial at + * unit_point. + * + * Consider using compute() instead. + */ + + Tensor<1,2> compute_grad (const unsigned int i, + const Point<2> &p) const; + /** + * Computes the second derivative + * (grad_grad) of the ith + * polynomial at + * unit_point. + * + * Consider using compute() instead. + */ + + Tensor<2,2> compute_grad_grad (const unsigned int i, const Point<2> &p) const; + Tensor<2,2> compute_grad_grad_2 (const unsigned int i, const Point<2> &p) const; + +private: + /** + * Store the coefficients of the + * polynominals in the order + * $1,x,y,x^2,y^2,xy,x^3,y^3,xy^2,x^2y,x^3y,xy^3$ + */ + Table<2, double> coef; + + /** + * Store the coefficients of the x-derivative + * of the polynominals in the order + * $1,x,y,x^2,y^2,xy,x^3,y^3,xy^2,x^2y,x^3y,xy^3$ + */ + + Table<2, double> dx; + /** + * Store the coefficients of the y-derivative + * of the polynominals in the order + * $1,x,y,x^2,y^2,xy,x^3,y^3,xy^2,x^2y,x^3y,xy^3$ + */ + + Table<2, double> dy; + /** + * Store the coefficients of the second x-derivative + * of the polynominals in the order + * $1,x,y,x^2,y^2,xy,x^3,y^3,xy^2,x^2y,x^3y,xy^3$ + */ + Table<2, double> dxx; + /** + * Store the coefficients of the second y-derivative + * of the polynominals in the order + * $1,x,y,x^2,y^2,xy,x^3,y^3,xy^2,x^2y,x^3y,xy^3$ + */ + Table<2, double> dyy; + /** + * Store the coefficients of the second mixed derivative + * of the polynominals in the order + * $1,x,y,x^2,y^2,xy,x^3,y^3,xy^2,x^2y,x^3y,xy^3$ + */ + Table<2, double> dxy; }; diff --git a/deal.II/include/deal.II/base/polynomials_bdm.h b/deal.II/include/deal.II/base/polynomials_bdm.h index b5bd31e2f7..70994912ee 100644 --- a/deal.II/include/deal.II/base/polynomials_bdm.h +++ b/deal.II/include/deal.II/base/polynomials_bdm.h @@ -57,123 +57,123 @@ DEAL_II_NAMESPACE_OPEN template class PolynomialsBDM { - public: - /** - * Constructor. Creates all basis - * functions for BDM polynomials - * of given degree. - * - * @arg k: the degree of the - * BDM-space, which is the degree - * of the largest complete - * polynomial space - * Pk contained - * in the BDM-space. - */ - PolynomialsBDM (const unsigned int k); - - /** - * Computes the value and the - * first and second derivatives - * of each BDM - * polynomial at @p unit_point. - * - * The size of the vectors must - * either be zero or equal - * n(). In the - * first case, the function will - * not compute these values. - * - * If you need values or - * derivatives of all tensor - * product polynomials then use - * this function, rather than - * using any of the - * compute_value, - * compute_grad or - * compute_grad_grad - * functions, see below, in a - * loop over all tensor product - * polynomials. - */ - void compute (const Point &unit_point, - std::vector > &values, - std::vector > &grads, - std::vector > &grad_grads) const; - - /** - * Returns the number of BDM polynomials. - */ - unsigned int n () const; - - /** - * Returns the degree of the BDM - * space, which is one less than - * the highest polynomial degree. - */ - unsigned int degree () const; - - /** - * Return the name of the space , - * which is BDM. - */ - std::string name () const; - - /** - * Return the number of - * polynomials in the space - * BDM(degree) without - * requiring to build an object - * of PolynomialsBDM. This is - * required by the FiniteElement - * classes. - */ - static unsigned int compute_n_pols(unsigned int degree); - - private: - /** - * An object representing the - * polynomial space used - * here. The constructor fills - * this with the monomial basis. - */ - const PolynomialSpace polynomial_space; - - /** - * Storage for monomials. In 2D, - * this is just the polynomial of - * order k. In 3D, we - * need all polynomials from - * degree zero to k. - */ - std::vector > monomials; - - /** - * Number of BDM - * polynomials. - */ - unsigned int n_pols; - - /** - * A mutex that guards the - * following scratch arrays. - */ - mutable Threads::Mutex mutex; - - /** - * Auxiliary memory. - */ - mutable std::vector p_values; - - /** - * Auxiliary memory. - */ - mutable std::vector > p_grads; - - /** - * Auxiliary memory. - */ - mutable std::vector > p_grad_grads; +public: + /** + * Constructor. Creates all basis + * functions for BDM polynomials + * of given degree. + * + * @arg k: the degree of the + * BDM-space, which is the degree + * of the largest complete + * polynomial space + * Pk contained + * in the BDM-space. + */ + PolynomialsBDM (const unsigned int k); + + /** + * Computes the value and the + * first and second derivatives + * of each BDM + * polynomial at @p unit_point. + * + * The size of the vectors must + * either be zero or equal + * n(). In the + * first case, the function will + * not compute these values. + * + * If you need values or + * derivatives of all tensor + * product polynomials then use + * this function, rather than + * using any of the + * compute_value, + * compute_grad or + * compute_grad_grad + * functions, see below, in a + * loop over all tensor product + * polynomials. + */ + void compute (const Point &unit_point, + std::vector > &values, + std::vector > &grads, + std::vector > &grad_grads) const; + + /** + * Returns the number of BDM polynomials. + */ + unsigned int n () const; + + /** + * Returns the degree of the BDM + * space, which is one less than + * the highest polynomial degree. + */ + unsigned int degree () const; + + /** + * Return the name of the space , + * which is BDM. + */ + std::string name () const; + + /** + * Return the number of + * polynomials in the space + * BDM(degree) without + * requiring to build an object + * of PolynomialsBDM. This is + * required by the FiniteElement + * classes. + */ + static unsigned int compute_n_pols(unsigned int degree); + +private: + /** + * An object representing the + * polynomial space used + * here. The constructor fills + * this with the monomial basis. + */ + const PolynomialSpace polynomial_space; + + /** + * Storage for monomials. In 2D, + * this is just the polynomial of + * order k. In 3D, we + * need all polynomials from + * degree zero to k. + */ + std::vector > monomials; + + /** + * Number of BDM + * polynomials. + */ + unsigned int n_pols; + + /** + * A mutex that guards the + * following scratch arrays. + */ + mutable Threads::Mutex mutex; + + /** + * Auxiliary memory. + */ + mutable std::vector p_values; + + /** + * Auxiliary memory. + */ + mutable std::vector > p_grads; + + /** + * Auxiliary memory. + */ + mutable std::vector > p_grad_grads; }; diff --git a/deal.II/include/deal.II/base/polynomials_nedelec.h b/deal.II/include/deal.II/base/polynomials_nedelec.h index 5e77df2bd3..70f891ef2d 100644 --- a/deal.II/include/deal.II/base/polynomials_nedelec.h +++ b/deal.II/include/deal.II/base/polynomials_nedelec.h @@ -47,105 +47,105 @@ DEAL_II_NAMESPACE_OPEN template class PolynomialsNedelec { - public: - /** - * Constructor. Creates all basis - * functions for Nédélec polynomials - * of given degree. - * - * @arg k: the degree of the - * Nédélec space, which is the degree - * of the largest tensor product - * polynomial space - * Qk contained. - */ - PolynomialsNedelec (const unsigned int k); - - /** - * Computes the value and the - * first and second derivatives - * of each Nédélec - * polynomial at @p unit_point. - * - * The size of the vectors must - * either be zero or equal - * n(). In the - * first case, the function will - * not compute these values. - * - * If you need values or - * derivatives of all tensor - * product polynomials then use - * this function, rather than - * using any of the - * compute_value, - * compute_grad or - * compute_grad_grad - * functions, see below, in a - * loop over all tensor product - * polynomials. - */ - void compute (const Point &unit_point, std::vector > &values, std::vector > &grads, std::vector > &grad_grads) const; - - /** - * Returns the number of Nédélec - * polynomials. - */ - unsigned int n () const; - - /** - * Returns the degree of the Nédélec - * space, which is one less than - * the highest polynomial degree. - */ - unsigned int degree () const; - - /** - * Return the name of the space , - * which is Nedelec. - */ - std::string name () const; - - /** - * Return the number of - * polynomials in the space - * N(degree) without - * requiring to build an object - * of PolynomialsNedelec. This is - * required by the FiniteElement - * classes. - */ - static unsigned int compute_n_pols (unsigned int degree); - - private: - /** - * The degree of this object as - * given to the constructor. - */ - const unsigned int my_degree; - - /** - * An object representing the - * polynomial space for a single - * component. We can re-use it by - * rotating the coordinates of - * the evaluation point. - */ - const AnisotropicPolynomials polynomial_space; - - /** - * Number of Nédélec polynomials. - */ - const unsigned int n_pols; - - /** - * A static member function that - * creates the polynomial space - * we use to initialize the - * #polynomial_space member - * variable. - */ - static std::vector > > create_polynomials (const unsigned int k); +public: + /** + * Constructor. Creates all basis + * functions for Nédélec polynomials + * of given degree. + * + * @arg k: the degree of the + * Nédélec space, which is the degree + * of the largest tensor product + * polynomial space + * Qk contained. + */ + PolynomialsNedelec (const unsigned int k); + + /** + * Computes the value and the + * first and second derivatives + * of each Nédélec + * polynomial at @p unit_point. + * + * The size of the vectors must + * either be zero or equal + * n(). In the + * first case, the function will + * not compute these values. + * + * If you need values or + * derivatives of all tensor + * product polynomials then use + * this function, rather than + * using any of the + * compute_value, + * compute_grad or + * compute_grad_grad + * functions, see below, in a + * loop over all tensor product + * polynomials. + */ + void compute (const Point &unit_point, std::vector > &values, std::vector > &grads, std::vector > &grad_grads) const; + + /** + * Returns the number of Nédélec + * polynomials. + */ + unsigned int n () const; + + /** + * Returns the degree of the Nédélec + * space, which is one less than + * the highest polynomial degree. + */ + unsigned int degree () const; + + /** + * Return the name of the space , + * which is Nedelec. + */ + std::string name () const; + + /** + * Return the number of + * polynomials in the space + * N(degree) without + * requiring to build an object + * of PolynomialsNedelec. This is + * required by the FiniteElement + * classes. + */ + static unsigned int compute_n_pols (unsigned int degree); + +private: + /** + * The degree of this object as + * given to the constructor. + */ + const unsigned int my_degree; + + /** + * An object representing the + * polynomial space for a single + * component. We can re-use it by + * rotating the coordinates of + * the evaluation point. + */ + const AnisotropicPolynomials polynomial_space; + + /** + * Number of Nédélec polynomials. + */ + const unsigned int n_pols; + + /** + * A static member function that + * creates the polynomial space + * we use to initialize the + * #polynomial_space member + * variable. + */ + static std::vector > > create_polynomials (const unsigned int k); }; diff --git a/deal.II/include/deal.II/base/polynomials_p.h b/deal.II/include/deal.II/base/polynomials_p.h index 23033706f8..e7a50068af 100644 --- a/deal.II/include/deal.II/base/polynomials_p.h +++ b/deal.II/include/deal.II/base/polynomials_p.h @@ -44,60 +44,60 @@ DEAL_II_NAMESPACE_OPEN template class PolynomialsP: public PolynomialSpace { - public: - /** - * Access to the dimension of - * this object, for checking and - * automatic setting of dimension - * in other classes. - */ - static const unsigned int dimension = dim; - - /** - * Constructor. Creates all basis - * functions of $P_p$. - * @arg p: the degree of the - * polynomial space - */ - PolynomialsP (const unsigned int p); - - /** - * Returns the degree p - * of the polynomial space - * P_p. - * - * Note, that this number is - * PolynomialSpace::degree()-1, - * compare definition in - * PolynomialSpace. - */ - unsigned int degree() const; - - /** - * For the nth - * polynomial $p_n(x,y,z)=x^i y^j - * z^k$ this function gives the - * degrees i,j,k in the x,y,z - * directions. - */ - void directional_degrees(unsigned int n, - unsigned int (°rees)[dim]) const; - - private: - - /** - * Fills the index_map. - */ - void create_polynomial_ordering(std::vector &index_map) const; - - /** - * Degree p of the - * polynomial space $P_p$, - * i.e. the number p - * which was given to the - * constructor. - */ - const unsigned int p; +public: + /** + * Access to the dimension of + * this object, for checking and + * automatic setting of dimension + * in other classes. + */ + static const unsigned int dimension = dim; + + /** + * Constructor. Creates all basis + * functions of $P_p$. + * @arg p: the degree of the + * polynomial space + */ + PolynomialsP (const unsigned int p); + + /** + * Returns the degree p + * of the polynomial space + * P_p. + * + * Note, that this number is + * PolynomialSpace::degree()-1, + * compare definition in + * PolynomialSpace. + */ + unsigned int degree() const; + + /** + * For the nth + * polynomial $p_n(x,y,z)=x^i y^j + * z^k$ this function gives the + * degrees i,j,k in the x,y,z + * directions. + */ + void directional_degrees(unsigned int n, + unsigned int (°rees)[dim]) const; + +private: + + /** + * Fills the index_map. + */ + void create_polynomial_ordering(std::vector &index_map) const; + + /** + * Degree p of the + * polynomial space $P_p$, + * i.e. the number p + * which was given to the + * constructor. + */ + const unsigned int p; }; /** @} */ diff --git a/deal.II/include/deal.II/base/polynomials_raviart_thomas.h b/deal.II/include/deal.II/base/polynomials_raviart_thomas.h index 0c112f983c..6769205300 100644 --- a/deal.II/include/deal.II/base/polynomials_raviart_thomas.h +++ b/deal.II/include/deal.II/base/polynomials_raviart_thomas.h @@ -46,110 +46,110 @@ DEAL_II_NAMESPACE_OPEN template class PolynomialsRaviartThomas { - public: - /** - * Constructor. Creates all basis - * functions for Raviart-Thomas polynomials - * of given degree. - * - * @arg k: the degree of the - * Raviart-Thomas-space, which is the degree - * of the largest tensor product - * polynomial space - * Qk contained. - */ - PolynomialsRaviartThomas (const unsigned int k); - - /** - * Computes the value and the - * first and second derivatives - * of each Raviart-Thomas - * polynomial at @p unit_point. - * - * The size of the vectors must - * either be zero or equal - * n(). In the - * first case, the function will - * not compute these values. - * - * If you need values or - * derivatives of all tensor - * product polynomials then use - * this function, rather than - * using any of the - * compute_value, - * compute_grad or - * compute_grad_grad - * functions, see below, in a - * loop over all tensor product - * polynomials. - */ - void compute (const Point &unit_point, - std::vector > &values, - std::vector > &grads, - std::vector > &grad_grads) const; - - /** - * Returns the number of Raviart-Thomas polynomials. - */ - unsigned int n () const; - - /** - * Returns the degree of the Raviart-Thomas - * space, which is one less than - * the highest polynomial degree. - */ - unsigned int degree () const; - - /** - * Return the name of the space , - * which is RaviartThomas. - */ - std::string name () const; - - /** - * Return the number of - * polynomials in the space - * RT(degree) without - * requiring to build an object - * of PolynomialsRaviartThomas. This is - * required by the FiniteElement - * classes. - */ - static unsigned int compute_n_pols(unsigned int degree); - - private: - /** - * The degree of this object as - * given to the constructor. - */ - const unsigned int my_degree; - - /** - * An object representing the - * polynomial space for a single - * component. We can re-use it by - * rotating the coordinates of - * the evaluation point. - */ - const AnisotropicPolynomials polynomial_space; - - /** - * Number of Raviart-Thomas - * polynomials. - */ - const unsigned int n_pols; - - /** - * A static member function that - * creates the polynomial space - * we use to initialize the - * #polynomial_space member - * variable. - */ - static - std::vector > > - create_polynomials (const unsigned int k); +public: + /** + * Constructor. Creates all basis + * functions for Raviart-Thomas polynomials + * of given degree. + * + * @arg k: the degree of the + * Raviart-Thomas-space, which is the degree + * of the largest tensor product + * polynomial space + * Qk contained. + */ + PolynomialsRaviartThomas (const unsigned int k); + + /** + * Computes the value and the + * first and second derivatives + * of each Raviart-Thomas + * polynomial at @p unit_point. + * + * The size of the vectors must + * either be zero or equal + * n(). In the + * first case, the function will + * not compute these values. + * + * If you need values or + * derivatives of all tensor + * product polynomials then use + * this function, rather than + * using any of the + * compute_value, + * compute_grad or + * compute_grad_grad + * functions, see below, in a + * loop over all tensor product + * polynomials. + */ + void compute (const Point &unit_point, + std::vector > &values, + std::vector > &grads, + std::vector > &grad_grads) const; + + /** + * Returns the number of Raviart-Thomas polynomials. + */ + unsigned int n () const; + + /** + * Returns the degree of the Raviart-Thomas + * space, which is one less than + * the highest polynomial degree. + */ + unsigned int degree () const; + + /** + * Return the name of the space , + * which is RaviartThomas. + */ + std::string name () const; + + /** + * Return the number of + * polynomials in the space + * RT(degree) without + * requiring to build an object + * of PolynomialsRaviartThomas. This is + * required by the FiniteElement + * classes. + */ + static unsigned int compute_n_pols(unsigned int degree); + +private: + /** + * The degree of this object as + * given to the constructor. + */ + const unsigned int my_degree; + + /** + * An object representing the + * polynomial space for a single + * component. We can re-use it by + * rotating the coordinates of + * the evaluation point. + */ + const AnisotropicPolynomials polynomial_space; + + /** + * Number of Raviart-Thomas + * polynomials. + */ + const unsigned int n_pols; + + /** + * A static member function that + * creates the polynomial space + * we use to initialize the + * #polynomial_space member + * variable. + */ + static + std::vector > > + create_polynomials (const unsigned int k); }; diff --git a/deal.II/include/deal.II/base/qprojector.h b/deal.II/include/deal.II/base/qprojector.h index 7ffa03220a..c89c41ffe7 100644 --- a/deal.II/include/deal.II/base/qprojector.h +++ b/deal.II/include/deal.II/base/qprojector.h @@ -72,380 +72,380 @@ DEAL_II_NAMESPACE_OPEN template class QProjector { +public: + /** + * Define a typedef for a + * quadrature that acts on an + * object of one dimension + * less. For cells, this would + * then be a face quadrature. + */ + typedef Quadrature SubQuadrature; + + /** + * Compute the quadrature points + * on the cell if the given + * quadrature formula is used on + * face face_no. For further + * details, see the general doc + * for this class. + */ + static void project_to_face (const SubQuadrature &quadrature, + const unsigned int face_no, + std::vector > &q_points); + + /** + * Compute the cell quadrature + * formula corresponding to using + * quadrature on face + * face_no. For further + * details, see the general doc + * for this class. + */ + static Quadrature + project_to_face (const SubQuadrature &quadrature, + const unsigned int face_no); + + /** + * Compute the quadrature points on the + * cell if the given quadrature formula is + * used on face face_no, subface + * number subface_no corresponding + * to RefineCase::Type + * ref_case. The last argument is + * only used in 3D. + * + * @note Only the points are + * transformed. The quadrature + * weights are the same as those + * of the original rule. + */ + static void project_to_subface (const SubQuadrature &quadrature, + const unsigned int face_no, + const unsigned int subface_no, + std::vector > &q_points, + const RefinementCase &ref_case=RefinementCase::isotropic_refinement); + + /** + * Compute the cell quadrature formula + * corresponding to using + * quadrature on subface + * subface_no of face + * face_no with + * RefinementCase + * ref_case. The last argument is + * only used in 3D. + * + * @note Only the points are + * transformed. The quadrature + * weights are the same as those + * of the original rule. + */ + static Quadrature + project_to_subface (const SubQuadrature &quadrature, + const unsigned int face_no, + const unsigned int subface_no, + const RefinementCase &ref_case=RefinementCase::isotropic_refinement); + + /** + * Take a face quadrature formula + * and generate a cell quadrature + * formula from it where the + * quadrature points of the given + * argument are projected on all + * faces. + * + * The weights of the new rule + * are replications of the + * original weights. Thus, the + * sum of the weights is not one, + * but the number of faces, which + * is the surface of the + * reference cell. + * + * This in particular allows us + * to extract a subset of points + * corresponding to a single face + * and use it as a quadrature on + * this face, as is done in + * FEFaceValues. + * + * @note In 3D, this function + * produces eight sets of + * quadrature points for each + * face, in order to cope + * possibly different + * orientations of the mesh. + */ + static Quadrature + project_to_all_faces (const SubQuadrature &quadrature); + + /** + * Take a face quadrature formula + * and generate a cell quadrature + * formula from it where the + * quadrature points of the given + * argument are projected on all + * subfaces. + * + * Like in project_to_all_faces(), + * the weights of the new rule + * sum up to the number of faces + * (not subfaces), which + * is the surface of the + * reference cell. + * + * This in particular allows us + * to extract a subset of points + * corresponding to a single subface + * and use it as a quadrature on + * this face, as is done in + * FESubfaceValues. + */ + static Quadrature + project_to_all_subfaces (const SubQuadrature &quadrature); + + /** + * Project a given quadrature + * formula to a child of a + * cell. You may want to use this + * function in case you want to + * extend an integral only over + * the area which a potential + * child would occupy. The child + * numbering is the same as the + * children would be numbered + * upon refinement of the cell. + * + * As integration using this + * quadrature formula now only + * extends over a fraction of the + * cell, the weights of the + * resulting object are divided by + * GeometryInfo::children_per_cell. + */ + static + Quadrature + project_to_child (const Quadrature &quadrature, + const unsigned int child_no); + + /** + * Project a quadrature rule to + * all children of a + * cell. Similarly to + * project_to_all_subfaces(), + * this function replicates the + * formula generated by + * project_to_child() for all + * children, such that the + * weights sum up to one, the + * volume of the total cell + * again. + * + * The child + * numbering is the same as the + * children would be numbered + * upon refinement of the cell. + */ + static + Quadrature + project_to_all_children (const Quadrature &quadrature); + + /** + * Project the onedimensional + * rule quadrature to + * the straight line connecting + * the points p1 and + * p2. + */ + static + Quadrature + project_to_line(const Quadrature<1> &quadrature, + const Point &p1, + const Point &p2); + + /** + * Since the + * project_to_all_faces() and + * project_to_all_subfaces() + * functions chain together the + * quadrature points and weights + * of all projections of a face + * quadrature formula to the + * faces or subfaces of a cell, + * we need a way to identify + * where the starting index of + * the points and weights for a + * particular face or subface + * is. This class provides this: + * there are static member + * functions that generate + * objects of this type, given + * face or subface indices, and + * you can then use the generated + * object in place of an integer + * that denotes the offset of a + * given dataset. + * + * @author Wolfgang Bangerth, 2003 + */ + class DataSetDescriptor + { public: - /** - * Define a typedef for a - * quadrature that acts on an - * object of one dimension - * less. For cells, this would - * then be a face quadrature. - */ - typedef Quadrature SubQuadrature; - - /** - * Compute the quadrature points - * on the cell if the given - * quadrature formula is used on - * face face_no. For further - * details, see the general doc - * for this class. - */ - static void project_to_face (const SubQuadrature &quadrature, - const unsigned int face_no, - std::vector > &q_points); - - /** - * Compute the cell quadrature - * formula corresponding to using - * quadrature on face - * face_no. For further - * details, see the general doc - * for this class. - */ - static Quadrature - project_to_face (const SubQuadrature &quadrature, - const unsigned int face_no); - - /** - * Compute the quadrature points on the - * cell if the given quadrature formula is - * used on face face_no, subface - * number subface_no corresponding - * to RefineCase::Type - * ref_case. The last argument is - * only used in 3D. - * - * @note Only the points are - * transformed. The quadrature - * weights are the same as those - * of the original rule. - */ - static void project_to_subface (const SubQuadrature &quadrature, - const unsigned int face_no, - const unsigned int subface_no, - std::vector > &q_points, - const RefinementCase &ref_case=RefinementCase::isotropic_refinement); - - /** - * Compute the cell quadrature formula - * corresponding to using - * quadrature on subface - * subface_no of face - * face_no with - * RefinementCase - * ref_case. The last argument is - * only used in 3D. - * - * @note Only the points are - * transformed. The quadrature - * weights are the same as those - * of the original rule. - */ - static Quadrature - project_to_subface (const SubQuadrature &quadrature, - const unsigned int face_no, - const unsigned int subface_no, - const RefinementCase &ref_case=RefinementCase::isotropic_refinement); - - /** - * Take a face quadrature formula - * and generate a cell quadrature - * formula from it where the - * quadrature points of the given - * argument are projected on all - * faces. - * - * The weights of the new rule - * are replications of the - * original weights. Thus, the - * sum of the weights is not one, - * but the number of faces, which - * is the surface of the - * reference cell. - * - * This in particular allows us - * to extract a subset of points - * corresponding to a single face - * and use it as a quadrature on - * this face, as is done in - * FEFaceValues. - * - * @note In 3D, this function - * produces eight sets of - * quadrature points for each - * face, in order to cope - * possibly different - * orientations of the mesh. - */ - static Quadrature - project_to_all_faces (const SubQuadrature &quadrature); - - /** - * Take a face quadrature formula - * and generate a cell quadrature - * formula from it where the - * quadrature points of the given - * argument are projected on all - * subfaces. - * - * Like in project_to_all_faces(), - * the weights of the new rule - * sum up to the number of faces - * (not subfaces), which - * is the surface of the - * reference cell. - * - * This in particular allows us - * to extract a subset of points - * corresponding to a single subface - * and use it as a quadrature on - * this face, as is done in - * FESubfaceValues. - */ - static Quadrature - project_to_all_subfaces (const SubQuadrature &quadrature); - - /** - * Project a given quadrature - * formula to a child of a - * cell. You may want to use this - * function in case you want to - * extend an integral only over - * the area which a potential - * child would occupy. The child - * numbering is the same as the - * children would be numbered - * upon refinement of the cell. - * - * As integration using this - * quadrature formula now only - * extends over a fraction of the - * cell, the weights of the - * resulting object are divided by - * GeometryInfo::children_per_cell. - */ + /** + * Default constructor. This + * doesn't do much except + * generating an invalid + * index, since you didn't + * give a valid descriptor of + * the cell, face, or subface + * you wanted. + */ + DataSetDescriptor (); + + /** + * Static function to + * generate the offset of a + * cell. Since we only have + * one cell per quadrature + * object, this offset is of + * course zero, but we carry + * this function around for + * consistency with the other + * static functions. + */ + static DataSetDescriptor cell (); + + /** + * Static function to generate an + * offset object for a given face of a + * cell with the given face + * orientation, flip and rotation. This + * function of course is only allowed + * if dim>=2, and the face + * orientation, flip and rotation are + * ignored if the space dimension + * equals 2. + * + * The last argument denotes + * the number of quadrature + * points the + * lower-dimensional face + * quadrature formula (the + * one that has been + * projected onto the faces) + * has. + */ static - Quadrature - project_to_child (const Quadrature &quadrature, - const unsigned int child_no); - - /** - * Project a quadrature rule to - * all children of a - * cell. Similarly to - * project_to_all_subfaces(), - * this function replicates the - * formula generated by - * project_to_child() for all - * children, such that the - * weights sum up to one, the - * volume of the total cell - * again. - * - * The child - * numbering is the same as the - * children would be numbered - * upon refinement of the cell. - */ + DataSetDescriptor + face (const unsigned int face_no, + const bool face_orientation, + const bool face_flip, + const bool face_rotation, + const unsigned int n_quadrature_points); + + /** + * Static function to generate an + * offset object for a given subface of + * a cell with the given face + * orientation, flip and rotation. This + * function of course is only allowed + * if dim>=2, and the face + * orientation, flip and rotation are + * ignored if the space dimension + * equals 2. + * + * The last but one argument denotes + * the number of quadrature + * points the + * lower-dimensional face + * quadrature formula (the + * one that has been + * projected onto the faces) + * has. + * + * Through the last argument + * anisotropic refinement can be + * respected. + */ static - Quadrature - project_to_all_children (const Quadrature &quadrature); - - /** - * Project the onedimensional - * rule quadrature to - * the straight line connecting - * the points p1 and - * p2. - */ - static - Quadrature - project_to_line(const Quadrature<1>& quadrature, - const Point& p1, - const Point& p2); - - /** - * Since the - * project_to_all_faces() and - * project_to_all_subfaces() - * functions chain together the - * quadrature points and weights - * of all projections of a face - * quadrature formula to the - * faces or subfaces of a cell, - * we need a way to identify - * where the starting index of - * the points and weights for a - * particular face or subface - * is. This class provides this: - * there are static member - * functions that generate - * objects of this type, given - * face or subface indices, and - * you can then use the generated - * object in place of an integer - * that denotes the offset of a - * given dataset. - * - * @author Wolfgang Bangerth, 2003 - */ - class DataSetDescriptor - { - public: - /** - * Default constructor. This - * doesn't do much except - * generating an invalid - * index, since you didn't - * give a valid descriptor of - * the cell, face, or subface - * you wanted. - */ - DataSetDescriptor (); - - /** - * Static function to - * generate the offset of a - * cell. Since we only have - * one cell per quadrature - * object, this offset is of - * course zero, but we carry - * this function around for - * consistency with the other - * static functions. - */ - static DataSetDescriptor cell (); - - /** - * Static function to generate an - * offset object for a given face of a - * cell with the given face - * orientation, flip and rotation. This - * function of course is only allowed - * if dim>=2, and the face - * orientation, flip and rotation are - * ignored if the space dimension - * equals 2. - * - * The last argument denotes - * the number of quadrature - * points the - * lower-dimensional face - * quadrature formula (the - * one that has been - * projected onto the faces) - * has. - */ - static - DataSetDescriptor - face (const unsigned int face_no, - const bool face_orientation, - const bool face_flip, - const bool face_rotation, - const unsigned int n_quadrature_points); - - /** - * Static function to generate an - * offset object for a given subface of - * a cell with the given face - * orientation, flip and rotation. This - * function of course is only allowed - * if dim>=2, and the face - * orientation, flip and rotation are - * ignored if the space dimension - * equals 2. - * - * The last but one argument denotes - * the number of quadrature - * points the - * lower-dimensional face - * quadrature formula (the - * one that has been - * projected onto the faces) - * has. - * - * Through the last argument - * anisotropic refinement can be - * respected. - */ - static - DataSetDescriptor - subface (const unsigned int face_no, - const unsigned int subface_no, - const bool face_orientation, - const bool face_flip, - const bool face_rotation, - const unsigned int n_quadrature_points, - const internal::SubfaceCase ref_case=internal::SubfaceCase::case_isotropic); - - /** - * Conversion operator to an - * integer denoting the - * offset of the first - * element of this dataset in - * the set of quadrature - * formulas all projected - * onto faces and - * subfaces. This conversion - * operator allows us to use - * offset descriptor objects - * in place of integer - * offsets. - */ - operator unsigned int () const; - - private: - /** - * Store the integer offset - * for a given cell, face, or - * subface. - */ - const unsigned int dataset_offset; - - /** - * This is the real - * constructor, but it is - * private and thus only - * available to the static - * member functions above. - */ - DataSetDescriptor (const unsigned int dataset_offset); - }; + DataSetDescriptor + subface (const unsigned int face_no, + const unsigned int subface_no, + const bool face_orientation, + const bool face_flip, + const bool face_rotation, + const unsigned int n_quadrature_points, + const internal::SubfaceCase ref_case=internal::SubfaceCase::case_isotropic); + + /** + * Conversion operator to an + * integer denoting the + * offset of the first + * element of this dataset in + * the set of quadrature + * formulas all projected + * onto faces and + * subfaces. This conversion + * operator allows us to use + * offset descriptor objects + * in place of integer + * offsets. + */ + operator unsigned int () const; private: - /** - * Given a quadrature object in - * 2d, reflect all quadrature - * points at the main diagonal - * and return them with their - * original weights. - * - * This function is necessary for - * projecting a 2d quadrature - * rule onto the faces of a 3d - * cube, since there we need both - * orientations. - */ - static Quadrature<2> reflect (const Quadrature<2> &q); - - /** - * Given a quadrature object in - * 2d, rotate all quadrature - * points by @p n_times * 90 degrees - * counterclockwise - * and return them with their - * original weights. - * - * This function is necessary for - * projecting a 2d quadrature - * rule onto the faces of a 3d - * cube, since there we need all - * rotations to account for - * face_flip and face_rotation - * of non-standard faces. - */ - static Quadrature<2> rotate (const Quadrature<2> &q, - const unsigned int n_times); + /** + * Store the integer offset + * for a given cell, face, or + * subface. + */ + const unsigned int dataset_offset; + + /** + * This is the real + * constructor, but it is + * private and thus only + * available to the static + * member functions above. + */ + DataSetDescriptor (const unsigned int dataset_offset); + }; + +private: + /** + * Given a quadrature object in + * 2d, reflect all quadrature + * points at the main diagonal + * and return them with their + * original weights. + * + * This function is necessary for + * projecting a 2d quadrature + * rule onto the faces of a 3d + * cube, since there we need both + * orientations. + */ + static Quadrature<2> reflect (const Quadrature<2> &q); + + /** + * Given a quadrature object in + * 2d, rotate all quadrature + * points by @p n_times * 90 degrees + * counterclockwise + * and return them with their + * original weights. + * + * This function is necessary for + * projecting a 2d quadrature + * rule onto the faces of a 3d + * cube, since there we need all + * rotations to account for + * face_flip and face_rotation + * of non-standard faces. + */ + static Quadrature<2> rotate (const Quadrature<2> &q, + const unsigned int n_times); }; /*@}*/ @@ -459,8 +459,8 @@ template inline QProjector::DataSetDescriptor:: DataSetDescriptor (const unsigned int dataset_offset) - : - dataset_offset (dataset_offset) + : + dataset_offset (dataset_offset) {} @@ -468,8 +468,8 @@ template inline QProjector::DataSetDescriptor:: DataSetDescriptor () - : - dataset_offset (numbers::invalid_unsigned_int) + : + dataset_offset (numbers::invalid_unsigned_int) {} diff --git a/deal.II/include/deal.II/base/quadrature.h b/deal.II/include/deal.II/base/quadrature.h index 6d891868fe..cfe06b50fc 100644 --- a/deal.II/include/deal.II/base/quadrature.h +++ b/deal.II/include/deal.II/base/quadrature.h @@ -79,195 +79,195 @@ DEAL_II_NAMESPACE_OPEN template class Quadrature : public Subscriptor { - public: - /** - * Define a typedef for a - * quadrature that acts on an - * object of one dimension - * less. For cells, this would - * then be a face quadrature. - */ - typedef Quadrature SubQuadrature; - - /** - * Constructor. - * - * This constructor is marked as - * explicit to avoid involuntary - * accidents like in - * hp::QCollection@ - * q_collection(3) where - * hp::QCollection@ - * q_collection(QGauss@(3)) - * was meant. - */ - explicit Quadrature (const unsigned int n_quadrature_points = 0); - - /** - * Build this quadrature formula - * as the tensor product of a - * formula in a dimension one - * less than the present and a - * formula in one dimension. - * - * SubQuadrature::type - * expands to - * Quadrature. - */ - Quadrature (const SubQuadrature &, - const Quadrature<1> &); - - /** - * Build this quadrature formula - * as the dim-fold - * tensor product of a formula in - * one dimension. - * - * Assuming that the points in - * the one-dimensional rule are in - * ascending order, the points of - * the resulting rule are ordered - * lexicographically with - * x running fastest. - * - * In order to avoid a conflict - * with the copy constructor in - * 1d, we let the argument be a - * 0d quadrature formula for - * dim==1, and a 1d quadrature - * formula for all other space - * dimensions. - */ - explicit Quadrature (const Quadrature &quadrature_1d); - - /** - * Copy constructor. - */ - Quadrature (const Quadrature &q); - - /** - * Construct a quadrature formula - * from given vectors of - * quadrature points (which - * should really be in the unit - * cell) and the corresponding - * weights. You will want to have - * the weights sum up to one, but - * this is not checked. - */ - Quadrature (const std::vector > &points, - const std::vector &weights); - - /** - * Construct a dummy quadrature - * formula from a list of points, - * with weights set to - * infinity. The resulting object - * is therefore not meant to - * actually perform integrations, - * but rather to be used with - * FEValues objects in - * order to find the position of - * some points (the quadrature - * points in this object) on the - * transformed cell in real - * space. - */ - Quadrature (const std::vector > &points); - - /** - * Constructor for a one-point - * quadrature. Sets the weight of - * this point to one. - */ - Quadrature (const Point &point); - - /** - * Virtual destructor. - */ - virtual ~Quadrature (); - - /** - * Assignment operator. Copies - * contents of #weights and - * #quadrature_points as well as - * size. - */ - Quadrature& operator = (const Quadrature&); - - /** - * Test for equality of two quadratures. - */ - bool operator == (const Quadrature & p) const; - - /** - * Set the quadrature points and - * weights to the values provided - * in the arguments. - */ - void initialize(const std::vector > &points, - const std::vector &weights); - - /** - * Number of quadrature points. - */ - unsigned int size () const; - - /** - * Return the ith quadrature - * point. - */ - const Point & point (const unsigned int i) const; - - /** - * Return a reference to the - * whole array of quadrature - * points. - */ - const std::vector > & get_points () const; - - /** - * Return the weight of the ith - * quadrature point. - */ - double weight (const unsigned int i) const; - - /** - * Return a reference to the whole array - * of weights. - */ - const std::vector & get_weights () const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. - */ - std::size_t memory_consumption () const; - - /** - * Write or read the data of this object to or - * from a stream for the purpose of serialization. - */ - template - void serialize (Archive & ar, const unsigned int version); - - protected: - /** - * List of quadrature points. To - * be filled by the constructors - * of derived classes. - */ - std::vector > quadrature_points; - - /** - * List of weights of the - * quadrature points. To be - * filled by the constructors of - * derived classes. - */ - std::vector weights; +public: + /** + * Define a typedef for a + * quadrature that acts on an + * object of one dimension + * less. For cells, this would + * then be a face quadrature. + */ + typedef Quadrature SubQuadrature; + + /** + * Constructor. + * + * This constructor is marked as + * explicit to avoid involuntary + * accidents like in + * hp::QCollection@ + * q_collection(3) where + * hp::QCollection@ + * q_collection(QGauss@(3)) + * was meant. + */ + explicit Quadrature (const unsigned int n_quadrature_points = 0); + + /** + * Build this quadrature formula + * as the tensor product of a + * formula in a dimension one + * less than the present and a + * formula in one dimension. + * + * SubQuadrature::type + * expands to + * Quadrature. + */ + Quadrature (const SubQuadrature &, + const Quadrature<1> &); + + /** + * Build this quadrature formula + * as the dim-fold + * tensor product of a formula in + * one dimension. + * + * Assuming that the points in + * the one-dimensional rule are in + * ascending order, the points of + * the resulting rule are ordered + * lexicographically with + * x running fastest. + * + * In order to avoid a conflict + * with the copy constructor in + * 1d, we let the argument be a + * 0d quadrature formula for + * dim==1, and a 1d quadrature + * formula for all other space + * dimensions. + */ + explicit Quadrature (const Quadrature &quadrature_1d); + + /** + * Copy constructor. + */ + Quadrature (const Quadrature &q); + + /** + * Construct a quadrature formula + * from given vectors of + * quadrature points (which + * should really be in the unit + * cell) and the corresponding + * weights. You will want to have + * the weights sum up to one, but + * this is not checked. + */ + Quadrature (const std::vector > &points, + const std::vector &weights); + + /** + * Construct a dummy quadrature + * formula from a list of points, + * with weights set to + * infinity. The resulting object + * is therefore not meant to + * actually perform integrations, + * but rather to be used with + * FEValues objects in + * order to find the position of + * some points (the quadrature + * points in this object) on the + * transformed cell in real + * space. + */ + Quadrature (const std::vector > &points); + + /** + * Constructor for a one-point + * quadrature. Sets the weight of + * this point to one. + */ + Quadrature (const Point &point); + + /** + * Virtual destructor. + */ + virtual ~Quadrature (); + + /** + * Assignment operator. Copies + * contents of #weights and + * #quadrature_points as well as + * size. + */ + Quadrature &operator = (const Quadrature &); + + /** + * Test for equality of two quadratures. + */ + bool operator == (const Quadrature &p) const; + + /** + * Set the quadrature points and + * weights to the values provided + * in the arguments. + */ + void initialize(const std::vector > &points, + const std::vector &weights); + + /** + * Number of quadrature points. + */ + unsigned int size () const; + + /** + * Return the ith quadrature + * point. + */ + const Point &point (const unsigned int i) const; + + /** + * Return a reference to the + * whole array of quadrature + * points. + */ + const std::vector > &get_points () const; + + /** + * Return the weight of the ith + * quadrature point. + */ + double weight (const unsigned int i) const; + + /** + * Return a reference to the whole array + * of weights. + */ + const std::vector &get_weights () const; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. + */ + std::size_t memory_consumption () const; + + /** + * Write or read the data of this object to or + * from a stream for the purpose of serialization. + */ + template + void serialize (Archive &ar, const unsigned int version); + +protected: + /** + * List of quadrature points. To + * be filled by the constructors + * of derived classes. + */ + std::vector > quadrature_points; + + /** + * List of weights of the + * quadrature points. To be + * filled by the constructors of + * derived classes. + */ + std::vector weights; }; @@ -285,29 +285,29 @@ class Quadrature : public Subscriptor template class QAnisotropic : public Quadrature { - public: - /** - * Constructor for a - * one-dimensional formula. This - * one just copies the given - * quadrature rule. - */ - QAnisotropic(const Quadrature<1>& qx); - - /** - * Constructor for a - * two-dimensional formula. - */ - QAnisotropic(const Quadrature<1>& qx, - const Quadrature<1>& qy); - - /** - * Constructor for a - * three-dimensional formula. - */ - QAnisotropic(const Quadrature<1>& qx, - const Quadrature<1>& qy, - const Quadrature<1>& qz); +public: + /** + * Constructor for a + * one-dimensional formula. This + * one just copies the given + * quadrature rule. + */ + QAnisotropic(const Quadrature<1> &qx); + + /** + * Constructor for a + * two-dimensional formula. + */ + QAnisotropic(const Quadrature<1> &qx, + const Quadrature<1> &qy); + + /** + * Constructor for a + * three-dimensional formula. + */ + QAnisotropic(const Quadrature<1> &qx, + const Quadrature<1> &qy, + const Quadrature<1> &qz); }; @@ -339,28 +339,28 @@ class QAnisotropic : public Quadrature template class QIterated : public Quadrature { - public: - /** - * Constructor. Iterate the given - * quadrature formula n_copies times in - * each direction. - */ - QIterated (const Quadrature<1> &base_quadrature, - const unsigned int n_copies); - - /** - * Exception - */ - DeclException0 (ExcInvalidQuadratureFormula); - private: - /** - * Check whether the given - * quadrature formula has quadrature - * points at the left and right end points - * of the interval. - */ - static bool - uses_both_endpoints (const Quadrature<1> &base_quadrature); +public: + /** + * Constructor. Iterate the given + * quadrature formula n_copies times in + * each direction. + */ + QIterated (const Quadrature<1> &base_quadrature, + const unsigned int n_copies); + + /** + * Exception + */ + DeclException0 (ExcInvalidQuadratureFormula); +private: + /** + * Check whether the given + * quadrature formula has quadrature + * points at the left and right end points + * of the interval. + */ + static bool + uses_both_endpoints (const Quadrature<1> &base_quadrature); }; @@ -426,13 +426,13 @@ template template inline void -Quadrature::serialize (Archive & ar, const unsigned int) +Quadrature::serialize (Archive &ar, const unsigned int) { - // forward to serialization - // function in the base class. - ar & static_cast(*this); + // forward to serialization + // function in the base class. + ar &static_cast(*this); - ar & quadrature_points & weights; + ar &quadrature_points &weights; } diff --git a/deal.II/include/deal.II/base/quadrature_lib.h b/deal.II/include/deal.II/base/quadrature_lib.h index a73a6d2f26..b152a99937 100644 --- a/deal.II/include/deal.II/base/quadrature_lib.h +++ b/deal.II/include/deal.II/base/quadrature_lib.h @@ -32,15 +32,15 @@ DEAL_II_NAMESPACE_OPEN template class QGauss : public Quadrature { - public: - /** - * Generate a formula with - * n quadrature points (in - * each space direction), exact for - * polynomials of degree - * 2n-1. - */ - QGauss (const unsigned int n); +public: + /** + * Generate a formula with + * n quadrature points (in + * each space direction), exact for + * polynomials of degree + * 2n-1. + */ + QGauss (const unsigned int n); }; @@ -73,67 +73,67 @@ class QGauss : public Quadrature template class QGaussLobatto : public Quadrature { - public: - /** - * Generate a formula with - * n quadrature points - * (in each space direction). - */ - QGaussLobatto(const unsigned int n); - - protected: - /** - * Compute Legendre-Gauss-Lobatto - * quadrature points in the - * interval $[-1, +1]$. They are - * equal to the roots of the - * corresponding Jacobi - * polynomial (specified by @p - * alpha, @p beta). @p q is - * number of points. - * - * @return vector containing nodes. - */ - std::vector - compute_quadrature_points (const unsigned int q, - const int alpha, - const int beta) const; - - /** - * Compute Legendre-Gauss-Lobatto quadrature - * weights. - * The quadrature points and weights are - * related to Jacobi polynomial specified - * by @p alpha, @p beta. - * @p x denotes the quadrature points. - * @return vector containing weights. - */ - std::vector - compute_quadrature_weights (const std::vector &x, - const int alpha, - const int beta) const; - - /** - * Evaluate a Jacobi polynomial - * $ P^{\alpha, \beta}_n(x) $ - * specified by the parameters - * @p alpha, @p beta, @p n. - * Note: The Jacobi polynomials are - * not orthonormal and defined on - * the interval $[-1, +1]$. - * @p x is the point of evaluation. - */ - long double JacobiP(const long double x, - const int alpha, - const int beta, - const unsigned int n) const; - - /** - * Evaluate the Gamma function - * $ \Gamma(n) = (n-1)! $. - * @param n point of evaluation (integer). - */ - long double gamma(const unsigned int n) const; +public: + /** + * Generate a formula with + * n quadrature points + * (in each space direction). + */ + QGaussLobatto(const unsigned int n); + +protected: + /** + * Compute Legendre-Gauss-Lobatto + * quadrature points in the + * interval $[-1, +1]$. They are + * equal to the roots of the + * corresponding Jacobi + * polynomial (specified by @p + * alpha, @p beta). @p q is + * number of points. + * + * @return vector containing nodes. + */ + std::vector + compute_quadrature_points (const unsigned int q, + const int alpha, + const int beta) const; + + /** + * Compute Legendre-Gauss-Lobatto quadrature + * weights. + * The quadrature points and weights are + * related to Jacobi polynomial specified + * by @p alpha, @p beta. + * @p x denotes the quadrature points. + * @return vector containing weights. + */ + std::vector + compute_quadrature_weights (const std::vector &x, + const int alpha, + const int beta) const; + + /** + * Evaluate a Jacobi polynomial + * $ P^{\alpha, \beta}_n(x) $ + * specified by the parameters + * @p alpha, @p beta, @p n. + * Note: The Jacobi polynomials are + * not orthonormal and defined on + * the interval $[-1, +1]$. + * @p x is the point of evaluation. + */ + long double JacobiP(const long double x, + const int alpha, + const int beta, + const unsigned int n) const; + + /** + * Evaluate the Gamma function + * $ \Gamma(n) = (n-1)! $. + * @param n point of evaluation (integer). + */ + long double gamma(const unsigned int n) const; }; @@ -144,8 +144,8 @@ class QGaussLobatto : public Quadrature template class QMidpoint : public Quadrature { - public: - QMidpoint (); +public: + QMidpoint (); }; @@ -155,8 +155,8 @@ class QMidpoint : public Quadrature template class QSimpson : public Quadrature { - public: - QSimpson (); +public: + QSimpson (); }; @@ -166,8 +166,8 @@ class QSimpson : public Quadrature template class QTrapez : public Quadrature { - public: - QTrapez (); +public: + QTrapez (); }; /** @@ -177,8 +177,8 @@ class QTrapez : public Quadrature template class QMilne : public Quadrature { - public: - QMilne (); +public: + QMilne (); }; @@ -189,8 +189,8 @@ class QMilne : public Quadrature template class QWeddle : public Quadrature { - public: - QWeddle (); +public: + QWeddle (); }; @@ -213,26 +213,26 @@ class QWeddle : public Quadrature template class QGaussLog : public Quadrature { - public: - /** - * Generate a formula with - * n quadrature points - */ +public: + /** + * Generate a formula with + * n quadrature points + */ QGaussLog(const unsigned int n, const bool revert=false); - protected: - /** - * Sets the points of the - * quadrature formula. - */ +protected: + /** + * Sets the points of the + * quadrature formula. + */ std::vector set_quadrature_points(const unsigned int n) const; - /** - * Sets the weights of the - * quadrature formula. - */ + /** + * Sets the weights of the + * quadrature formula. + */ std::vector set_quadrature_weights(const unsigned int n) const; @@ -287,31 +287,31 @@ class QGaussLog : public Quadrature template class QGaussLogR : public Quadrature { - public: - /** - * The constructor takes four arguments: - * the order of the gauss formula on each - * of the segments $[0,x_0]$ and - * $[x_0,1]$, the actual location of the - * singularity, the scale factor inside - * the logarithmic function and a flag - * that decides wether the singularity is - * left inside the quadrature formula or - * it is factored out, to be included in - * the integrand. - */ - QGaussLogR(const unsigned int n, - const Point x0 = Point(), - const double alpha = 1, - const bool factor_out_singular_weight=false); - - protected: - /** - * This is the length of interval - * $(0,origin)$, or 1 if either of the two - * extremes have been selected. - */ - const double fraction; +public: + /** + * The constructor takes four arguments: + * the order of the gauss formula on each + * of the segments $[0,x_0]$ and + * $[x_0,1]$, the actual location of the + * singularity, the scale factor inside + * the logarithmic function and a flag + * that decides wether the singularity is + * left inside the quadrature formula or + * it is factored out, to be included in + * the integrand. + */ + QGaussLogR(const unsigned int n, + const Point x0 = Point(), + const double alpha = 1, + const bool factor_out_singular_weight=false); + +protected: + /** + * This is the length of interval + * $(0,origin)$, or 1 if either of the two + * extremes have been selected. + */ + const double fraction; }; @@ -342,90 +342,90 @@ class QGaussLogR : public Quadrature template class QGaussOneOverR : public Quadrature { - public: - /** - * This constructor takes three arguments: the order of the Gauss - * formula, the point of the reference element in which the - * singularity is located, and whether we include the weighting - * singular function inside the quadrature, or we leave it in the - * user function to be integrated. - * - * Traditionally, quadrature formulas include their weighting - * function, and the last argument is set to false by - * default. There are cases, however, where this is undesirable - * (for example when you only know that your singularity has the - * same order of 1/R, but cannot be written exactly in this - * way). - * - * In other words, you can use this function in either of - * the following way, obtaining the same result: - * - * @code - * QGaussOneOverR singular_quad(order, q_point, false); - * // This will produce the integral of f(x)/R - * for(unsigned int i=0; ivertex(vertex_id)).norm(); - * integral += f(singular_quad_noR.point(i))*singular_quad_noR.weight(i)/R; - * } - * @endcode - */ - QGaussOneOverR(const unsigned int n, - const Point singularity, - const bool factor_out_singular_weight=false); - /** - * The constructor takes three arguments: the order of the Gauss - * formula, the index of the vertex where the singularity is - * located, and whether we include the weighting singular function - * inside the quadrature, or we leave it in the user function to - * be integrated. Notice that this is a specialized version of the - * previous constructor which works only for the vertices of the - * quadrilateral. - * - * Traditionally, quadrature formulas include their weighting - * function, and the last argument is set to false by - * default. There are cases, however, where this is undesirable - * (for example when you only know that your singularity has the - * same order of 1/R, but cannot be written exactly in this - * way). - * - * In other words, you can use this function in either of - * the following way, obtaining the same result: - * - * @code - * QGaussOneOverR singular_quad(order, vertex_id, false); - * // This will produce the integral of f(x)/R - * for(unsigned int i=0; ivertex(vertex_id)).norm(); - * integral += f(singular_quad_noR.point(i))*singular_quad_noR.weight(i)/R; - * } - * @endcode - */ - QGaussOneOverR(const unsigned int n, - const unsigned int vertex_index, - const bool factor_out_singular_weight=false); - private: - /** Given a quadrature point and a degree n, this function returns - * the size of the singular quadrature rule, considering whether - * the point is inside the cell, on an edge of the cell, or on a - * corner of the cell. */ - static unsigned int quad_size(const Point singularity, - const unsigned int n); +public: + /** + * This constructor takes three arguments: the order of the Gauss + * formula, the point of the reference element in which the + * singularity is located, and whether we include the weighting + * singular function inside the quadrature, or we leave it in the + * user function to be integrated. + * + * Traditionally, quadrature formulas include their weighting + * function, and the last argument is set to false by + * default. There are cases, however, where this is undesirable + * (for example when you only know that your singularity has the + * same order of 1/R, but cannot be written exactly in this + * way). + * + * In other words, you can use this function in either of + * the following way, obtaining the same result: + * + * @code + * QGaussOneOverR singular_quad(order, q_point, false); + * // This will produce the integral of f(x)/R + * for(unsigned int i=0; ivertex(vertex_id)).norm(); + * integral += f(singular_quad_noR.point(i))*singular_quad_noR.weight(i)/R; + * } + * @endcode + */ + QGaussOneOverR(const unsigned int n, + const Point singularity, + const bool factor_out_singular_weight=false); + /** + * The constructor takes three arguments: the order of the Gauss + * formula, the index of the vertex where the singularity is + * located, and whether we include the weighting singular function + * inside the quadrature, or we leave it in the user function to + * be integrated. Notice that this is a specialized version of the + * previous constructor which works only for the vertices of the + * quadrilateral. + * + * Traditionally, quadrature formulas include their weighting + * function, and the last argument is set to false by + * default. There are cases, however, where this is undesirable + * (for example when you only know that your singularity has the + * same order of 1/R, but cannot be written exactly in this + * way). + * + * In other words, you can use this function in either of + * the following way, obtaining the same result: + * + * @code + * QGaussOneOverR singular_quad(order, vertex_id, false); + * // This will produce the integral of f(x)/R + * for(unsigned int i=0; ivertex(vertex_id)).norm(); + * integral += f(singular_quad_noR.point(i))*singular_quad_noR.weight(i)/R; + * } + * @endcode + */ + QGaussOneOverR(const unsigned int n, + const unsigned int vertex_index, + const bool factor_out_singular_weight=false); +private: + /** Given a quadrature point and a degree n, this function returns + * the size of the singular quadrature rule, considering whether + * the point is inside the cell, on an edge of the cell, or on a + * corner of the cell. */ + static unsigned int quad_size(const Point singularity, + const unsigned int n); }; @@ -441,7 +441,7 @@ std::vector QGaussLobatto<1>:: compute_quadrature_points(const unsigned int, const int, const int) const; template <> std::vector QGaussLobatto<1>:: -compute_quadrature_weights(const std::vector&, const int, const int) const; +compute_quadrature_weights(const std::vector &, const int, const int) const; template <> long double QGaussLobatto<1>:: JacobiP(const long double, const int, const int, const unsigned int) const; diff --git a/deal.II/include/deal.II/base/quadrature_selector.h b/deal.II/include/deal.II/base/quadrature_selector.h index 837cc4cc81..be61b286d1 100644 --- a/deal.II/include/deal.II/base/quadrature_selector.h +++ b/deal.II/include/deal.II/base/quadrature_selector.h @@ -37,70 +37,70 @@ DEAL_II_NAMESPACE_OPEN template class QuadratureSelector : public Quadrature { - public: - /** - * Constructor. Takes the name of - * the quadrature rule (one of - * "gauss", "milne", "weddle", - * etc) and, if it iss "gauss", - * the order of the quadrature - * rule as argument. - */ - QuadratureSelector (const std::string &s, - const unsigned int order=0); +public: + /** + * Constructor. Takes the name of + * the quadrature rule (one of + * "gauss", "milne", "weddle", + * etc) and, if it iss "gauss", + * the order of the quadrature + * rule as argument. + */ + QuadratureSelector (const std::string &s, + const unsigned int order=0); - /** - * This function returns all - * possible names for quadratures - * as a list separated by |, - * so that you can use it for the - * definition of parameter files - * (see ParameterHandler for - * details). - */ - static std::string get_quadrature_names(); + /** + * This function returns all + * possible names for quadratures + * as a list separated by |, + * so that you can use it for the + * definition of parameter files + * (see ParameterHandler for + * details). + */ + static std::string get_quadrature_names(); - /** @addtogroup Exceptions - * @{ */ + /** @addtogroup Exceptions + * @{ */ - /** - * Exception - */ - DeclException1 (ExcInvalidQGaussOrder, - int, - << "You tried to generate QGauss with an invalid order of " - << arg1 << " (must be >= 2)"); - /** - * Exception - */ - DeclException2 (ExcInvalidOrder, - std::string, - unsigned int, - << "You tried to generate a " << arg1 - << " object; no order is needed (" << arg2 - << " was given as parameter)"); - /** - * Exception - */ - DeclException1 (ExcInvalidQuadrature, - std::string, - << arg1 - << " is not a valid quadrature name for a quadrature rule"); - //@} - private: - /** - * This static function creates a - * quadrature object according to - * the name given as a string, - * and the appropriate order (if - * the name is "gauss"). It is - * called from the constructor. - */ - static - Quadrature - create_quadrature (const std::string &s, - const unsigned int order); + /** + * Exception + */ + DeclException1 (ExcInvalidQGaussOrder, + int, + << "You tried to generate QGauss with an invalid order of " + << arg1 << " (must be >= 2)"); + /** + * Exception + */ + DeclException2 (ExcInvalidOrder, + std::string, + unsigned int, + << "You tried to generate a " << arg1 + << " object; no order is needed (" << arg2 + << " was given as parameter)"); + /** + * Exception + */ + DeclException1 (ExcInvalidQuadrature, + std::string, + << arg1 + << " is not a valid quadrature name for a quadrature rule"); + //@} +private: + /** + * This static function creates a + * quadrature object according to + * the name given as a string, + * and the appropriate order (if + * the name is "gauss"). It is + * called from the constructor. + */ + static + Quadrature + create_quadrature (const std::string &s, + const unsigned int order); }; DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/include/deal.II/base/smartpointer.h b/deal.II/include/deal.II/base/smartpointer.h index f580e80acc..1efb98f93d 100644 --- a/deal.II/include/deal.II/base/smartpointer.h +++ b/deal.II/include/deal.II/base/smartpointer.h @@ -58,194 +58,194 @@ DEAL_II_NAMESPACE_OPEN template class SmartPointer { - public: - /** - * Standard constructor for null - * pointer. The id of this - * pointer is set to the name of - * the class P. - */ - SmartPointer (); - - /* - * Copy constructor for - * SmartPointer. We do now - * copy the object subscribed to - * from tt, but subscribe - * ourselves to it again. - */ - template - SmartPointer (const SmartPointer &tt); - - /* - * Copy constructor for - * SmartPointer. We do now - * copy the object subscribed to - * from tt, but subscribe - * ourselves to it again. - */ - SmartPointer (const SmartPointer &tt); - - /** - * Constructor taking a normal - * pointer. If possible, i.e. if - * the pointer is not a null - * pointer, the constructor - * subscribes to the given object - * to lock it, i.e. to prevent - * its destruction before the end - * of its use. - * - * The id is used in the - * call to - * Subscriptor::subscribe(id) and - * by ~SmartPointer() in the call - * to Subscriptor::unsubscribe(). - */ - SmartPointer (T *t, const char* id); - - /** - * Constructor taking a normal - * pointer. If possible, i.e. if - * the pointer is not a null - * pointer, the constructor - * subscribes to the given object - * to lock it, i.e. to prevent - * its destruction before the end - * of its use. The id of this - * pointer is set to the name of - * the class P. - */ - SmartPointer (T *t); - - - /** - * Destructor, removing the - * subscription. - */ - ~SmartPointer(); - - /** - * Assignment operator for normal - * pointers. The pointer - * subscribes to the new object - * automatically and unsubscribes - * to an old one if it exists. It - * will not try to subscribe to a - * null-pointer, but still - * delete the old subscription. - */ - SmartPointer & operator= (T *tt); - - /** - * Assignment operator for - * SmartPointer. The pointer - * subscribes to the new object - * automatically and unsubscribes - * to an old one if it exists. - */ - template - SmartPointer & operator= (const SmartPointer &tt); - - /** - * Assignment operator for - * SmartPointer. The pointer - * subscribes to the new object - * automatically and unsubscribes - * to an old one if it exists. - */ - SmartPointer & operator= (const SmartPointer &tt); - - /** - * Delete the object pointed to - * and set the pointer to zero. - */ - void clear (); - - /** - * Conversion to normal pointer. - */ - operator T* () const; - - /** - * Dereferencing operator. This - * operator throws an - * ExcNotInitialized if the - * pointer is a null pointer. - */ - T& operator * () const; - - /** - * Dereferencing operator. This - * operator throws an - * ExcNotInitialized if the - * pointer is a null pointer. - */ - T * operator -> () const; - - /** - * Exchange the pointers of this - * object and the argument. Since - * both the objects to which is - * pointed are subscribed to - * before and after, we do not - * have to change their - * subscription counters. - * - * Note that this function (with - * two arguments) and the - * respective functions where one - * of the arguments is a pointer - * and the other one is a C-style - * pointer are implemented in - * global namespace. - */ - template - void swap (SmartPointer &tt); - - /** - * Swap pointers between this - * object and the pointer - * given. As this releases the - * object pointed to presently, - * we reduce its subscription - * count by one, and increase it - * at the object which we will - * point to in the future. - * - * Note that we indeed need a - * reference of a pointer, as we - * want to change the pointer - * variable which we are given. - */ - void swap (T *&tt); - - /** - * Return an estimate of the - * amount of memory (in bytes) - * used by this class. Note in - * particular, that this only - * includes the amount of memory - * used by this object, not - * by the object pointed to. - */ - std::size_t memory_consumption () const; - - private: - /** - * Pointer to the object we want - * to subscribt to. Since it is - * often necessary to follow this - * pointer when debugging, we - * have deliberately chosen a - * short name. - */ - T * t; - /** - * The identification for the - * subscriptor. - */ - const char* const id; +public: + /** + * Standard constructor for null + * pointer. The id of this + * pointer is set to the name of + * the class P. + */ + SmartPointer (); + + /* + * Copy constructor for + * SmartPointer. We do now + * copy the object subscribed to + * from tt, but subscribe + * ourselves to it again. + */ + template + SmartPointer (const SmartPointer &tt); + + /* + * Copy constructor for + * SmartPointer. We do now + * copy the object subscribed to + * from tt, but subscribe + * ourselves to it again. + */ + SmartPointer (const SmartPointer &tt); + + /** + * Constructor taking a normal + * pointer. If possible, i.e. if + * the pointer is not a null + * pointer, the constructor + * subscribes to the given object + * to lock it, i.e. to prevent + * its destruction before the end + * of its use. + * + * The id is used in the + * call to + * Subscriptor::subscribe(id) and + * by ~SmartPointer() in the call + * to Subscriptor::unsubscribe(). + */ + SmartPointer (T *t, const char *id); + + /** + * Constructor taking a normal + * pointer. If possible, i.e. if + * the pointer is not a null + * pointer, the constructor + * subscribes to the given object + * to lock it, i.e. to prevent + * its destruction before the end + * of its use. The id of this + * pointer is set to the name of + * the class P. + */ + SmartPointer (T *t); + + + /** + * Destructor, removing the + * subscription. + */ + ~SmartPointer(); + + /** + * Assignment operator for normal + * pointers. The pointer + * subscribes to the new object + * automatically and unsubscribes + * to an old one if it exists. It + * will not try to subscribe to a + * null-pointer, but still + * delete the old subscription. + */ + SmartPointer &operator= (T *tt); + + /** + * Assignment operator for + * SmartPointer. The pointer + * subscribes to the new object + * automatically and unsubscribes + * to an old one if it exists. + */ + template + SmartPointer &operator= (const SmartPointer &tt); + + /** + * Assignment operator for + * SmartPointer. The pointer + * subscribes to the new object + * automatically and unsubscribes + * to an old one if it exists. + */ + SmartPointer &operator= (const SmartPointer &tt); + + /** + * Delete the object pointed to + * and set the pointer to zero. + */ + void clear (); + + /** + * Conversion to normal pointer. + */ + operator T *() const; + + /** + * Dereferencing operator. This + * operator throws an + * ExcNotInitialized if the + * pointer is a null pointer. + */ + T &operator * () const; + + /** + * Dereferencing operator. This + * operator throws an + * ExcNotInitialized if the + * pointer is a null pointer. + */ + T *operator -> () const; + + /** + * Exchange the pointers of this + * object and the argument. Since + * both the objects to which is + * pointed are subscribed to + * before and after, we do not + * have to change their + * subscription counters. + * + * Note that this function (with + * two arguments) and the + * respective functions where one + * of the arguments is a pointer + * and the other one is a C-style + * pointer are implemented in + * global namespace. + */ + template + void swap (SmartPointer &tt); + + /** + * Swap pointers between this + * object and the pointer + * given. As this releases the + * object pointed to presently, + * we reduce its subscription + * count by one, and increase it + * at the object which we will + * point to in the future. + * + * Note that we indeed need a + * reference of a pointer, as we + * want to change the pointer + * variable which we are given. + */ + void swap (T *&tt); + + /** + * Return an estimate of the + * amount of memory (in bytes) + * used by this class. Note in + * particular, that this only + * includes the amount of memory + * used by this object, not + * by the object pointed to. + */ + std::size_t memory_consumption () const; + +private: + /** + * Pointer to the object we want + * to subscribt to. Since it is + * often necessary to follow this + * pointer when debugging, we + * have deliberately chosen a + * short name. + */ + T *t; + /** + * The identification for the + * subscriptor. + */ + const char *const id; }; @@ -255,8 +255,8 @@ class SmartPointer template inline SmartPointer::SmartPointer () - : - t (0), id(typeid(P).name()) + : + t (0), id(typeid(P).name()) {} @@ -264,8 +264,8 @@ SmartPointer::SmartPointer () template inline SmartPointer::SmartPointer (T *t) - : - t (t), id(typeid(P).name()) + : + t (t), id(typeid(P).name()) { if (t != 0) t->subscribe(id); @@ -275,9 +275,9 @@ SmartPointer::SmartPointer (T *t) template inline -SmartPointer::SmartPointer (T *t, const char* id) - : - t (t), id(id) +SmartPointer::SmartPointer (T *t, const char *id) + : + t (t), id(id) { if (t != 0) t->subscribe(id); @@ -289,8 +289,8 @@ template template inline SmartPointer::SmartPointer (const SmartPointer &tt) - : - t (tt.t), id(tt.id) + : + t (tt.t), id(tt.id) { if (t != 0) t->subscribe(id); @@ -301,8 +301,8 @@ SmartPointer::SmartPointer (const SmartPointer &tt) template inline SmartPointer::SmartPointer (const SmartPointer &tt) - : - t (tt.t), id(tt.id) + : + t (tt.t), id(tt.id) { if (t != 0) t->subscribe(id); @@ -337,10 +337,10 @@ SmartPointer::clear () template inline -SmartPointer & SmartPointer::operator = (T *tt) +SmartPointer &SmartPointer::operator = (T *tt) { - // optimize if no real action is - // requested + // optimize if no real action is + // requested if (t == tt) return *this; @@ -358,17 +358,17 @@ template template inline SmartPointer & -SmartPointer::operator = (const SmartPointer& tt) +SmartPointer::operator = (const SmartPointer &tt) { - // if objects on the left and right - // hand side of the operator= are - // the same, then this is a no-op + // if objects on the left and right + // hand side of the operator= are + // the same, then this is a no-op if (&tt == this) return *this; if (t != 0) t->unsubscribe(id); - t = static_cast(tt); + t = static_cast(tt); if (tt != 0) tt->subscribe(id); return *this; @@ -379,17 +379,17 @@ SmartPointer::operator = (const SmartPointer& tt) template inline SmartPointer & -SmartPointer::operator = (const SmartPointer& tt) +SmartPointer::operator = (const SmartPointer &tt) { - // if objects on the left and right - // hand side of the operator= are - // the same, then this is a no-op + // if objects on the left and right + // hand side of the operator= are + // the same, then this is a no-op if (&tt == this) return *this; if (t != 0) t->unsubscribe(id); - t = static_cast(tt); + t = static_cast(tt); if (tt != 0) tt->subscribe(id); return *this; @@ -399,7 +399,7 @@ SmartPointer::operator = (const SmartPointer& tt) template inline -SmartPointer::operator T* () const +SmartPointer::operator T *() const { return t; } @@ -408,7 +408,7 @@ SmartPointer::operator T* () const template inline -T & SmartPointer::operator * () const +T &SmartPointer::operator * () const { Assert(t != 0, ExcNotInitialized()); return *t; @@ -418,7 +418,7 @@ T & SmartPointer::operator * () const template inline -T * SmartPointer::operator -> () const +T *SmartPointer::operator -> () const { Assert(t != 0, ExcNotInitialized()); return t; diff --git a/deal.II/include/deal.II/base/subscriptor.h b/deal.II/include/deal.II/base/subscriptor.h index 3b3fecd6bb..257347cdaa 100644 --- a/deal.II/include/deal.II/base/subscriptor.h +++ b/deal.II/include/deal.II/base/subscriptor.h @@ -51,207 +51,207 @@ DEAL_II_NAMESPACE_OPEN */ class Subscriptor { - public: - /** - * Constructor setting the counter to zero. - */ - Subscriptor(); - - /** - * Copy-constructor. - * - * The counter of the copy is zero, - * since references point to the - * original object. - */ - Subscriptor(const Subscriptor&); - - /** - * Destructor, asserting that the counter - * is zero. - */ - virtual ~Subscriptor(); - - /** - * Assignment operator. - * - * This has to be handled with - * care, too, because the counter - * has to remain the same. It therefore - * does nothing more than returning - * *this. - */ - Subscriptor& operator = (const Subscriptor&); - - /** - * Subscribes a user of the - * object. The subscriber may be - * identified by text supplied as - * identifier. - */ - void subscribe (const char* identifier = 0) const; - - /** - * Unsubscribes a user from the - * object. - * - * @note The identifier - * must be the same - * pointer as the one - * supplied to subscribe(), not - * just the same text. - */ - void unsubscribe (const char* identifier = 0) const; - - /** - * Return the present number of - * subscriptions to this object. - * This allows to use this class - * for reference counted lifetime - * determination where the last one - * to unsubscribe also deletes the - * object. - */ - unsigned int n_subscriptions () const; - - /** - * List the subscribers to @p deallog. - */ - void list_subscribers () const; - - /** @addtogroup Exceptions - * @{ */ - - /** - * Exception: - * Object may not be deleted, since - * it is used. - */ - DeclException3(ExcInUse, - int, char*, std::string &, - << "Object of class " << arg2 - << " is still used by " << arg1 << " other objects." - << arg3); - - /** - * A subscriber with the - * identification string given to - * Subscriptor::unsubscribe() did - * not subscribe to the object. - */ - DeclException2(ExcNoSubscriber, char*, char*, - << "No subscriber with identifier \"" << arg2 - << "\" did subscribe to this object of class " << arg1); - //@} - - /** - * Read or write the data of this - * object to or from a stream for - * the purpose of serialization. - * - * This function does not - * actually serialize any of the - * member variables of this - * class. The reason is that what - * this class stores is only who - * subscribes to this object, but - * who does so at the time of - * storing the contents of this - * object does not necessarily - * have anything to do with who - * subscribes to the object when - * it is restored. Consequently, - * we do not want to overwrite - * the subscribers at the time of - * restoring, and then there is - * no reason to write the - * subscribers out in the first - * place. - */ - template - void serialize(Archive & ar, const unsigned int version); - - private: - /** - * Register a subscriber for - * debugging purposes. Called by - * subscribe() in debug mode. - */ - void do_subscribe(const char* id) const; - - /** - * Deregister a subscriber for - * debugging purposes. Called by - * unsubscribe() in debug mode. - */ - void do_unsubscribe(const char* id) const; - - /** - * The data type used in - * #counter_map. - */ - typedef std::map::value_type - map_value_type; - - /** - * The iterator type used in - * #counter_map. - */ - typedef std::map::iterator - map_iterator; - - /** - * Store the number of objects - * which subscribed to this - * object. Initialally, this - * number is zero, and upon - * destruction it shall be zero - * again (i.e. all objects which - * subscribed should have - * unsubscribed again). - * - * The creator (and owner) of an - * object is counted in the map - * below if HE manages to supply - * identification. - * - * We use the mutable keyword - * in order to allow subscription - * to constant objects also. - * - * In multithreaded mode, this - * counter may be modified by - * different threads. We thus - * have to mark it - * volatile. However, this is - * counter-productive in non-MT - * mode since it may pessimize - * code. So use the macro - * defined above to selectively - * add volatility. - */ - mutable DEAL_VOLATILE unsigned int counter; - - /** - * In this map, we count - * subscriptions for each - * different identification string - * supplied to subscribe(). - */ - mutable std::map counter_map; - - /** - * Pointer to the typeinfo object - * of this object, from which we - * can later deduce the class - * name. Since this information - * on the derived class is - * neither available in the - * destructor, nor in the - * constructor, we obtain it in - * between and store it here. - */ - mutable const std::type_info * object_info; +public: + /** + * Constructor setting the counter to zero. + */ + Subscriptor(); + + /** + * Copy-constructor. + * + * The counter of the copy is zero, + * since references point to the + * original object. + */ + Subscriptor(const Subscriptor &); + + /** + * Destructor, asserting that the counter + * is zero. + */ + virtual ~Subscriptor(); + + /** + * Assignment operator. + * + * This has to be handled with + * care, too, because the counter + * has to remain the same. It therefore + * does nothing more than returning + * *this. + */ + Subscriptor &operator = (const Subscriptor &); + + /** + * Subscribes a user of the + * object. The subscriber may be + * identified by text supplied as + * identifier. + */ + void subscribe (const char *identifier = 0) const; + + /** + * Unsubscribes a user from the + * object. + * + * @note The identifier + * must be the same + * pointer as the one + * supplied to subscribe(), not + * just the same text. + */ + void unsubscribe (const char *identifier = 0) const; + + /** + * Return the present number of + * subscriptions to this object. + * This allows to use this class + * for reference counted lifetime + * determination where the last one + * to unsubscribe also deletes the + * object. + */ + unsigned int n_subscriptions () const; + + /** + * List the subscribers to @p deallog. + */ + void list_subscribers () const; + + /** @addtogroup Exceptions + * @{ */ + + /** + * Exception: + * Object may not be deleted, since + * it is used. + */ + DeclException3(ExcInUse, + int, char *, std::string &, + << "Object of class " << arg2 + << " is still used by " << arg1 << " other objects." + << arg3); + + /** + * A subscriber with the + * identification string given to + * Subscriptor::unsubscribe() did + * not subscribe to the object. + */ + DeclException2(ExcNoSubscriber, char *, char *, + << "No subscriber with identifier \"" << arg2 + << "\" did subscribe to this object of class " << arg1); + //@} + + /** + * Read or write the data of this + * object to or from a stream for + * the purpose of serialization. + * + * This function does not + * actually serialize any of the + * member variables of this + * class. The reason is that what + * this class stores is only who + * subscribes to this object, but + * who does so at the time of + * storing the contents of this + * object does not necessarily + * have anything to do with who + * subscribes to the object when + * it is restored. Consequently, + * we do not want to overwrite + * the subscribers at the time of + * restoring, and then there is + * no reason to write the + * subscribers out in the first + * place. + */ + template + void serialize(Archive &ar, const unsigned int version); + +private: + /** + * Register a subscriber for + * debugging purposes. Called by + * subscribe() in debug mode. + */ + void do_subscribe(const char *id) const; + + /** + * Deregister a subscriber for + * debugging purposes. Called by + * unsubscribe() in debug mode. + */ + void do_unsubscribe(const char *id) const; + + /** + * The data type used in + * #counter_map. + */ + typedef std::map::value_type + map_value_type; + + /** + * The iterator type used in + * #counter_map. + */ + typedef std::map::iterator + map_iterator; + + /** + * Store the number of objects + * which subscribed to this + * object. Initialally, this + * number is zero, and upon + * destruction it shall be zero + * again (i.e. all objects which + * subscribed should have + * unsubscribed again). + * + * The creator (and owner) of an + * object is counted in the map + * below if HE manages to supply + * identification. + * + * We use the mutable keyword + * in order to allow subscription + * to constant objects also. + * + * In multithreaded mode, this + * counter may be modified by + * different threads. We thus + * have to mark it + * volatile. However, this is + * counter-productive in non-MT + * mode since it may pessimize + * code. So use the macro + * defined above to selectively + * add volatility. + */ + mutable DEAL_VOLATILE unsigned int counter; + + /** + * In this map, we count + * subscriptions for each + * different identification string + * supplied to subscribe(). + */ + mutable std::map counter_map; + + /** + * Pointer to the typeinfo object + * of this object, from which we + * can later deduce the class + * name. Since this information + * on the derived class is + * neither available in the + * destructor, nor in the + * constructor, we obtain it in + * between and store it here. + */ + mutable const std::type_info *object_info; }; //--------------------------------------------------------------------------- @@ -262,8 +262,8 @@ void Subscriptor::serialize(Archive &, const unsigned int) { - // do nothing, as explained in the - // documentation of this function + // do nothing, as explained in the + // documentation of this function } // If we are in optimized mode, the subscription checking is turned @@ -274,14 +274,14 @@ Subscriptor::serialize(Archive &, #ifdef DEBUG inline void -Subscriptor::subscribe(const char* id) const +Subscriptor::subscribe(const char *id) const { do_subscribe(id); } inline void -Subscriptor::unsubscribe(const char* id) const +Subscriptor::unsubscribe(const char *id) const { do_unsubscribe(id); } @@ -289,12 +289,12 @@ Subscriptor::unsubscribe(const char* id) const #else inline void -Subscriptor::subscribe(const char*) const +Subscriptor::subscribe(const char *) const {} inline void -Subscriptor::unsubscribe(const char*) const +Subscriptor::unsubscribe(const char *) const {} #endif diff --git a/deal.II/include/deal.II/base/symmetric_tensor.h b/deal.II/include/deal.II/base/symmetric_tensor.h index a190398a65..6f1d47d626 100644 --- a/deal.II/include/deal.II/base/symmetric_tensor.h +++ b/deal.II/include/deal.II/base/symmetric_tensor.h @@ -40,24 +40,24 @@ determinant (const SymmetricTensor<2,dim,Number> &); namespace internal { - /** - * A namespace for classes that are - * internal to how the SymmetricTensor - * class works. - */ + /** + * A namespace for classes that are + * internal to how the SymmetricTensor + * class works. + */ namespace SymmetricTensorAccessors { - /** - * Create a TableIndices<2> - * object where the first entries - * up to position-1 are - * taken from previous_indices, - * and new_index is put at - * position - * position. The - * remaining indices remain in - * invalid state. - */ + /** + * Create a TableIndices<2> + * object where the first entries + * up to position-1 are + * taken from previous_indices, + * and new_index is put at + * position + * position. The + * remaining indices remain in + * invalid state. + */ inline TableIndices<2> merge (const TableIndices<2> &previous_indices, const unsigned int new_index, @@ -73,17 +73,17 @@ namespace internal - /** - * Create a TableIndices<4> - * object where the first entries - * up to position-1 are - * taken from previous_indices, - * and new_index is put at - * position - * position. The - * remaining indices remain in - * invalid state. - */ + /** + * Create a TableIndices<4> + * object where the first entries + * up to position-1 are + * taken from previous_indices, + * and new_index is put at + * position + * position. The + * remaining indices remain in + * invalid state. + */ inline TableIndices<4> merge (const TableIndices<4> &previous_indices, const unsigned int new_index, @@ -93,40 +93,40 @@ namespace internal switch (position) { - case 0: - return TableIndices<4>(new_index); - case 1: - return TableIndices<4>(previous_indices[0], - new_index); - case 2: - return TableIndices<4>(previous_indices[0], - previous_indices[1], - new_index); - case 3: - return TableIndices<4>(previous_indices[0], - previous_indices[1], - previous_indices[2], - new_index); + case 0: + return TableIndices<4>(new_index); + case 1: + return TableIndices<4>(previous_indices[0], + new_index); + case 2: + return TableIndices<4>(previous_indices[0], + previous_indices[1], + new_index); + case 3: + return TableIndices<4>(previous_indices[0], + previous_indices[1], + previous_indices[2], + new_index); } Assert (false, ExcInternalError()); return TableIndices<4>(); } - /** - * Typedef template magic - * denoting the result of a - * double contraction between two - * tensors or ranks rank1 and - * rank2. In general, this is a - * tensor of rank - * rank1+rank2-4, but if - * this is zero it is a single - * scalar Number. For this case, - * we have a specialization. - * - * @author Wolfgang Bangerth, 2005 - */ + /** + * Typedef template magic + * denoting the result of a + * double contraction between two + * tensors or ranks rank1 and + * rank2. In general, this is a + * tensor of rank + * rank1+rank2-4, but if + * this is zero it is a single + * scalar Number. For this case, + * we have a specialization. + * + * @author Wolfgang Bangerth, 2005 + */ template struct double_contraction_result { @@ -134,399 +134,399 @@ namespace internal }; - /** - * Typedef template magic - * denoting the result of a - * double contraction between two - * tensors or ranks rank1 and - * rank2. In general, this is a - * tensor of rank - * rank1+rank2-4, but if - * this is zero it is a single - * scalar Number. For this case, - * we have a specialization. - * - * @author Wolfgang Bangerth, 2005 - */ + /** + * Typedef template magic + * denoting the result of a + * double contraction between two + * tensors or ranks rank1 and + * rank2. In general, this is a + * tensor of rank + * rank1+rank2-4, but if + * this is zero it is a single + * scalar Number. For this case, + * we have a specialization. + * + * @author Wolfgang Bangerth, 2005 + */ template struct double_contraction_result<2,2,dim,Number> { - typedef Number type; + typedef Number type; }; - /** - * Declaration of typedefs for the type - * of data structures which are used to - * store symmetric tensors. For example, - * for rank-2 symmetric tensors, we use a - * flat vector to store all the - * elements. On the other hand, symmetric - * rank-4 tensors are mappings from - * symmetric rank-2 tensors into - * symmetric rank-2 tensors, so they can - * be represented as matrices, etc. - * - * This information is probably of little - * interest to all except the accessor - * classes that need it. In particular, - * you shouldn't make any assumptions - * about the storage format in your - * application programs. - */ + /** + * Declaration of typedefs for the type + * of data structures which are used to + * store symmetric tensors. For example, + * for rank-2 symmetric tensors, we use a + * flat vector to store all the + * elements. On the other hand, symmetric + * rank-4 tensors are mappings from + * symmetric rank-2 tensors into + * symmetric rank-2 tensors, so they can + * be represented as matrices, etc. + * + * This information is probably of little + * interest to all except the accessor + * classes that need it. In particular, + * you shouldn't make any assumptions + * about the storage format in your + * application programs. + */ template struct StorageType; - /** - * Specialization of StorageType for - * rank-2 tensors. - */ + /** + * Specialization of StorageType for + * rank-2 tensors. + */ template struct StorageType<2,dim,Number> { - /** - * Number of independent components of a - * symmetric tensor of rank 2. We store - * only the upper right half of it. - */ - static const unsigned int - n_independent_components = (dim*dim + dim)/2; - - /** - * Declare the type in which we actually - * store the data. - */ - typedef Tensor<1,n_independent_components,Number> base_tensor_type; + /** + * Number of independent components of a + * symmetric tensor of rank 2. We store + * only the upper right half of it. + */ + static const unsigned int + n_independent_components = (dim*dim + dim)/2; + + /** + * Declare the type in which we actually + * store the data. + */ + typedef Tensor<1,n_independent_components,Number> base_tensor_type; }; - /** - * Specialization of StorageType for - * rank-4 tensors. - */ + /** + * Specialization of StorageType for + * rank-4 tensors. + */ template struct StorageType<4,dim,Number> { - /** - * Number of independent components - * of a symmetric tensor of rank - * 2. Since rank-4 tensors are - * mappings between such objects, we - * need this information. - */ - static const unsigned int - n_rank2_components = (dim*dim + dim)/2; - - /** - * Number of independent components of a - * symmetric tensor of rank 4. - */ - static const unsigned int - n_independent_components = (n_rank2_components * - StorageType<2,dim,Number>::n_independent_components); - - /** - * Declare the type in which we - * actually store the data. Symmetric - * rank-4 tensors are mappings - * between symmetric rank-2 tensors, - * so we can represent the data as a - * matrix if we represent the rank-2 - * tensors as vectors. - */ - typedef Tensor<2,n_rank2_components,Number> base_tensor_type; + /** + * Number of independent components + * of a symmetric tensor of rank + * 2. Since rank-4 tensors are + * mappings between such objects, we + * need this information. + */ + static const unsigned int + n_rank2_components = (dim*dim + dim)/2; + + /** + * Number of independent components of a + * symmetric tensor of rank 4. + */ + static const unsigned int + n_independent_components = (n_rank2_components * + StorageType<2,dim,Number>::n_independent_components); + + /** + * Declare the type in which we + * actually store the data. Symmetric + * rank-4 tensors are mappings + * between symmetric rank-2 tensors, + * so we can represent the data as a + * matrix if we represent the rank-2 + * tensors as vectors. + */ + typedef Tensor<2,n_rank2_components,Number> base_tensor_type; }; - /** - * Switch type to select a tensor of - * rank 2 and dimension dim, - * switching on whether the tensor - * should be constant or not. - */ + /** + * Switch type to select a tensor of + * rank 2 and dimension dim, + * switching on whether the tensor + * should be constant or not. + */ template struct AccessorTypes; - /** - * Switch type to select a tensor of - * rank 2 and dimension dim, - * switching on whether the tensor - * should be constant or not. - * - * Specialization for constant tensors. - */ + /** + * Switch type to select a tensor of + * rank 2 and dimension dim, + * switching on whether the tensor + * should be constant or not. + * + * Specialization for constant tensors. + */ template struct AccessorTypes { - typedef const ::dealii::SymmetricTensor tensor_type; + typedef const ::dealii::SymmetricTensor tensor_type; - typedef Number reference; + typedef Number reference; }; - /** - * Switch type to select a tensor of - * rank 2 and dimension dim, - * switching on whether the tensor - * should be constant or not. - * - * Specialization for non-constant - * tensors. - */ + /** + * Switch type to select a tensor of + * rank 2 and dimension dim, + * switching on whether the tensor + * should be constant or not. + * + * Specialization for non-constant + * tensors. + */ template struct AccessorTypes { - typedef ::dealii::SymmetricTensor tensor_type; + typedef ::dealii::SymmetricTensor tensor_type; - typedef Number &reference; + typedef Number &reference; }; -/** - * @internal - * - * Class that acts as accessor to elements of type - * SymmetricTensor. The template parameter C may be either - * true or false, and indicates whether the objects worked on are - * constant or not (i.e. write access is only allowed if the value is - * false). - * - * Since with N indices, the effect of applying - * operator[] is getting access to something we N-1 - * indices, we have to implement these accessor classes recursively, - * with stopping when we have only one index left. For the latter - * case, a specialization of this class is declared below, where - * calling operator[] gives you access to the objects - * actually stored by the tensor; the tensor class also makes sure - * that only those elements are actually accessed which we actually - * store, i.e. it reorders indices if necessary. The template - * parameter P indicates how many remaining indices there - * are. For a rank-2 tensor, P may be two, and when using - * operator[], an object with P=1 emerges. - * - * As stated for the entire namespace, you will not usually have to do - * with these classes directly, and should not try to use their - * interface directly as it may change without notice. In fact, since - * the constructors are made private, you will not even be able to - * generate objects of this class, as they are only thought as - * temporaries for access to elements of the table class, not for - * passing them around as arguments of functions, etc. - * - * This class is an adaptation of a similar class used for the Table - * class. - * - * @author Wolfgang Bangerth, 2002, 2005 - */ + /** + * @internal + * + * Class that acts as accessor to elements of type + * SymmetricTensor. The template parameter C may be either + * true or false, and indicates whether the objects worked on are + * constant or not (i.e. write access is only allowed if the value is + * false). + * + * Since with N indices, the effect of applying + * operator[] is getting access to something we N-1 + * indices, we have to implement these accessor classes recursively, + * with stopping when we have only one index left. For the latter + * case, a specialization of this class is declared below, where + * calling operator[] gives you access to the objects + * actually stored by the tensor; the tensor class also makes sure + * that only those elements are actually accessed which we actually + * store, i.e. it reorders indices if necessary. The template + * parameter P indicates how many remaining indices there + * are. For a rank-2 tensor, P may be two, and when using + * operator[], an object with P=1 emerges. + * + * As stated for the entire namespace, you will not usually have to do + * with these classes directly, and should not try to use their + * interface directly as it may change without notice. In fact, since + * the constructors are made private, you will not even be able to + * generate objects of this class, as they are only thought as + * temporaries for access to elements of the table class, not for + * passing them around as arguments of functions, etc. + * + * This class is an adaptation of a similar class used for the Table + * class. + * + * @author Wolfgang Bangerth, 2002, 2005 + */ template class Accessor { - public: - /** - * Import two typedefs from the - * switch class above. - */ - typedef typename AccessorTypes::reference reference; - typedef typename AccessorTypes::tensor_type tensor_type; - - private: - /** - * Constructor. Take a - * reference to the tensor - * object which we will - * access. - * - * The second argument - * denotes the values of - * previous indices into the - * tensor. For example, for a - * rank-4 tensor, if P=2, - * then we will already have - * had two successive element - * selections (e.g. through - * tensor[1][2]), - * and the two index values - * have to be stored - * somewhere. This class - * therefore only makes use - * of the first rank-P - * elements of this array, - * but passes it on to the - * next level with P-1 which - * fills the next entry, and - * so on. - * - * The constructor is made - * private in order to prevent - * you having such objects - * around. The only way to - * create such objects is via - * the Table class, which - * only generates them as - * temporary objects. This - * guarantees that the accessor - * objects go out of scope - * earlier than the mother - * object, avoid problems with - * data consistency. - */ - Accessor (tensor_type &tensor, - const TableIndices &previous_indices); - - /** - * Default constructor. Not - * needed, and invisible, so - * private. - */ - Accessor (); - - /** - * Copy constructor. Not - * needed, and invisible, so - * private. - */ - Accessor (const Accessor &a); - - public: - - /** - * Index operator. - */ - Accessor operator [] (const unsigned int i); - - private: - /** - * Store the data given to the - * constructor. - */ - tensor_type &tensor; - const TableIndices previous_indices; - - // declare some other classes - // as friends. make sure to - // work around bugs in some - // compilers - template friend class SymmetricTensor; - template - friend class Accessor; + public: + /** + * Import two typedefs from the + * switch class above. + */ + typedef typename AccessorTypes::reference reference; + typedef typename AccessorTypes::tensor_type tensor_type; + + private: + /** + * Constructor. Take a + * reference to the tensor + * object which we will + * access. + * + * The second argument + * denotes the values of + * previous indices into the + * tensor. For example, for a + * rank-4 tensor, if P=2, + * then we will already have + * had two successive element + * selections (e.g. through + * tensor[1][2]), + * and the two index values + * have to be stored + * somewhere. This class + * therefore only makes use + * of the first rank-P + * elements of this array, + * but passes it on to the + * next level with P-1 which + * fills the next entry, and + * so on. + * + * The constructor is made + * private in order to prevent + * you having such objects + * around. The only way to + * create such objects is via + * the Table class, which + * only generates them as + * temporary objects. This + * guarantees that the accessor + * objects go out of scope + * earlier than the mother + * object, avoid problems with + * data consistency. + */ + Accessor (tensor_type &tensor, + const TableIndices &previous_indices); + + /** + * Default constructor. Not + * needed, and invisible, so + * private. + */ + Accessor (); + + /** + * Copy constructor. Not + * needed, and invisible, so + * private. + */ + Accessor (const Accessor &a); + + public: + + /** + * Index operator. + */ + Accessor operator [] (const unsigned int i); + + private: + /** + * Store the data given to the + * constructor. + */ + tensor_type &tensor; + const TableIndices previous_indices; + + // declare some other classes + // as friends. make sure to + // work around bugs in some + // compilers + template friend class SymmetricTensor; + template + friend class Accessor; # ifndef DEAL_II_TEMPL_SPEC_FRIEND_BUG - friend class ::dealii::SymmetricTensor; - friend class Accessor; + friend class ::dealii::SymmetricTensor; + friend class Accessor; # endif }; -/** - * @internal - * Accessor class for SymmetricTensor. This is the specialization for the last - * index, which actually allows access to the elements of the table, - * rather than recursively returning access objects for further - * subsets. The same holds for this specialization as for the general - * template; see there for more information. - * - * @author Wolfgang Bangerth, 2002, 2005 - */ + /** + * @internal + * Accessor class for SymmetricTensor. This is the specialization for the last + * index, which actually allows access to the elements of the table, + * rather than recursively returning access objects for further + * subsets. The same holds for this specialization as for the general + * template; see there for more information. + * + * @author Wolfgang Bangerth, 2002, 2005 + */ template class Accessor { - public: - /** - * Import two typedefs from the - * switch class above. - */ - typedef typename AccessorTypes::reference reference; - typedef typename AccessorTypes::tensor_type tensor_type; - - private: - /** - * Constructor. Take a - * reference to the tensor - * object which we will - * access. - * - * The second argument - * denotes the values of - * previous indices into the - * tensor. For example, for a - * rank-4 tensor, if P=2, - * then we will already have - * had two successive element - * selections (e.g. through - * tensor[1][2]), - * and the two index values - * have to be stored - * somewhere. This class - * therefore only makes use - * of the first rank-P - * elements of this array, - * but passes it on to the - * next level with P-1 which - * fills the next entry, and - * so on. - * - * For this particular - * specialization, i.e. for - * P==1, all but the last - * index are already filled. - * - * The constructor is made - * private in order to prevent - * you having such objects - * around. The only way to - * create such objects is via - * the Table class, which - * only generates them as - * temporary objects. This - * guarantees that the accessor - * objects go out of scope - * earlier than the mother - * object, avoid problems with - * data consistency. - */ - Accessor (tensor_type &tensor, - const TableIndices &previous_indices); - - /** - * Default constructor. Not - * needed, and invisible, so - * private. - */ - Accessor (); - - /** - * Copy constructor. Not - * needed, and invisible, so - * private. - */ - Accessor (const Accessor &a); - - public: - - /** - * Index operator. - */ - reference operator [] (const unsigned int); - - private: - /** - * Store the data given to the - * constructor. - */ - tensor_type &tensor; - const TableIndices previous_indices; - - // declare some other classes - // as friends. make sure to - // work around bugs in some - // compilers - template friend class SymmetricTensor; - template - friend class SymmetricTensorAccessors::Accessor; + public: + /** + * Import two typedefs from the + * switch class above. + */ + typedef typename AccessorTypes::reference reference; + typedef typename AccessorTypes::tensor_type tensor_type; + + private: + /** + * Constructor. Take a + * reference to the tensor + * object which we will + * access. + * + * The second argument + * denotes the values of + * previous indices into the + * tensor. For example, for a + * rank-4 tensor, if P=2, + * then we will already have + * had two successive element + * selections (e.g. through + * tensor[1][2]), + * and the two index values + * have to be stored + * somewhere. This class + * therefore only makes use + * of the first rank-P + * elements of this array, + * but passes it on to the + * next level with P-1 which + * fills the next entry, and + * so on. + * + * For this particular + * specialization, i.e. for + * P==1, all but the last + * index are already filled. + * + * The constructor is made + * private in order to prevent + * you having such objects + * around. The only way to + * create such objects is via + * the Table class, which + * only generates them as + * temporary objects. This + * guarantees that the accessor + * objects go out of scope + * earlier than the mother + * object, avoid problems with + * data consistency. + */ + Accessor (tensor_type &tensor, + const TableIndices &previous_indices); + + /** + * Default constructor. Not + * needed, and invisible, so + * private. + */ + Accessor (); + + /** + * Copy constructor. Not + * needed, and invisible, so + * private. + */ + Accessor (const Accessor &a); + + public: + + /** + * Index operator. + */ + reference operator [] (const unsigned int); + + private: + /** + * Store the data given to the + * constructor. + */ + tensor_type &tensor; + const TableIndices previous_indices; + + // declare some other classes + // as friends. make sure to + // work around bugs in some + // compilers + template friend class SymmetricTensor; + template + friend class SymmetricTensorAccessors::Accessor; # ifndef DEAL_II_TEMPL_SPEC_FRIEND_BUG - friend class ::dealii::SymmetricTensor; - friend class SymmetricTensorAccessors::Accessor; + friend class ::dealii::SymmetricTensor; + friend class SymmetricTensorAccessors::Accessor; # endif }; } @@ -603,447 +603,447 @@ namespace internal template class SymmetricTensor { - public: - /** - * Provide a way to get the - * dimension of an object without - * explicit knowledge of it's - * data type. Implementation is - * this way instead of providing - * a function dimension() - * because now it is possible to - * get the dimension at compile - * time without the expansion and - * preevaluation of an inlined - * function; the compiler may - * therefore produce more - * efficient code and you may use - * this value to declare other - * data types. - */ - static const unsigned int dimension = dim; - - /** - * An integer denoting the number of - * independent components that fully - * describe a symmetric tensor. In $d$ - * space dimensions, this number equals - * $\frac 12 (d^2+d)$ for symmetric - * tensors of rank 2. - */ - static const unsigned int n_independent_components +public: + /** + * Provide a way to get the + * dimension of an object without + * explicit knowledge of it's + * data type. Implementation is + * this way instead of providing + * a function dimension() + * because now it is possible to + * get the dimension at compile + * time without the expansion and + * preevaluation of an inlined + * function; the compiler may + * therefore produce more + * efficient code and you may use + * this value to declare other + * data types. + */ + static const unsigned int dimension = dim; + + /** + * An integer denoting the number of + * independent components that fully + * describe a symmetric tensor. In $d$ + * space dimensions, this number equals + * $\frac 12 (d^2+d)$ for symmetric + * tensors of rank 2. + */ + static const unsigned int n_independent_components = internal::SymmetricTensorAccessors::StorageType:: n_independent_components; - /** - * Default constructor. Creates a zero - * tensor. - */ - SymmetricTensor (); - - /** - * Constructor. Generate a symmetric - * tensor from a general one. Assumes - * that @p t is already symmetric, and in - * debug mode this is in fact - * checked. Note that no provision is - * made to assure that the tensor is - * symmetric only up to round-off error: - * if the incoming tensor is not exactly - * symmetric, then an exception is - * thrown. If you know that incoming - * tensor is symmetric only up to - * round-off, then you may want to call - * the symmetrize function - * first. If you aren't sure, it is good - * practice to check before calling - * symmetrize. - */ - SymmetricTensor (const Tensor<2,dim,Number> &t); - - /** - * A constructor that creates a - * symmetric tensor from an array - * holding its independent - * elements. Using this - * constructor assumes that the - * caller knows the order in - * which elements are stored in - * symmetric tensors; its use is - * therefore discouraged, but if - * you think you want to use it - * anyway you can query the order - * of elements using the - * unrolled_index() function. - * - * This constructor is currently only - * implemented for symmetric tensors of - * rank 2. - * - * The size of the array passed - * is equal to - * SymmetricTensor::n_independent_component; - * the reason for using the - * object from the internal - * namespace is to work around - * bugs in some older compilers. - */ - SymmetricTensor (const Number (&array) [n_independent_components]); - - /** - * Assignment operator. - */ - SymmetricTensor & operator = (const SymmetricTensor &); - - /** - * This operator assigns a scalar - * to a tensor. To avoid - * confusion with what exactly it - * means to assign a scalar value - * to a tensor, zero is the only - * value allowed for d, - * allowing the intuitive - * notation t=0 to reset - * all elements of the tensor to - * zero. - */ - SymmetricTensor & operator = (const Number d); - - /** - * Convert the present symmetric tensor - * into a full tensor with the same - * elements, but using the different - * storage scheme of full tensors. - */ - operator Tensor () const; - - /** - * Test for equality of two tensors. - */ - bool operator == (const SymmetricTensor &) const; - - /** - * Test for inequality of two tensors. - */ - bool operator != (const SymmetricTensor &) const; - - /** - * Add another tensor. - */ - SymmetricTensor & operator += (const SymmetricTensor &); - - /** - * Subtract another tensor. - */ - SymmetricTensor & operator -= (const SymmetricTensor &); - - /** - * Scale the tensor by factor, - * i.e. multiply all components by - * factor. - */ - SymmetricTensor & operator *= (const Number factor); - - /** - * Scale the vector by - * 1/factor. - */ - SymmetricTensor & operator /= (const Number factor); - - /** - * Add two tensors. If possible, you - * should use operator += - * instead since this does not need the - * creation of a temporary. - */ - SymmetricTensor operator + (const SymmetricTensor &s) const; - - /** - * Subtract two tensors. If possible, - * you should use operator -= - * instead since this does not need the - * creation of a temporary. - */ - SymmetricTensor operator - (const SymmetricTensor &s) const; - - /** - * Unary minus operator. Negate all - * entries of a tensor. - */ - SymmetricTensor operator - () const; - - /** - * Product between the present - * symmetric tensor and a tensor - * of rank 2. For example, if the - * present object is also a - * rank-2 tensor, then this is - * the scalar-product double - * contraction - * aijbij - * over all indices - * i,j. In this case, - * the return value evaluates to - * a single scalar. While it is - * possible to define other - * scalar product (and associated - * induced norms), this one seems - * to be the most appropriate - * one. - * - * If the present object is a - * rank-4 tensor, the the result - * is a rank-2 tensor, the - * operation contracts over the - * last two indices of the - * present object and the indices - * of the argument, and the - * result is a tensor of rank 2. - * - * Note that the multiplication - * operator for symmetrict - * tensors is defined to be a - * double contraction over two - * indices, while it is defined - * as a single contraction over - * only one index for regular - * Tensor objects. For - * symmetric tensors it therefore - * acts in a way that is commonly - * denoted by a "colon - * multiplication" in the - * mathematica literature. - * - * There are global functions - * double_contract that - * do the same work as this - * operator, but rather than - * returning the result as a - * return value, they write it - * into the first argument to the - * function. - */ + /** + * Default constructor. Creates a zero + * tensor. + */ + SymmetricTensor (); + + /** + * Constructor. Generate a symmetric + * tensor from a general one. Assumes + * that @p t is already symmetric, and in + * debug mode this is in fact + * checked. Note that no provision is + * made to assure that the tensor is + * symmetric only up to round-off error: + * if the incoming tensor is not exactly + * symmetric, then an exception is + * thrown. If you know that incoming + * tensor is symmetric only up to + * round-off, then you may want to call + * the symmetrize function + * first. If you aren't sure, it is good + * practice to check before calling + * symmetrize. + */ + SymmetricTensor (const Tensor<2,dim,Number> &t); + + /** + * A constructor that creates a + * symmetric tensor from an array + * holding its independent + * elements. Using this + * constructor assumes that the + * caller knows the order in + * which elements are stored in + * symmetric tensors; its use is + * therefore discouraged, but if + * you think you want to use it + * anyway you can query the order + * of elements using the + * unrolled_index() function. + * + * This constructor is currently only + * implemented for symmetric tensors of + * rank 2. + * + * The size of the array passed + * is equal to + * SymmetricTensor::n_independent_component; + * the reason for using the + * object from the internal + * namespace is to work around + * bugs in some older compilers. + */ + SymmetricTensor (const Number (&array) [n_independent_components]); + + /** + * Assignment operator. + */ + SymmetricTensor &operator = (const SymmetricTensor &); + + /** + * This operator assigns a scalar + * to a tensor. To avoid + * confusion with what exactly it + * means to assign a scalar value + * to a tensor, zero is the only + * value allowed for d, + * allowing the intuitive + * notation t=0 to reset + * all elements of the tensor to + * zero. + */ + SymmetricTensor &operator = (const Number d); + + /** + * Convert the present symmetric tensor + * into a full tensor with the same + * elements, but using the different + * storage scheme of full tensors. + */ + operator Tensor () const; + + /** + * Test for equality of two tensors. + */ + bool operator == (const SymmetricTensor &) const; + + /** + * Test for inequality of two tensors. + */ + bool operator != (const SymmetricTensor &) const; + + /** + * Add another tensor. + */ + SymmetricTensor &operator += (const SymmetricTensor &); + + /** + * Subtract another tensor. + */ + SymmetricTensor &operator -= (const SymmetricTensor &); + + /** + * Scale the tensor by factor, + * i.e. multiply all components by + * factor. + */ + SymmetricTensor &operator *= (const Number factor); + + /** + * Scale the vector by + * 1/factor. + */ + SymmetricTensor &operator /= (const Number factor); + + /** + * Add two tensors. If possible, you + * should use operator += + * instead since this does not need the + * creation of a temporary. + */ + SymmetricTensor operator + (const SymmetricTensor &s) const; + + /** + * Subtract two tensors. If possible, + * you should use operator -= + * instead since this does not need the + * creation of a temporary. + */ + SymmetricTensor operator - (const SymmetricTensor &s) const; + + /** + * Unary minus operator. Negate all + * entries of a tensor. + */ + SymmetricTensor operator - () const; + + /** + * Product between the present + * symmetric tensor and a tensor + * of rank 2. For example, if the + * present object is also a + * rank-2 tensor, then this is + * the scalar-product double + * contraction + * aijbij + * over all indices + * i,j. In this case, + * the return value evaluates to + * a single scalar. While it is + * possible to define other + * scalar product (and associated + * induced norms), this one seems + * to be the most appropriate + * one. + * + * If the present object is a + * rank-4 tensor, the the result + * is a rank-2 tensor, the + * operation contracts over the + * last two indices of the + * present object and the indices + * of the argument, and the + * result is a tensor of rank 2. + * + * Note that the multiplication + * operator for symmetrict + * tensors is defined to be a + * double contraction over two + * indices, while it is defined + * as a single contraction over + * only one index for regular + * Tensor objects. For + * symmetric tensors it therefore + * acts in a way that is commonly + * denoted by a "colon + * multiplication" in the + * mathematica literature. + * + * There are global functions + * double_contract that + * do the same work as this + * operator, but rather than + * returning the result as a + * return value, they write it + * into the first argument to the + * function. + */ typename internal::SymmetricTensorAccessors::double_contraction_result::type operator * (const SymmetricTensor<2,dim,Number> &s) const; - /** - * Contraction over two indices - * of the present object with the - * rank-4 symmetric tensor given - * as argument. - */ - typename internal::SymmetricTensorAccessors::double_contraction_result::type - operator * (const SymmetricTensor<4,dim,Number> &s) const; - - /** - * Return a read-write reference - * to the indicated element. - */ - Number & operator() (const TableIndices &indices); - - /** - * Return the value of the - * indicated element as a - * read-only reference. - * - * We return the requested value - * as a constant reference rather - * than by value since this - * object may hold data types - * that may be large, and we - * don't know here whether - * copying is expensive or not. - */ - Number operator() (const TableIndices &indices) const; - - /** - * Access the elements of a row of this - * symmetric tensor. This function is - * called for constant tensors. - */ - internal::SymmetricTensorAccessors::Accessor - operator [] (const unsigned int row) const; - - /** - * Access the elements of a row of this - * symmetric tensor. This function is - * called for non-constant tensors. - */ - internal::SymmetricTensorAccessors::Accessor - operator [] (const unsigned int row); - - /** - * Access to an element where you - * specify the entire set of - * indices. - */ - Number - operator [] (const TableIndices &indices) const; - - /** - * Access to an element where you - * specify the entire set of - * indices. - */ - Number & - operator [] (const TableIndices &indices); - - /** - * Access to an element according - * to unrolled index. The - * function - * s.access_raw_entry(i) - * does the same as - * s[s.unrolled_to_component_indices(i)], - * but more efficiently. - */ - Number - access_raw_entry (const unsigned int unrolled_index) const; - - /** - * Access to an element according - * to unrolled index. The - * function - * s.access_raw_entry(i) - * does the same as - * s[s.unrolled_to_component_indices(i)], - * but more efficiently. - */ - Number & - access_raw_entry (const unsigned int unrolled_index); - - /** - * Return the Frobenius-norm of a tensor, - * i.e. the square root of the sum of - * squares of all entries. This norm is - * induced by the scalar product defined - * above for two symmetric tensors. Note - * that it includes all entries of - * the tensor, counting symmetry, not - * only the unique ones (for example, for - * rank-2 tensors, this norm includes - * adding up the squares of upper right - * as well as lower left entries, not - * just one of them, although they are - * equal for symmetric tensors). - */ - Number norm () const; - - /** - * Tensors can be unrolled by - * simply pasting all elements - * into one long vector, but for - * this an order of elements has - * to be defined. For symmetric - * tensors, this function returns - * which index within the range - * [0,n_independent_components) - * the given entry in a symmetric - * tensor has. - */ - static - unsigned int - component_to_unrolled_index (const TableIndices &indices); - - /** - * The opposite of the previous - * function: given an index $i$ - * in the unrolled form of the - * tensor, return what set of - * indices $(k,l)$ (for rank-2 - * tensors) or $(k,l,m,n)$ (for - * rank-4 tensors) corresponds to - * it. - */ - static - TableIndices - unrolled_to_component_indices (const unsigned int i); - - /** - * Reset all values to zero. - * - * Note that this is partly inconsistent - * with the semantics of the @p clear() - * member functions of the STL and of - * several other classes within deal.II - * which not only reset the values of - * stored elements to zero, but release - * all memory and return the object into - * a virginial state. However, since the - * size of objects of the present type is - * determined by its template parameters, - * resizing is not an option, and indeed - * the state where all elements have a - * zero value is the state right after - * construction of such an object. - */ - void clear (); - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. - */ - static std::size_t memory_consumption (); - - /** - * Read or write the data of this object to or - * from a stream for the purpose of serialization - */ - template - void serialize(Archive & ar, const unsigned int version); - - private: - /** - * A structure that describes - * properties of the base tensor. - */ - typedef - internal::SymmetricTensorAccessors::StorageType - base_tensor_descriptor; - - /** - * Data storage type for a - * symmetric tensor. - */ - typedef typename base_tensor_descriptor::base_tensor_type base_tensor_type; - - /** - * The place where we store the - * data of the tensor. - */ - base_tensor_type data; - - /** - * Make all other symmetric tensors friends. - */ - template friend class SymmetricTensor; - - /** - * Make a few more functions friends. - */ - template - friend Number2 trace (const SymmetricTensor<2,dim2,Number2> &d); - - template - friend Number2 determinant (const SymmetricTensor<2,dim2,Number2> &t); - - template - friend SymmetricTensor<2,dim2,Number2> - deviator (const SymmetricTensor<2,dim2,Number2> &t); - - template - friend SymmetricTensor<2,dim2,Number2> unit_symmetric_tensor (); - - template - friend SymmetricTensor<4,dim2,Number2> deviator_tensor (); - - template - friend SymmetricTensor<4,dim2,Number2> identity_tensor (); - - template - friend SymmetricTensor<4,dim2,Number2> invert (const SymmetricTensor<4,dim2,Number2> &); + /** + * Contraction over two indices + * of the present object with the + * rank-4 symmetric tensor given + * as argument. + */ + typename internal::SymmetricTensorAccessors::double_contraction_result::type + operator * (const SymmetricTensor<4,dim,Number> &s) const; + + /** + * Return a read-write reference + * to the indicated element. + */ + Number &operator() (const TableIndices &indices); + + /** + * Return the value of the + * indicated element as a + * read-only reference. + * + * We return the requested value + * as a constant reference rather + * than by value since this + * object may hold data types + * that may be large, and we + * don't know here whether + * copying is expensive or not. + */ + Number operator() (const TableIndices &indices) const; + + /** + * Access the elements of a row of this + * symmetric tensor. This function is + * called for constant tensors. + */ + internal::SymmetricTensorAccessors::Accessor + operator [] (const unsigned int row) const; + + /** + * Access the elements of a row of this + * symmetric tensor. This function is + * called for non-constant tensors. + */ + internal::SymmetricTensorAccessors::Accessor + operator [] (const unsigned int row); + + /** + * Access to an element where you + * specify the entire set of + * indices. + */ + Number + operator [] (const TableIndices &indices) const; + + /** + * Access to an element where you + * specify the entire set of + * indices. + */ + Number & + operator [] (const TableIndices &indices); + + /** + * Access to an element according + * to unrolled index. The + * function + * s.access_raw_entry(i) + * does the same as + * s[s.unrolled_to_component_indices(i)], + * but more efficiently. + */ + Number + access_raw_entry (const unsigned int unrolled_index) const; + + /** + * Access to an element according + * to unrolled index. The + * function + * s.access_raw_entry(i) + * does the same as + * s[s.unrolled_to_component_indices(i)], + * but more efficiently. + */ + Number & + access_raw_entry (const unsigned int unrolled_index); + + /** + * Return the Frobenius-norm of a tensor, + * i.e. the square root of the sum of + * squares of all entries. This norm is + * induced by the scalar product defined + * above for two symmetric tensors. Note + * that it includes all entries of + * the tensor, counting symmetry, not + * only the unique ones (for example, for + * rank-2 tensors, this norm includes + * adding up the squares of upper right + * as well as lower left entries, not + * just one of them, although they are + * equal for symmetric tensors). + */ + Number norm () const; + + /** + * Tensors can be unrolled by + * simply pasting all elements + * into one long vector, but for + * this an order of elements has + * to be defined. For symmetric + * tensors, this function returns + * which index within the range + * [0,n_independent_components) + * the given entry in a symmetric + * tensor has. + */ + static + unsigned int + component_to_unrolled_index (const TableIndices &indices); + + /** + * The opposite of the previous + * function: given an index $i$ + * in the unrolled form of the + * tensor, return what set of + * indices $(k,l)$ (for rank-2 + * tensors) or $(k,l,m,n)$ (for + * rank-4 tensors) corresponds to + * it. + */ + static + TableIndices + unrolled_to_component_indices (const unsigned int i); + + /** + * Reset all values to zero. + * + * Note that this is partly inconsistent + * with the semantics of the @p clear() + * member functions of the STL and of + * several other classes within deal.II + * which not only reset the values of + * stored elements to zero, but release + * all memory and return the object into + * a virginial state. However, since the + * size of objects of the present type is + * determined by its template parameters, + * resizing is not an option, and indeed + * the state where all elements have a + * zero value is the state right after + * construction of such an object. + */ + void clear (); + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. + */ + static std::size_t memory_consumption (); + + /** + * Read or write the data of this object to or + * from a stream for the purpose of serialization + */ + template + void serialize(Archive &ar, const unsigned int version); + +private: + /** + * A structure that describes + * properties of the base tensor. + */ + typedef + internal::SymmetricTensorAccessors::StorageType + base_tensor_descriptor; + + /** + * Data storage type for a + * symmetric tensor. + */ + typedef typename base_tensor_descriptor::base_tensor_type base_tensor_type; + + /** + * The place where we store the + * data of the tensor. + */ + base_tensor_type data; + + /** + * Make all other symmetric tensors friends. + */ + template friend class SymmetricTensor; + + /** + * Make a few more functions friends. + */ + template + friend Number2 trace (const SymmetricTensor<2,dim2,Number2> &d); + + template + friend Number2 determinant (const SymmetricTensor<2,dim2,Number2> &t); + + template + friend SymmetricTensor<2,dim2,Number2> + deviator (const SymmetricTensor<2,dim2,Number2> &t); + + template + friend SymmetricTensor<2,dim2,Number2> unit_symmetric_tensor (); + + template + friend SymmetricTensor<4,dim2,Number2> deviator_tensor (); + + template + friend SymmetricTensor<4,dim2,Number2> identity_tensor (); + + template + friend SymmetricTensor<4,dim2,Number2> invert (const SymmetricTensor<4,dim2,Number2> &); }; @@ -1060,9 +1060,9 @@ namespace internal Accessor:: Accessor (tensor_type &tensor, const TableIndices &previous_indices) - : - tensor (tensor), - previous_indices (previous_indices) + : + tensor (tensor), + previous_indices (previous_indices) {} @@ -1081,9 +1081,9 @@ namespace internal Accessor:: Accessor (tensor_type &tensor, const TableIndices &previous_indices) - : - tensor (tensor), - previous_indices (previous_indices) + : + tensor (tensor), + previous_indices (previous_indices) {} @@ -1146,8 +1146,8 @@ SymmetricTensor::SymmetricTensor (const Tensor<2,dim,Number> &t template inline SymmetricTensor::SymmetricTensor (const Number (&array) [n_independent_components]) - : - data (array) + : + data (array) {} @@ -1178,8 +1178,8 @@ SymmetricTensor::operator = (const Number d) - // helper function to convert symmetric tensor - // to generic tensor +// helper function to convert symmetric tensor +// to generic tensor namespace internal { template @@ -1197,7 +1197,8 @@ namespace internal conversion (const Tensor<1,3,Number> &data) { const Number t[2][2] = {{data[0], data[2]}, - {data[2], data[1]}}; + {data[2], data[1]} + }; return Tensor<2,2,Number>(t); } @@ -1207,8 +1208,9 @@ namespace internal conversion (const Tensor<1,6,Number> &data) { const Number t[3][3] = {{data[0], data[3], data[4]}, - {data[3], data[1], data[5]}, - {data[4], data[5], data[2]}}; + {data[3], data[1], data[5]}, + {data[4], data[5], data[2]} + }; return Tensor<2,3,Number>(t); } } @@ -1501,10 +1503,10 @@ inline typename internal::SymmetricTensorAccessors::double_contraction_result::type SymmetricTensor::operator * (const SymmetricTensor<2,dim,Number> &s) const { - // need to have two different function calls - // because a scalar and rank-2 tensor are not - // the same data type (see internal function - // above) + // need to have two different function calls + // because a scalar and rank-2 tensor are not + // the same data type (see internal function + // above) return internal::perform_double_contraction (data, s.data); } @@ -1516,21 +1518,21 @@ typename internal::SymmetricTensorAccessors::double_contraction_result::operator * (const SymmetricTensor<4,dim,Number> &s) const { typename internal::SymmetricTensorAccessors:: - double_contraction_result::type tmp; + double_contraction_result::type tmp; tmp.data = internal::perform_double_contraction (data,s.data); return tmp; } - // internal namespace to switch between the - // access of different tensors. There used to - // be explicit instantiations before for - // different ranks and dimensions, but since - // we now allow for templates on the data - // type, and since we cannot partially - // specialize the implementation, this got - // into a separate namespace +// internal namespace to switch between the +// access of different tensors. There used to +// be explicit instantiations before for +// different ranks and dimensions, but since +// we now allow for templates on the data +// type, and since we cannot partially +// specialize the implementation, this got +// into a separate namespace namespace internal { template @@ -1545,30 +1547,30 @@ namespace internal return data[0]; case 2: - // first treat the main diagonal - // elements, which are stored - // consecutively at the beginning + // first treat the main diagonal + // elements, which are stored + // consecutively at the beginning if (indices[0] == indices[1]) return data[indices[0]]; - // the rest is messier and requires a few - // switches. at least for the 2x2 case it - // is reasonably simple + // the rest is messier and requires a few + // switches. at least for the 2x2 case it + // is reasonably simple Assert (((indices[0]==1) && (indices[1]==0)) || ((indices[0]==0) && (indices[1]==1)), ExcInternalError()); return data[2]; case 3: - // first treat the main diagonal - // elements, which are stored - // consecutively at the beginning + // first treat the main diagonal + // elements, which are stored + // consecutively at the beginning if (indices[0] == indices[1]) return data[indices[0]]; - // the rest is messier and requires a few - // switches, but simpler if we just sort - // our indices + // the rest is messier and requires a few + // switches, but simpler if we just sort + // our indices { TableIndices<2> sorted_indices (indices); sorted_indices.sort (); @@ -1602,30 +1604,30 @@ namespace internal return data[0]; case 2: - // first treat the main diagonal - // elements, which are stored - // consecutively at the beginning + // first treat the main diagonal + // elements, which are stored + // consecutively at the beginning if (indices[0] == indices[1]) return data[indices[0]]; - // the rest is messier and requires a few - // switches. at least for the 2x2 case it - // is reasonably simple + // the rest is messier and requires a few + // switches. at least for the 2x2 case it + // is reasonably simple Assert (((indices[0]==1) && (indices[1]==0)) || ((indices[0]==0) && (indices[1]==1)), ExcInternalError()); return data[2]; case 3: - // first treat the main diagonal - // elements, which are stored - // consecutively at the beginning + // first treat the main diagonal + // elements, which are stored + // consecutively at the beginning if (indices[0] == indices[1]) return data[indices[0]]; - // the rest is messier and requires a few - // switches, but simpler if we just sort - // our indices + // the rest is messier and requires a few + // switches, but simpler if we just sort + // our indices { TableIndices<2> sorted_indices (indices); sorted_indices.sort (); @@ -1655,95 +1657,95 @@ namespace internal { switch (dim) { - case 1: - return data[0][0]; - - case 2: - // each entry of the tensor can be - // thought of as an entry in a - // matrix that maps the rolled-out - // rank-2 tensors into rolled-out - // rank-2 tensors. this is the - // format in which we store rank-4 - // tensors. determine which - // position the present entry is - // stored in - { - unsigned int base_index[2] ; - if ((indices[0] == 0) && (indices[1] == 0)) - base_index[0] = 0; - else if ((indices[0] == 1) && (indices[1] == 1)) - base_index[0] = 1; - else - base_index[0] = 2; - - if ((indices[2] == 0) && (indices[3] == 0)) - base_index[1] = 0; - else if ((indices[2] == 1) && (indices[3] == 1)) - base_index[1] = 1; - else - base_index[1] = 2; + case 1: + return data[0][0]; - return data[base_index[0]][base_index[1]]; - } + case 2: + // each entry of the tensor can be + // thought of as an entry in a + // matrix that maps the rolled-out + // rank-2 tensors into rolled-out + // rank-2 tensors. this is the + // format in which we store rank-4 + // tensors. determine which + // position the present entry is + // stored in + { + unsigned int base_index[2] ; + if ((indices[0] == 0) && (indices[1] == 0)) + base_index[0] = 0; + else if ((indices[0] == 1) && (indices[1] == 1)) + base_index[0] = 1; + else + base_index[0] = 2; + + if ((indices[2] == 0) && (indices[3] == 0)) + base_index[1] = 0; + else if ((indices[2] == 1) && (indices[3] == 1)) + base_index[1] = 1; + else + base_index[1] = 2; + + return data[base_index[0]][base_index[1]]; + } - case 3: - // each entry of the tensor can be - // thought of as an entry in a - // matrix that maps the rolled-out - // rank-2 tensors into rolled-out - // rank-2 tensors. this is the - // format in which we store rank-4 - // tensors. determine which - // position the present entry is - // stored in - { - unsigned int base_index[2] ; - if ((indices[0] == 0) && (indices[1] == 0)) - base_index[0] = 0; - else if ((indices[0] == 1) && (indices[1] == 1)) - base_index[0] = 1; - else if ((indices[0] == 2) && (indices[1] == 2)) - base_index[0] = 2; - else if (((indices[0] == 0) && (indices[1] == 1)) || - ((indices[0] == 1) && (indices[1] == 0))) - base_index[0] = 3; - else if (((indices[0] == 0) && (indices[1] == 2)) || - ((indices[0] == 2) && (indices[1] == 0))) - base_index[0] = 4; - else - { - Assert (((indices[0] == 1) && (indices[1] == 2)) || - ((indices[0] == 2) && (indices[1] == 1)), - ExcInternalError()); - base_index[0] = 5; - } - - if ((indices[2] == 0) && (indices[3] == 0)) - base_index[1] = 0; - else if ((indices[2] == 1) && (indices[3] == 1)) - base_index[1] = 1; - else if ((indices[2] == 2) && (indices[3] == 2)) - base_index[1] = 2; - else if (((indices[2] == 0) && (indices[3] == 1)) || - ((indices[2] == 1) && (indices[3] == 0))) - base_index[1] = 3; - else if (((indices[2] == 0) && (indices[3] == 2)) || - ((indices[2] == 2) && (indices[3] == 0))) - base_index[1] = 4; - else - { - Assert (((indices[2] == 1) && (indices[3] == 2)) || - ((indices[2] == 2) && (indices[3] == 1)), - ExcInternalError()); - base_index[1] = 5; - } - - return data[base_index[0]][base_index[1]]; - } + case 3: + // each entry of the tensor can be + // thought of as an entry in a + // matrix that maps the rolled-out + // rank-2 tensors into rolled-out + // rank-2 tensors. this is the + // format in which we store rank-4 + // tensors. determine which + // position the present entry is + // stored in + { + unsigned int base_index[2] ; + if ((indices[0] == 0) && (indices[1] == 0)) + base_index[0] = 0; + else if ((indices[0] == 1) && (indices[1] == 1)) + base_index[0] = 1; + else if ((indices[0] == 2) && (indices[1] == 2)) + base_index[0] = 2; + else if (((indices[0] == 0) && (indices[1] == 1)) || + ((indices[0] == 1) && (indices[1] == 0))) + base_index[0] = 3; + else if (((indices[0] == 0) && (indices[1] == 2)) || + ((indices[0] == 2) && (indices[1] == 0))) + base_index[0] = 4; + else + { + Assert (((indices[0] == 1) && (indices[1] == 2)) || + ((indices[0] == 2) && (indices[1] == 1)), + ExcInternalError()); + base_index[0] = 5; + } + + if ((indices[2] == 0) && (indices[3] == 0)) + base_index[1] = 0; + else if ((indices[2] == 1) && (indices[3] == 1)) + base_index[1] = 1; + else if ((indices[2] == 2) && (indices[3] == 2)) + base_index[1] = 2; + else if (((indices[2] == 0) && (indices[3] == 1)) || + ((indices[2] == 1) && (indices[3] == 0))) + base_index[1] = 3; + else if (((indices[2] == 0) && (indices[3] == 2)) || + ((indices[2] == 2) && (indices[3] == 0))) + base_index[1] = 4; + else + { + Assert (((indices[2] == 1) && (indices[3] == 2)) || + ((indices[2] == 2) && (indices[3] == 1)), + ExcInternalError()); + base_index[1] = 5; + } + + return data[base_index[0]][base_index[1]]; + } - default: - Assert (false, ExcNotImplemented()); + default: + Assert (false, ExcNotImplemented()); } static Number dummy; @@ -1763,91 +1765,91 @@ namespace internal return data[0][0]; case 2: - // each entry of the tensor can be - // thought of as an entry in a - // matrix that maps the rolled-out - // rank-2 tensors into rolled-out - // rank-2 tensors. this is the - // format in which we store rank-4 - // tensors. determine which - // position the present entry is - // stored in - { - unsigned int base_index[2] ; - if ((indices[0] == 0) && (indices[1] == 0)) - base_index[0] = 0; - else if ((indices[0] == 1) && (indices[1] == 1)) - base_index[0] = 1; - else - base_index[0] = 2; - - if ((indices[2] == 0) && (indices[3] == 0)) - base_index[1] = 0; - else if ((indices[2] == 1) && (indices[3] == 1)) - base_index[1] = 1; - else - base_index[1] = 2; - - return data[base_index[0]][base_index[1]]; - } + // each entry of the tensor can be + // thought of as an entry in a + // matrix that maps the rolled-out + // rank-2 tensors into rolled-out + // rank-2 tensors. this is the + // format in which we store rank-4 + // tensors. determine which + // position the present entry is + // stored in + { + unsigned int base_index[2] ; + if ((indices[0] == 0) && (indices[1] == 0)) + base_index[0] = 0; + else if ((indices[0] == 1) && (indices[1] == 1)) + base_index[0] = 1; + else + base_index[0] = 2; + + if ((indices[2] == 0) && (indices[3] == 0)) + base_index[1] = 0; + else if ((indices[2] == 1) && (indices[3] == 1)) + base_index[1] = 1; + else + base_index[1] = 2; + + return data[base_index[0]][base_index[1]]; + } case 3: - // each entry of the tensor can be - // thought of as an entry in a - // matrix that maps the rolled-out - // rank-2 tensors into rolled-out - // rank-2 tensors. this is the - // format in which we store rank-4 - // tensors. determine which - // position the present entry is - // stored in - { - unsigned int base_index[2] ; - if ((indices[0] == 0) && (indices[1] == 0)) - base_index[0] = 0; - else if ((indices[0] == 1) && (indices[1] == 1)) - base_index[0] = 1; - else if ((indices[0] == 2) && (indices[1] == 2)) - base_index[0] = 2; - else if (((indices[0] == 0) && (indices[1] == 1)) || - ((indices[0] == 1) && (indices[1] == 0))) - base_index[0] = 3; - else if (((indices[0] == 0) && (indices[1] == 2)) || - ((indices[0] == 2) && (indices[1] == 0))) - base_index[0] = 4; - else - { - Assert (((indices[0] == 1) && (indices[1] == 2)) || - ((indices[0] == 2) && (indices[1] == 1)), - ExcInternalError()); - base_index[0] = 5; - } - - if ((indices[2] == 0) && (indices[3] == 0)) - base_index[1] = 0; - else if ((indices[2] == 1) && (indices[3] == 1)) - base_index[1] = 1; - else if ((indices[2] == 2) && (indices[3] == 2)) - base_index[1] = 2; - else if (((indices[2] == 0) && (indices[3] == 1)) || - ((indices[2] == 1) && (indices[3] == 0))) - base_index[1] = 3; - else if (((indices[2] == 0) && (indices[3] == 2)) || - ((indices[2] == 2) && (indices[3] == 0))) - base_index[1] = 4; - else - { - Assert (((indices[2] == 1) && (indices[3] == 2)) || - ((indices[2] == 2) && (indices[3] == 1)), - ExcInternalError()); - base_index[1] = 5; - } - - return data[base_index[0]][base_index[1]]; - } + // each entry of the tensor can be + // thought of as an entry in a + // matrix that maps the rolled-out + // rank-2 tensors into rolled-out + // rank-2 tensors. this is the + // format in which we store rank-4 + // tensors. determine which + // position the present entry is + // stored in + { + unsigned int base_index[2] ; + if ((indices[0] == 0) && (indices[1] == 0)) + base_index[0] = 0; + else if ((indices[0] == 1) && (indices[1] == 1)) + base_index[0] = 1; + else if ((indices[0] == 2) && (indices[1] == 2)) + base_index[0] = 2; + else if (((indices[0] == 0) && (indices[1] == 1)) || + ((indices[0] == 1) && (indices[1] == 0))) + base_index[0] = 3; + else if (((indices[0] == 0) && (indices[1] == 2)) || + ((indices[0] == 2) && (indices[1] == 0))) + base_index[0] = 4; + else + { + Assert (((indices[0] == 1) && (indices[1] == 2)) || + ((indices[0] == 2) && (indices[1] == 1)), + ExcInternalError()); + base_index[0] = 5; + } + + if ((indices[2] == 0) && (indices[3] == 0)) + base_index[1] = 0; + else if ((indices[2] == 1) && (indices[3] == 1)) + base_index[1] = 1; + else if ((indices[2] == 2) && (indices[3] == 2)) + base_index[1] = 2; + else if (((indices[2] == 0) && (indices[3] == 1)) || + ((indices[2] == 1) && (indices[3] == 0))) + base_index[1] = 3; + else if (((indices[2] == 0) && (indices[3] == 2)) || + ((indices[2] == 2) && (indices[3] == 0))) + base_index[1] = 4; + else + { + Assert (((indices[2] == 1) && (indices[3] == 2)) || + ((indices[2] == 2) && (indices[3] == 1)), + ExcInternalError()); + base_index[1] = 5; + } + + return data[base_index[0]][base_index[1]]; + } - default: - Assert (false, ExcNotImplemented()); + default: + Assert (false, ExcNotImplemented()); } static Number dummy; @@ -1874,7 +1876,7 @@ template inline Number SymmetricTensor::operator () - (const TableIndices &indices) const +(const TableIndices &indices) const { for (unsigned int r=0; r inline unsigned int SymmetricTensor::component_to_unrolled_index - (const TableIndices &indices) +(const TableIndices &indices) { Assert (rank == 2, ExcNotImplemented()); Assert (indices[0] < dim, ExcIndexRange(indices[0], 0, dim)); Assert (indices[1] < dim, ExcIndexRange(indices[1], 0, dim)); - switch(dim) + switch (dim) { case 1: return 0; case 2: - { - static const unsigned int table[2][2] = {{0, 2}, - {2, 1}}; - return table[indices[0]][indices[1]]; - } + { + static const unsigned int table[2][2] = {{0, 2}, + {2, 1} + }; + return table[indices[0]][indices[1]]; + } case 3: - { - static const unsigned int table[3][3] = {{0, 3, 4}, - {3, 1, 5}, - {4, 5, 2}}; - return table[indices[0]][indices[1]]; - } + { + static const unsigned int table[3][3] = {{0, 3, 4}, + {3, 1, 5}, + {4, 5, 2} + }; + return table[indices[0]][indices[1]]; + } case 4: - { - static const unsigned int table[4][4] = {{0, 4, 5, 6}, - {4, 1, 7, 8}, - {5, 7, 2, 9}, - {6, 8, 9, 3}}; - return table[indices[0]][indices[1]]; - } + { + static const unsigned int table[4][4] = {{0, 4, 5, 6}, + {4, 1, 7, 8}, + {5, 7, 2, 9}, + {6, 8, 9, 3} + }; + return table[indices[0]][indices[1]]; + } default: Assert (false, ExcNotImplemented()); return 0; @@ -2076,7 +2081,7 @@ template inline TableIndices SymmetricTensor::unrolled_to_component_indices - (const unsigned int i) +(const unsigned int i) { Assert (rank == 2, ExcNotImplemented()); Assert (i < n_independent_components, ExcIndexRange(i, 0, n_independent_components)); @@ -2085,24 +2090,28 @@ SymmetricTensor::unrolled_to_component_indices case 1: return TableIndices<2>(0,0); case 2: + { + static const TableIndices<2> table[3] = { - static const TableIndices<2> table[3] = - { TableIndices<2> (0,0), - TableIndices<2> (1,1), - TableIndices<2> (0,1) }; - return table[i]; - } + TableIndices<2> (0,0), + TableIndices<2> (1,1), + TableIndices<2> (0,1) + }; + return table[i]; + } case 3: + { + static const TableIndices<2> table[6] = { - static const TableIndices<2> table[6] = - { TableIndices<2> (0,0), - TableIndices<2> (1,1), - TableIndices<2> (2,2), - TableIndices<2> (0,1), - TableIndices<2> (0,2), - TableIndices<2> (1,2) }; - return table[i]; - } + TableIndices<2> (0,0), + TableIndices<2> (1,1), + TableIndices<2> (2,2), + TableIndices<2> (0,1), + TableIndices<2> (0,2), + TableIndices<2> (1,2) + }; + return table[i]; + } default: Assert (false, ExcNotImplemented()); return TableIndices<2>(0,0); @@ -2115,9 +2124,9 @@ template template inline void -SymmetricTensor::serialize(Archive & ar, const unsigned int) +SymmetricTensor::serialize(Archive &ar, const unsigned int) { - ar & data; + ar &data; } @@ -2149,9 +2158,9 @@ Number determinant (const SymmetricTensor<2,dim,Number> &t) case 2: return (t.data[0] * t.data[1] - t.data[2]*t.data[2]); case 3: - // in analogy to general tensors, but - // there's something to be simplified for - // the present case + // in analogy to general tensors, but + // there's something to be simplified for + // the present case return ( t.data[0]*t.data[1]*t.data[2] -t.data[0]*t.data[5]*t.data[5] -t.data[1]*t.data[4]*t.data[4] @@ -2303,7 +2312,7 @@ deviator (const SymmetricTensor<2,dim,Number> &t) { SymmetricTensor<2,dim,Number> tmp = t; - // subtract scaled trace from the diagonal + // subtract scaled trace from the diagonal const Number tr = trace(t) / dim; for (unsigned int i=0; i tmp; - // fill the elements treating the diagonal + // fill the elements treating the diagonal for (unsigned int i=0; i::n_rank2_components; ++i) @@ -2455,15 +2464,15 @@ identity_tensor () { SymmetricTensor<4,dim,Number> tmp; - // fill the elements treating the diagonal + // fill the elements treating the diagonal for (unsigned int i=0; i::n_rank2_components; ++i) @@ -2531,67 +2540,67 @@ invert (const SymmetricTensor<4,dim,Number> &t) break; case 2: - // inverting this tensor is a little more - // complicated than necessary, since we - // store the data of 't' as a 3x3 matrix - // t.data, but the product between a rank-4 - // and a rank-2 tensor is really not the - // product between this matrix and the - // 3-vector of a rhs, but rather - // - // B.vec = t.data * mult * A.vec - // - // where mult is a 3x3 matrix with - // entries [[1,0,0],[0,1,0],[0,0,2]] to - // capture the fact that we need to add up - // both the c_ij12*a_12 and the c_ij21*a_21 - // terms - // - // in addition, in this scheme, the - // identity tensor has the matrix - // representation mult^-1. - // - // the inverse of 't' therefore has the - // matrix representation - // - // inv.data = mult^-1 * t.data^-1 * mult^-1 - // - // in order to compute it, let's first - // compute the inverse of t.data and put it - // into tmp.data; at the end of the - // function we then scale the last row and - // column of the inverse by 1/2, - // corresponding to the left and right - // multiplication with mult^-1 - { - const Number t4 = t.data[0][0]*t.data[1][1], - t6 = t.data[0][0]*t.data[1][2], - t8 = t.data[0][1]*t.data[1][0], - t00 = t.data[0][2]*t.data[1][0], - t01 = t.data[0][1]*t.data[2][0], - t04 = t.data[0][2]*t.data[2][0], - t07 = 1.0/(t4*t.data[2][2]-t6*t.data[2][1]- - t8*t.data[2][2]+t00*t.data[2][1]+ - t01*t.data[1][2]-t04*t.data[1][1]); - tmp.data[0][0] = (t.data[1][1]*t.data[2][2]-t.data[1][2]*t.data[2][1])*t07; - tmp.data[0][1] = -(t.data[0][1]*t.data[2][2]-t.data[0][2]*t.data[2][1])*t07; - tmp.data[0][2] = -(-t.data[0][1]*t.data[1][2]+t.data[0][2]*t.data[1][1])*t07; - tmp.data[1][0] = -(t.data[1][0]*t.data[2][2]-t.data[1][2]*t.data[2][0])*t07; - tmp.data[1][1] = (t.data[0][0]*t.data[2][2]-t04)*t07; - tmp.data[1][2] = -(t6-t00)*t07; - tmp.data[2][0] = -(-t.data[1][0]*t.data[2][1]+t.data[1][1]*t.data[2][0])*t07; - tmp.data[2][1] = -(t.data[0][0]*t.data[2][1]-t01)*t07; - tmp.data[2][2] = (t4-t8)*t07; - - // scale last row and column as mentioned - // above - tmp.data[2][0] /= 2; - tmp.data[2][1] /= 2; - tmp.data[0][2] /= 2; - tmp.data[1][2] /= 2; - tmp.data[2][2] /= 4; - } - break; + // inverting this tensor is a little more + // complicated than necessary, since we + // store the data of 't' as a 3x3 matrix + // t.data, but the product between a rank-4 + // and a rank-2 tensor is really not the + // product between this matrix and the + // 3-vector of a rhs, but rather + // + // B.vec = t.data * mult * A.vec + // + // where mult is a 3x3 matrix with + // entries [[1,0,0],[0,1,0],[0,0,2]] to + // capture the fact that we need to add up + // both the c_ij12*a_12 and the c_ij21*a_21 + // terms + // + // in addition, in this scheme, the + // identity tensor has the matrix + // representation mult^-1. + // + // the inverse of 't' therefore has the + // matrix representation + // + // inv.data = mult^-1 * t.data^-1 * mult^-1 + // + // in order to compute it, let's first + // compute the inverse of t.data and put it + // into tmp.data; at the end of the + // function we then scale the last row and + // column of the inverse by 1/2, + // corresponding to the left and right + // multiplication with mult^-1 + { + const Number t4 = t.data[0][0]*t.data[1][1], + t6 = t.data[0][0]*t.data[1][2], + t8 = t.data[0][1]*t.data[1][0], + t00 = t.data[0][2]*t.data[1][0], + t01 = t.data[0][1]*t.data[2][0], + t04 = t.data[0][2]*t.data[2][0], + t07 = 1.0/(t4*t.data[2][2]-t6*t.data[2][1]- + t8*t.data[2][2]+t00*t.data[2][1]+ + t01*t.data[1][2]-t04*t.data[1][1]); + tmp.data[0][0] = (t.data[1][1]*t.data[2][2]-t.data[1][2]*t.data[2][1])*t07; + tmp.data[0][1] = -(t.data[0][1]*t.data[2][2]-t.data[0][2]*t.data[2][1])*t07; + tmp.data[0][2] = -(-t.data[0][1]*t.data[1][2]+t.data[0][2]*t.data[1][1])*t07; + tmp.data[1][0] = -(t.data[1][0]*t.data[2][2]-t.data[1][2]*t.data[2][0])*t07; + tmp.data[1][1] = (t.data[0][0]*t.data[2][2]-t04)*t07; + tmp.data[1][2] = -(t6-t00)*t07; + tmp.data[2][0] = -(-t.data[1][0]*t.data[2][1]+t.data[1][1]*t.data[2][0])*t07; + tmp.data[2][1] = -(t.data[0][0]*t.data[2][1]-t01)*t07; + tmp.data[2][2] = (t4-t8)*t07; + + // scale last row and column as mentioned + // above + tmp.data[2][0] /= 2; + tmp.data[2][1] /= 2; + tmp.data[0][2] /= 2; + tmp.data[1][2] /= 2; + tmp.data[2][2] /= 4; + } + break; default: Assert (false, ExcNotImplemented()); } @@ -2642,7 +2651,7 @@ outer_product (const SymmetricTensor<2,dim,Number> &t1, { SymmetricTensor<4,dim,Number> tmp; - // fill only the elements really needed + // fill only the elements really needed for (unsigned int i=0; i &t) = { t[0][0], t[1][1], t[2][2], (t[0][1] + t[1][0])/2, (t[0][2] + t[2][0])/2, - (t[1][2] + t[2][1])/2 }; + (t[1][2] + t[2][1])/2 + }; return SymmetricTensor<2,3,Number>(array); } @@ -3061,12 +3071,12 @@ operator * (const SymmetricTensor<2,dim,Number> &src1, */ template inline -std::ostream & operator << (std::ostream &out, - const SymmetricTensor<2,dim,Number> &t) +std::ostream &operator << (std::ostream &out, + const SymmetricTensor<2,dim,Number> &t) { - //make out lives a bit simpler by outputing - //the tensor through the operator for the - //general Tensor class + //make out lives a bit simpler by outputing + //the tensor through the operator for the + //general Tensor class Tensor<2,dim,Number> tt; for (unsigned int i=0; i inline -std::ostream & operator << (std::ostream &out, - const SymmetricTensor<4,dim,Number> &t) +std::ostream &operator << (std::ostream &out, + const SymmetricTensor<4,dim,Number> &t) { - //make out lives a bit simpler by outputing - //the tensor through the operator for the - //general Tensor class + //make out lives a bit simpler by outputing + //the tensor through the operator for the + //general Tensor class Tensor<4,dim,Number> tt; for (unsigned int i=0; i struct SynchronousIterators { - /** - * Constructor. - */ - SynchronousIterators (const Iterators &i); - - /** - * Copy constructor. - */ - SynchronousIterators (const SynchronousIterators &i); - - /** - * Storage for the iterators - * represented by the current class. - */ - Iterators iterators; + /** + * Constructor. + */ + SynchronousIterators (const Iterators &i); + + /** + * Copy constructor. + */ + SynchronousIterators (const SynchronousIterators &i); + + /** + * Storage for the iterators + * represented by the current class. + */ + Iterators iterators; }; @@ -75,8 +75,8 @@ template inline SynchronousIterators:: SynchronousIterators (const Iterators &i) - : - iterators (i) + : + iterators (i) {} @@ -84,21 +84,21 @@ template inline SynchronousIterators:: SynchronousIterators (const SynchronousIterators &i) - : - iterators (i.iterators) + : + iterators (i.iterators) {} - /** - * Return whether the first element of - * the first argument is less than the - * first element of the second - * argument. Since the objects compared - * march forward all elements at the same - * time, comparing the first element is - * sufficient. - */ +/** + * Return whether the first element of + * the first argument is less than the + * first element of the second + * argument. Since the objects compared + * march forward all elements at the same + * time, comparing the first element is + * sufficient. + */ template inline bool @@ -110,14 +110,14 @@ operator< (const SynchronousIterators &a, - /** - * Return the distance between the first - * and the second argument. Since the - * objects compared march forward all - * elements at the same time, - * differencing the first element is - * sufficient. - */ +/** + * Return the distance between the first + * and the second argument. Since the + * objects compared march forward all + * elements at the same time, + * differencing the first element is + * sufficient. + */ template inline std::size_t @@ -132,9 +132,9 @@ operator- (const SynchronousIterators &a, } - /** - * Advance a tuple of iterators by $n$. - */ +/** + * Advance a tuple of iterators by $n$. + */ template inline void advance (std_cxx1x::tuple &t, @@ -144,9 +144,9 @@ void advance (std_cxx1x::tuple &t, std::advance (std_cxx1x::get<1>(t), n); } - /** - * Advance a tuple of iterators by $n$. - */ +/** + * Advance a tuple of iterators by $n$. + */ template inline void advance (std_cxx1x::tuple &t, @@ -157,11 +157,11 @@ void advance (std_cxx1x::tuple &t, std::advance (std_cxx1x::get<2>(t), n); } - /** - * Advance a tuple of iterators by $n$. - */ +/** + * Advance a tuple of iterators by $n$. + */ template + typename I3, typename I4> inline void advance (std_cxx1x::tuple &t, const unsigned int n) @@ -174,9 +174,9 @@ void advance (std_cxx1x::tuple &t, - /** - * Advance a tuple of iterators by 1. - */ +/** + * Advance a tuple of iterators by 1. + */ template inline void advance_by_one (std_cxx1x::tuple &t) @@ -185,9 +185,9 @@ void advance_by_one (std_cxx1x::tuple &t) ++std_cxx1x::get<1>(t); } - /** - * Advance a tuple of iterators by 1. - */ +/** + * Advance a tuple of iterators by 1. + */ template inline void advance_by_one (std_cxx1x::tuple &t) @@ -197,11 +197,11 @@ void advance_by_one (std_cxx1x::tuple &t) ++std_cxx1x::get<2>(t); } - /** - * Advance a tuple of iterators by 1. - */ +/** + * Advance a tuple of iterators by 1. + */ template + typename I3, typename I4> inline void advance_by_one (std_cxx1x::tuple &t) { @@ -213,10 +213,10 @@ void advance_by_one (std_cxx1x::tuple &t) - /** - * Advance the elements of this iterator - * by $n$. - */ +/** + * Advance the elements of this iterator + * by $n$. + */ template inline SynchronousIterators @@ -228,10 +228,10 @@ operator + (const SynchronousIterators &a, return x; } - /** - * Advance the elements of this iterator - * by 1. - */ +/** + * Advance the elements of this iterator + * by 1. + */ template inline SynchronousIterators @@ -242,12 +242,12 @@ operator ++ (SynchronousIterators &a) } - /** - * Compare synch iterators for - * inequality. Since they march in synch, - * comparing only the first element is - * sufficient. - */ +/** + * Compare synch iterators for + * inequality. Since they march in synch, + * comparing only the first element is + * sufficient. + */ template inline bool diff --git a/deal.II/include/deal.II/base/table.h b/deal.II/include/deal.II/base/table.h index 051f680bf4..fc28f61f82 100644 --- a/deal.II/include/deal.II/base/table.h +++ b/deal.II/include/deal.II/base/table.h @@ -38,343 +38,343 @@ template class Table<6,T>; namespace internal { -/** - * @internal - * Have a namespace in which we declare some classes that are used to - * access the elements of tables using the operator[]. These are - * quite technical, since they have to do their work recursively (due - * to the fact that the number of indices is not known, we have to - * return an iterator into the next lower dimension object if we - * access one object, until we are on the lowest level and can - * actually return a reference to the stored data type itself). This - * is so technical that you will not usually want to look at these - * classes at all, except possibly for educational reasons. None of - * the classes herein has a interface that you should use explicitly - * in your programs (except, of course, through access to the elements - * of tables with operator[], which generates temporary objects of - * the types of this namespace). - * - * @author Wolfgang Bangerth, 2002 - */ + /** + * @internal + * Have a namespace in which we declare some classes that are used to + * access the elements of tables using the operator[]. These are + * quite technical, since they have to do their work recursively (due + * to the fact that the number of indices is not known, we have to + * return an iterator into the next lower dimension object if we + * access one object, until we are on the lowest level and can + * actually return a reference to the stored data type itself). This + * is so technical that you will not usually want to look at these + * classes at all, except possibly for educational reasons. None of + * the classes herein has a interface that you should use explicitly + * in your programs (except, of course, through access to the elements + * of tables with operator[], which generates temporary objects of + * the types of this namespace). + * + * @author Wolfgang Bangerth, 2002 + */ namespace TableBaseAccessors { -/** - * @internal - * Have a class which declares some nested typedefs, depending on its - * template parameters. The general template declares nothing, but - * there are more useful specializations regaring the last parameter - * indicating constness of the table for which accessor objects are to - * be generated in this namespace. - */ + /** + * @internal + * Have a class which declares some nested typedefs, depending on its + * template parameters. The general template declares nothing, but + * there are more useful specializations regaring the last parameter + * indicating constness of the table for which accessor objects are to + * be generated in this namespace. + */ template struct Types {}; -/** - * @internal - * Have a class which declares some nested typedefs, depending on its - * template parameters. Specialization for accessors to constant - * objects. - */ + /** + * @internal + * Have a class which declares some nested typedefs, depending on its + * template parameters. Specialization for accessors to constant + * objects. + */ template struct Types { - typedef const T value_type; - typedef const TableBase TableType; + typedef const T value_type; + typedef const TableBase TableType; - typedef typename std::vector::const_iterator iterator; - typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::const_iterator iterator; + typedef typename std::vector::const_iterator const_iterator; - typedef typename std::vector::const_reference reference; - typedef typename std::vector::const_reference const_reference; + typedef typename std::vector::const_reference reference; + typedef typename std::vector::const_reference const_reference; }; -/** - * @internal - * Have a class which declares some nested typedefs, depending on its - * template parameters. Specialization for accessors to non-constant - * objects. - */ + /** + * @internal + * Have a class which declares some nested typedefs, depending on its + * template parameters. Specialization for accessors to non-constant + * objects. + */ template struct Types { - typedef T value_type; - typedef TableBase TableType; + typedef T value_type; + typedef TableBase TableType; - typedef typename std::vector::iterator iterator; - typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; - typedef typename std::vector::reference reference; - typedef typename std::vector::const_reference const_reference; + typedef typename std::vector::reference reference; + typedef typename std::vector::const_reference const_reference; }; -/** - * @internal - * Class that acts as accessor to subobjects of tables of type - * Table. The template parameter C may be either true or - * false, and indicates whether the objects worked on are constant or - * not (i.e. write access is only allowed if the value is false). - * - * Since with N indices, the effect of applying operator[] is - * getting access to something we N-1 indices, we have to - * implement these accessor classes recursively, with stopping when we - * have only one index left. For the latter case, a specialization of - * this class is declared below, where calling operator[] gives - * you access to the objects actually stored by the table. In the - * value given to the index operator needs to be checked whether it is - * inside its bounds, for which we need to know which index of the - * table we are actually accessing presently. This is done through the - * template parameter P: it indicates, how many remaining indices - * there are. For a vector, P may only be one (and then the - * specialization below is used). For a table this value may be two, - * and when using operator[], an object with P=1 emerges. - * - * The value of P is also used to determine the stride: this - * object stores a pointer indicating the beginning of the range of - * objects that it may access. When we apply operator[] on this - * object, the resulting new accessor may only access a subset of - * these elements, and to know which subset we need to know the - * dimensions of the table and the present index, which is indicated - * by P. - * - * As stated for the entire namespace, you will not usually have to do - * with these classes directly, and should not try to use their - * interface directly as it may change without notice. In fact, since - * the constructors are made private, you will not even be able to - * generate objects of this class, as they are only thought as - * temporaries for access to elements of the table class, not for - * passing them around as arguments of functions, etc. - * - * @author Wolfgang Bangerth, 2002 - */ + /** + * @internal + * Class that acts as accessor to subobjects of tables of type + * Table. The template parameter C may be either true or + * false, and indicates whether the objects worked on are constant or + * not (i.e. write access is only allowed if the value is false). + * + * Since with N indices, the effect of applying operator[] is + * getting access to something we N-1 indices, we have to + * implement these accessor classes recursively, with stopping when we + * have only one index left. For the latter case, a specialization of + * this class is declared below, where calling operator[] gives + * you access to the objects actually stored by the table. In the + * value given to the index operator needs to be checked whether it is + * inside its bounds, for which we need to know which index of the + * table we are actually accessing presently. This is done through the + * template parameter P: it indicates, how many remaining indices + * there are. For a vector, P may only be one (and then the + * specialization below is used). For a table this value may be two, + * and when using operator[], an object with P=1 emerges. + * + * The value of P is also used to determine the stride: this + * object stores a pointer indicating the beginning of the range of + * objects that it may access. When we apply operator[] on this + * object, the resulting new accessor may only access a subset of + * these elements, and to know which subset we need to know the + * dimensions of the table and the present index, which is indicated + * by P. + * + * As stated for the entire namespace, you will not usually have to do + * with these classes directly, and should not try to use their + * interface directly as it may change without notice. In fact, since + * the constructors are made private, you will not even be able to + * generate objects of this class, as they are only thought as + * temporaries for access to elements of the table class, not for + * passing them around as arguments of functions, etc. + * + * @author Wolfgang Bangerth, 2002 + */ template class Accessor { - public: - typedef typename Types::TableType TableType; - - typedef typename Types::iterator iterator; - typedef typename Types::const_iterator const_iterator; - - private: - /** - * Constructor. Take a pointer - * to the table object to know - * about the sizes of the - * various dimensions, and a - * pointer to the subset of - * data we may access. - */ - Accessor (const TableType &table, - const iterator data); - - /** - * Default constructor. Not - * needed, and invisible, so - * private. - */ - Accessor (); - - public: - - /** - * Copy constructor. This constructor - * is public so that one can pass - * sub-tables to functions as - * arguments, as in - * f(table[i]). - * - * Using this constructor is risky if - * accessors are stored longer than - * the table it points to. Don't do - * this. - */ - Accessor (const Accessor &a); - - /** - * Index operator. Performs a - * range check. - */ - Accessor operator [] (const unsigned int i) const; - - /** - * Exception for range - * check. Do not use global - * exception since this way we - * can output which index is - * the wrong one. - */ - DeclException3 (ExcIndexRange, int, int, int, - << "Index " << N-P+1 << "has a value of " - << arg1 << " but needs to be in the range [" - << arg2 << "," << arg3 << "["); - private: - /** - * Store the data given to the - * constructor. There are no - * non-const member functions - * of this class, so there is - * no reason not to make these - * elements constant. - */ - const TableType &table; - const iterator data; - - // declare some other classes - // as friends. make sure to - // work around bugs in some - // compilers - template friend class dealii::Table; - template - friend class Accessor; + public: + typedef typename Types::TableType TableType; + + typedef typename Types::iterator iterator; + typedef typename Types::const_iterator const_iterator; + + private: + /** + * Constructor. Take a pointer + * to the table object to know + * about the sizes of the + * various dimensions, and a + * pointer to the subset of + * data we may access. + */ + Accessor (const TableType &table, + const iterator data); + + /** + * Default constructor. Not + * needed, and invisible, so + * private. + */ + Accessor (); + + public: + + /** + * Copy constructor. This constructor + * is public so that one can pass + * sub-tables to functions as + * arguments, as in + * f(table[i]). + * + * Using this constructor is risky if + * accessors are stored longer than + * the table it points to. Don't do + * this. + */ + Accessor (const Accessor &a); + + /** + * Index operator. Performs a + * range check. + */ + Accessor operator [] (const unsigned int i) const; + + /** + * Exception for range + * check. Do not use global + * exception since this way we + * can output which index is + * the wrong one. + */ + DeclException3 (ExcIndexRange, int, int, int, + << "Index " << N-P+1 << "has a value of " + << arg1 << " but needs to be in the range [" + << arg2 << "," << arg3 << "["); + private: + /** + * Store the data given to the + * constructor. There are no + * non-const member functions + * of this class, so there is + * no reason not to make these + * elements constant. + */ + const TableType &table; + const iterator data; + + // declare some other classes + // as friends. make sure to + // work around bugs in some + // compilers + template friend class dealii::Table; + template + friend class Accessor; # ifndef DEAL_II_TEMPL_SPEC_FRIEND_BUG - friend class dealii::Table; - friend class Accessor; + friend class dealii::Table; + friend class Accessor; # endif }; -/** - * @internal - * Accessor class for tables. This is the specialization for the last - * index, which actually allows access to the elements of the table, - * rather than recursively returning access objects for further - * subsets. The same holds for this specialization as for the general - * template; see there for more information. - * - * @author Wolfgang Bangerth, 2002 - */ + /** + * @internal + * Accessor class for tables. This is the specialization for the last + * index, which actually allows access to the elements of the table, + * rather than recursively returning access objects for further + * subsets. The same holds for this specialization as for the general + * template; see there for more information. + * + * @author Wolfgang Bangerth, 2002 + */ template class Accessor { - public: - /** - * Typedef constant and - * non-constant iterator - * types to the elements of - * this row, as well as all - * the other types usually - * required for the standard - * library algorithms. - */ - typedef typename Types::value_type value_type; - - typedef typename Types::iterator iterator; - typedef typename Types::const_iterator const_iterator; - - typedef typename Types::reference reference; - typedef typename Types::const_reference const_reference; - - typedef size_t size_type; - typedef ptrdiff_t difference_type; - - /** - * Import a typedef from the - * switch class above. - */ - typedef typename Types::TableType TableType; - - private: - - /** - * Constructor. Take an iterator - * to the table object to know - * about the sizes of the - * various dimensions, and a - * iterator to the subset of - * data we may access (which in - * this particular case is only - * one row). - * - * The constructor is made - * private in order to prevent - * you having such objects - * around. The only way to - * create such objects is via - * the Table class, which - * only generates them as - * temporary objects. This - * guarantees that the accessor - * objects go out of scope - * earlier than the mother - * object, avoid problems with - * data consistency. - */ - Accessor (const TableType &table, - const iterator data); - - /** - * Default constructor. Not needed, - * so private. - */ - Accessor (); - - public: - /** - * Copy constructor. This constructor - * is public so that one can pass - * sub-tables to functions as - * arguments, as in - * f(table[i]). - * - * Using this constructor is risky if - * accessors are stored longer than - * the table it points to. Don't do - * this. - */ - Accessor (const Accessor &a); - - - /** - * Index operator. Performs a - * range check. - */ - reference operator [] (const unsigned int) const; - - /** - * Return the length of one row, - * i.e. the number of elements - * corresponding to the last - * index of the table object. - */ - unsigned int size () const; - - /** - * Return an iterator to the - * first element of this - * row. - */ - iterator begin () const; - - /** - * Return an interator to the - * element past the end of - * this row. - */ - iterator end () const; - - private: - /** - * Store the data given to the - * constructor. There are no - * non-const member functions - * of this class, so there is - * no reason not to make these - * elements constant. - */ - const TableType &table; - const iterator data; - - // declare some other classes - // as friends. make sure to - // work around bugs in some - // compilers - template friend class dealii::Table; - template - friend class Accessor; + public: + /** + * Typedef constant and + * non-constant iterator + * types to the elements of + * this row, as well as all + * the other types usually + * required for the standard + * library algorithms. + */ + typedef typename Types::value_type value_type; + + typedef typename Types::iterator iterator; + typedef typename Types::const_iterator const_iterator; + + typedef typename Types::reference reference; + typedef typename Types::const_reference const_reference; + + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + /** + * Import a typedef from the + * switch class above. + */ + typedef typename Types::TableType TableType; + + private: + + /** + * Constructor. Take an iterator + * to the table object to know + * about the sizes of the + * various dimensions, and a + * iterator to the subset of + * data we may access (which in + * this particular case is only + * one row). + * + * The constructor is made + * private in order to prevent + * you having such objects + * around. The only way to + * create such objects is via + * the Table class, which + * only generates them as + * temporary objects. This + * guarantees that the accessor + * objects go out of scope + * earlier than the mother + * object, avoid problems with + * data consistency. + */ + Accessor (const TableType &table, + const iterator data); + + /** + * Default constructor. Not needed, + * so private. + */ + Accessor (); + + public: + /** + * Copy constructor. This constructor + * is public so that one can pass + * sub-tables to functions as + * arguments, as in + * f(table[i]). + * + * Using this constructor is risky if + * accessors are stored longer than + * the table it points to. Don't do + * this. + */ + Accessor (const Accessor &a); + + + /** + * Index operator. Performs a + * range check. + */ + reference operator [] (const unsigned int) const; + + /** + * Return the length of one row, + * i.e. the number of elements + * corresponding to the last + * index of the table object. + */ + unsigned int size () const; + + /** + * Return an iterator to the + * first element of this + * row. + */ + iterator begin () const; + + /** + * Return an interator to the + * element past the end of + * this row. + */ + iterator end () const; + + private: + /** + * Store the data given to the + * constructor. There are no + * non-const member functions + * of this class, so there is + * no reason not to make these + * elements constant. + */ + const TableType &table; + const iterator data; + + // declare some other classes + // as friends. make sure to + // work around bugs in some + // compilers + template friend class dealii::Table; + template + friend class Accessor; # ifndef DEAL_II_TEMPL_SPEC_FRIEND_BUG - friend class dealii::Table<2,T>; - friend class Accessor; + friend class dealii::Table<2,T>; + friend class Accessor; # endif }; } @@ -461,264 +461,264 @@ namespace internal template class TableBase : public Subscriptor { - public: - /** - * Default constructor. Set all - * dimensions to zero. - */ - TableBase (); - - /** - * Constructor. Initialize the - * array with the given - * dimensions in each index - * component. - */ - TableBase (const TableIndices &sizes); - - /** - * Copy constructor. Performs a - * deep copy. - */ - TableBase (const TableBase &src); - - /** - * Copy constructor. Performs a - * deep copy from a table object - * storing some other data type. - */ - template - TableBase (const TableBase &src); - - /** - * Destructor. Free allocated memory. - */ - ~TableBase (); - - /** - * Assignment operator. - * Copy all elements of src - * into the matrix. The size is - * adjusted if needed. - * - * We can't use the other, templatized - * version since if we don't declare - * this one, the compiler will happily - * generate a predefined copy - * operator which is not what we want. - */ - TableBase& operator = (const TableBase& src); - - /** - * Copy operator. - * Copy all elements of src - * into the array. The size is - * adjusted if needed. - * - * This function requires that the - * type T2 is convertible to - * T. - */ - template - TableBase& operator = (const TableBase &src); - - /** - * Test for equality of two tables. - */ - bool operator == (const TableBase & T2) const; - - /** - * Set all entries to their - * default value (i.e. copy them - * over with default constructed - * objects). Do not change the - * size of the table, though. - */ - void reset_values (); - - /** - * Set the dimensions of this object to - * the sizes given in the argument, and - * newly allocate the required - * memory. If fast is set to - * false, previous content is - * deleted, otherwise the memory is not - * touched. - */ - void reinit (const TableIndices &new_size, - const bool fast = false); - - /** - * Size of the table in direction - * i. - */ - unsigned int size (const unsigned int i) const; - - /** - * Return the sizes of this - * object in each direction. - */ - const TableIndices & size () const; - - /** - * Return the number of elements - * stored in this object, which - * is the product of the - * extensions in each dimension. - */ - unsigned int n_elements () const; - - /** - * Return whether the object is - * empty, i.e. one of the - * directions is zero. This is - * equivalent to - * n_elements()==0. - */ - bool empty () const; - - /** - * Fill array with an array of - * elements. The input array must - * be arranged in usual C style, - * i.e. with the last index - * running fastest. For - * two-dimensional tables, this - * means line by line. No range - * checking is performed, i.e., - * it is assumed that the input - * array entries contains - * n_rows()*n_cols() - * elements, and that the layout - * refers to the desired shape of - * this table. The only check we - * do is that the present array - * is non-empty. - * - * Note also that the type of the - * objects of the input array, - * T2, must be convertible to - * the type of the objects of - * this array. - */ - template - void fill (const T2 *entries); - - /** - * Fill all table entries with - * the same value. - */ - void fill (const T& value); - - /** - * Return a read-write reference - * to the indicated element. - */ - typename std::vector::reference - operator () (const TableIndices &indices); - - /** - * Return the value of the - * indicated element as a - * read-only reference. - * - * We return the requested value - * as a constant reference rather - * than by value since this - * object may hold data types - * that may be large, and we - * don't know here whether - * copying is expensive or not. - */ - typename std::vector::const_reference - operator () (const TableIndices &indices) const; - - /** - * Determine an estimate for the - * memory consumption (in bytes) - * of this object. - */ - std::size_t memory_consumption () const; - - /** - * Write or read the data of this object to or - * from a stream for the purpose of serialization. - */ - template - void serialize (Archive & ar, const unsigned int version); - - protected: - /** - * Return the position of the - * indicated element within the - * array of elements stored one - * after the other. This function - * does no index checking. - */ - unsigned int position (const TableIndices &indices) const; - - /** - * Return a read-write reference - * to the indicated element. - * - * This function does no bounds - * checking and is only to be - * used internally and in - * functions already checked. - */ - typename std::vector::reference el (const TableIndices &indices); - - /** - * Return the value of the - * indicated element as a - * read-only reference. - * - * This function does no bounds - * checking and is only to be - * used internally and in - * functions already checked. - * - * We return the requested value - * as a constant reference rather - * than by value since this - * object may hold data types - * that may be large, and we - * don't know here whether - * copying is expensive or not. - */ - typename std::vector::const_reference el (const TableIndices &indices) const; - - /** - * @deprecated This function - * accesses data directly and - * should not be used! - * - * Direct read-only access to - * data field. Used by - * FullMatrix of the LAC - * sublibrary (there even with a - * cast from const), otherwise, - * keep away! - */ - typename std::vector::const_pointer data () const; - - protected: - /** - * Component-array. - */ - std::vector values; - - /** - * Size in each direction of the - * table. - */ - TableIndices table_size; - - /** - * Make all other table classes - * friends. - */ - template friend class TableBase; +public: + /** + * Default constructor. Set all + * dimensions to zero. + */ + TableBase (); + + /** + * Constructor. Initialize the + * array with the given + * dimensions in each index + * component. + */ + TableBase (const TableIndices &sizes); + + /** + * Copy constructor. Performs a + * deep copy. + */ + TableBase (const TableBase &src); + + /** + * Copy constructor. Performs a + * deep copy from a table object + * storing some other data type. + */ + template + TableBase (const TableBase &src); + + /** + * Destructor. Free allocated memory. + */ + ~TableBase (); + + /** + * Assignment operator. + * Copy all elements of src + * into the matrix. The size is + * adjusted if needed. + * + * We can't use the other, templatized + * version since if we don't declare + * this one, the compiler will happily + * generate a predefined copy + * operator which is not what we want. + */ + TableBase &operator = (const TableBase &src); + + /** + * Copy operator. + * Copy all elements of src + * into the array. The size is + * adjusted if needed. + * + * This function requires that the + * type T2 is convertible to + * T. + */ + template + TableBase &operator = (const TableBase &src); + + /** + * Test for equality of two tables. + */ + bool operator == (const TableBase &T2) const; + + /** + * Set all entries to their + * default value (i.e. copy them + * over with default constructed + * objects). Do not change the + * size of the table, though. + */ + void reset_values (); + + /** + * Set the dimensions of this object to + * the sizes given in the argument, and + * newly allocate the required + * memory. If fast is set to + * false, previous content is + * deleted, otherwise the memory is not + * touched. + */ + void reinit (const TableIndices &new_size, + const bool fast = false); + + /** + * Size of the table in direction + * i. + */ + unsigned int size (const unsigned int i) const; + + /** + * Return the sizes of this + * object in each direction. + */ + const TableIndices &size () const; + + /** + * Return the number of elements + * stored in this object, which + * is the product of the + * extensions in each dimension. + */ + unsigned int n_elements () const; + + /** + * Return whether the object is + * empty, i.e. one of the + * directions is zero. This is + * equivalent to + * n_elements()==0. + */ + bool empty () const; + + /** + * Fill array with an array of + * elements. The input array must + * be arranged in usual C style, + * i.e. with the last index + * running fastest. For + * two-dimensional tables, this + * means line by line. No range + * checking is performed, i.e., + * it is assumed that the input + * array entries contains + * n_rows()*n_cols() + * elements, and that the layout + * refers to the desired shape of + * this table. The only check we + * do is that the present array + * is non-empty. + * + * Note also that the type of the + * objects of the input array, + * T2, must be convertible to + * the type of the objects of + * this array. + */ + template + void fill (const T2 *entries); + + /** + * Fill all table entries with + * the same value. + */ + void fill (const T &value); + + /** + * Return a read-write reference + * to the indicated element. + */ + typename std::vector::reference + operator () (const TableIndices &indices); + + /** + * Return the value of the + * indicated element as a + * read-only reference. + * + * We return the requested value + * as a constant reference rather + * than by value since this + * object may hold data types + * that may be large, and we + * don't know here whether + * copying is expensive or not. + */ + typename std::vector::const_reference + operator () (const TableIndices &indices) const; + + /** + * Determine an estimate for the + * memory consumption (in bytes) + * of this object. + */ + std::size_t memory_consumption () const; + + /** + * Write or read the data of this object to or + * from a stream for the purpose of serialization. + */ + template + void serialize (Archive &ar, const unsigned int version); + +protected: + /** + * Return the position of the + * indicated element within the + * array of elements stored one + * after the other. This function + * does no index checking. + */ + unsigned int position (const TableIndices &indices) const; + + /** + * Return a read-write reference + * to the indicated element. + * + * This function does no bounds + * checking and is only to be + * used internally and in + * functions already checked. + */ + typename std::vector::reference el (const TableIndices &indices); + + /** + * Return the value of the + * indicated element as a + * read-only reference. + * + * This function does no bounds + * checking and is only to be + * used internally and in + * functions already checked. + * + * We return the requested value + * as a constant reference rather + * than by value since this + * object may hold data types + * that may be large, and we + * don't know here whether + * copying is expensive or not. + */ + typename std::vector::const_reference el (const TableIndices &indices) const; + + /** + * @deprecated This function + * accesses data directly and + * should not be used! + * + * Direct read-only access to + * data field. Used by + * FullMatrix of the LAC + * sublibrary (there even with a + * cast from const), otherwise, + * keep away! + */ + typename std::vector::const_pointer data () const; + +protected: + /** + * Component-array. + */ + std::vector values; + + /** + * Size in each direction of the + * table. + */ + TableIndices table_size; + + /** + * Make all other table classes + * friends. + */ + template friend class TableBase; }; @@ -758,77 +758,77 @@ class Table : public TableBase template class Table<1,T> : public TableBase<1,T> { - public: - /** - * Default constructor. Set all - * dimensions to zero. - */ - Table (); - - /** - * Constructor. Pass down the - * given dimension to the base - * class. - */ - Table (const unsigned int size); - - /** - * Access operator. Since this is - * a one-dimensional object, this - * simply accesses the requested - * data element. Returns a - * read-only reference. - */ - typename std::vector::const_reference - operator [] (const unsigned int i) const; - - /** - * Access operator. Since this is - * a one-dimensional object, this - * simply accesses the requested - * data element. Returns a - * read-write reference. - */ - typename std::vector::reference - operator [] (const unsigned int i); - - /** - * Access operator. Since this is - * a one-dimensional object, this - * simply accesses the requested - * data element. Returns a - * read-only reference. - */ - typename std::vector::const_reference - operator () (const unsigned int i) const; - - /** - * Access operator. Since this is - * a one-dimensional object, this - * simply accesses the requested - * data element. Returns a - * read-write reference. - */ - typename std::vector::reference - operator () (const unsigned int i); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::reference - operator () (const TableIndices<1> &indices); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::const_reference - operator () (const TableIndices<1> &indices) const; +public: + /** + * Default constructor. Set all + * dimensions to zero. + */ + Table (); + + /** + * Constructor. Pass down the + * given dimension to the base + * class. + */ + Table (const unsigned int size); + + /** + * Access operator. Since this is + * a one-dimensional object, this + * simply accesses the requested + * data element. Returns a + * read-only reference. + */ + typename std::vector::const_reference + operator [] (const unsigned int i) const; + + /** + * Access operator. Since this is + * a one-dimensional object, this + * simply accesses the requested + * data element. Returns a + * read-write reference. + */ + typename std::vector::reference + operator [] (const unsigned int i); + + /** + * Access operator. Since this is + * a one-dimensional object, this + * simply accesses the requested + * data element. Returns a + * read-only reference. + */ + typename std::vector::const_reference + operator () (const unsigned int i) const; + + /** + * Access operator. Since this is + * a one-dimensional object, this + * simply accesses the requested + * data element. Returns a + * read-write reference. + */ + typename std::vector::reference + operator () (const unsigned int i); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::reference + operator () (const TableIndices<1> &indices); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::const_reference + operator () (const TableIndices<1> &indices) const; }; @@ -849,168 +849,168 @@ class Table<1,T> : public TableBase<1,T> template class Table<2,T> : public TableBase<2,T> { - public: - /** - * Default constructor. Set all - * dimensions to zero. - */ - Table (); - - /** - * Constructor. Pass down the - * given dimensions to the base - * class. - */ - Table (const unsigned int size1, - const unsigned int size2); - - /** - * Reinitialize the object. This - * function is mostly here for - * compatibility with the earlier - * vector2d class. Passes - * down to the base class by - * converting the arguments to - * the data type requested by the - * base class. - */ - void reinit (const unsigned int size1, - const unsigned int size2, - const bool fast = false); - - /** - * Access operator. Generate an - * object that accesses the - * requested row of this - * two-dimensional table. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - dealii::internal::TableBaseAccessors::Accessor<2,T,true,1> - operator [] (const unsigned int i) const; - - /** - * Access operator. Generate an - * object that accesses the - * requested row of this - * two-dimensional table. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - dealii::internal::TableBaseAccessors::Accessor<2,T,false,1> - operator [] (const unsigned int i); - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - typename std::vector::const_reference - operator () (const unsigned int i, - const unsigned int j) const; - - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - typename std::vector::reference - operator () (const unsigned int i, - const unsigned int j); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::reference - operator () (const TableIndices<2> &indices); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::const_reference - operator () (const TableIndices<2> &indices) const; - - - /** - * Number of rows. This function - * really makes only sense since - * we have a two-dimensional - * object here. - */ - unsigned int n_rows () const; - - /** - * Number of columns. This function - * really makes only sense since - * we have a two-dimensional - * object here. - */ - unsigned int n_cols () const; - - protected: - /** - * Return a read-write reference - * to the element (i,j). - * - * This function does no bounds - * checking and is only to be - * used internally and in - * functions already checked. - * - * These functions are mainly - * here for compatibility with a - * former implementation of these - * table classes for 2d arrays, - * then called vector2d. - */ - typename std::vector::reference el (const unsigned int i, - const unsigned int j); - - /** - * Return the value of the - * element (i,j) as a - * read-only reference. - * - * This function does no bounds - * checking and is only to be - * used internally and in - * functions already checked. - * - * We return the requested value - * as a constant reference rather - * than by value since this - * object may hold data types - * that may be large, and we - * don't know here whether - * copying is expensive or not. - * - * These functions are mainly - * here for compatibility with a - * former implementation of these - * table classes for 2d arrays, - * then called vector2d. - */ - typename std::vector::const_reference el (const unsigned int i, - const unsigned int j) const; +public: + /** + * Default constructor. Set all + * dimensions to zero. + */ + Table (); + + /** + * Constructor. Pass down the + * given dimensions to the base + * class. + */ + Table (const unsigned int size1, + const unsigned int size2); + + /** + * Reinitialize the object. This + * function is mostly here for + * compatibility with the earlier + * vector2d class. Passes + * down to the base class by + * converting the arguments to + * the data type requested by the + * base class. + */ + void reinit (const unsigned int size1, + const unsigned int size2, + const bool fast = false); + + /** + * Access operator. Generate an + * object that accesses the + * requested row of this + * two-dimensional table. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + dealii::internal::TableBaseAccessors::Accessor<2,T,true,1> + operator [] (const unsigned int i) const; + + /** + * Access operator. Generate an + * object that accesses the + * requested row of this + * two-dimensional table. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + dealii::internal::TableBaseAccessors::Accessor<2,T,false,1> + operator [] (const unsigned int i); + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + typename std::vector::const_reference + operator () (const unsigned int i, + const unsigned int j) const; + + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + typename std::vector::reference + operator () (const unsigned int i, + const unsigned int j); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::reference + operator () (const TableIndices<2> &indices); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::const_reference + operator () (const TableIndices<2> &indices) const; + + + /** + * Number of rows. This function + * really makes only sense since + * we have a two-dimensional + * object here. + */ + unsigned int n_rows () const; + + /** + * Number of columns. This function + * really makes only sense since + * we have a two-dimensional + * object here. + */ + unsigned int n_cols () const; + +protected: + /** + * Return a read-write reference + * to the element (i,j). + * + * This function does no bounds + * checking and is only to be + * used internally and in + * functions already checked. + * + * These functions are mainly + * here for compatibility with a + * former implementation of these + * table classes for 2d arrays, + * then called vector2d. + */ + typename std::vector::reference el (const unsigned int i, + const unsigned int j); + + /** + * Return the value of the + * element (i,j) as a + * read-only reference. + * + * This function does no bounds + * checking and is only to be + * used internally and in + * functions already checked. + * + * We return the requested value + * as a constant reference rather + * than by value since this + * object may hold data types + * that may be large, and we + * don't know here whether + * copying is expensive or not. + * + * These functions are mainly + * here for compatibility with a + * former implementation of these + * table classes for 2d arrays, + * then called vector2d. + */ + typename std::vector::const_reference el (const unsigned int i, + const unsigned int j) const; }; @@ -1028,92 +1028,92 @@ class Table<2,T> : public TableBase<2,T> template class Table<3,T> : public TableBase<3,T> { - public: - /** - * Default constructor. Set all - * dimensions to zero. - */ - Table (); - - /** - * Constructor. Pass down the - * given dimensions to the base - * class. - */ - Table (const unsigned int size1, - const unsigned int size2, - const unsigned int size3); - - /** - * Access operator. Generate an - * object that accesses the - * requested two-dimensional - * subobject of this - * three-dimensional table. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - dealii::internal::TableBaseAccessors::Accessor<3,T,true,2> - operator [] (const unsigned int i) const; - - /** - * Access operator. Generate an - * object that accesses the - * requested two-dimensional - * subobject of this - * three-dimensional table. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - dealii::internal::TableBaseAccessors::Accessor<3,T,false,2> - operator [] (const unsigned int i); - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - typename std::vector::const_reference operator () (const unsigned int i, - const unsigned int j, - const unsigned int k) const; - - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - typename std::vector::reference operator () (const unsigned int i, - const unsigned int j, - const unsigned int k); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::reference operator () (const TableIndices<3> &indices); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::const_reference operator () (const TableIndices<3> &indices) const; +public: + /** + * Default constructor. Set all + * dimensions to zero. + */ + Table (); + + /** + * Constructor. Pass down the + * given dimensions to the base + * class. + */ + Table (const unsigned int size1, + const unsigned int size2, + const unsigned int size3); + + /** + * Access operator. Generate an + * object that accesses the + * requested two-dimensional + * subobject of this + * three-dimensional table. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + dealii::internal::TableBaseAccessors::Accessor<3,T,true,2> + operator [] (const unsigned int i) const; + + /** + * Access operator. Generate an + * object that accesses the + * requested two-dimensional + * subobject of this + * three-dimensional table. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + dealii::internal::TableBaseAccessors::Accessor<3,T,false,2> + operator [] (const unsigned int i); + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + typename std::vector::const_reference operator () (const unsigned int i, + const unsigned int j, + const unsigned int k) const; + + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + typename std::vector::reference operator () (const unsigned int i, + const unsigned int j, + const unsigned int k); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::reference operator () (const TableIndices<3> &indices); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::const_reference operator () (const TableIndices<3> &indices) const; }; @@ -1131,97 +1131,97 @@ class Table<3,T> : public TableBase<3,T> template class Table<4,T> : public TableBase<4,T> { - public: - /** - * Default constructor. Set all - * dimensions to zero. - */ - Table (); - - /** - * Constructor. Pass down the - * given dimensions to the base - * class. - */ - Table (const unsigned int size1, - const unsigned int size2, - const unsigned int size3, - const unsigned int size4); - - /** - * Access operator. Generate an - * object that accesses the - * requested three-dimensional - * subobject of this - * four-dimensional table. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - dealii::internal::TableBaseAccessors::Accessor<4,T,true,3> - operator [] (const unsigned int i) const; - - /** - * Access operator. Generate an - * object that accesses the - * requested three-dimensional - * subobject of this - * four-dimensional table. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - dealii::internal::TableBaseAccessors::Accessor<4,T,false,3> - operator [] (const unsigned int i); - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - typename std::vector::const_reference operator () (const unsigned int i, - const unsigned int j, - const unsigned int k, - const unsigned int l) const; - - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - typename std::vector::reference operator () (const unsigned int i, - const unsigned int j, - const unsigned int k, - const unsigned int l); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::reference - operator () (const TableIndices<4> &indices); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::const_reference - operator () (const TableIndices<4> &indices) const; +public: + /** + * Default constructor. Set all + * dimensions to zero. + */ + Table (); + + /** + * Constructor. Pass down the + * given dimensions to the base + * class. + */ + Table (const unsigned int size1, + const unsigned int size2, + const unsigned int size3, + const unsigned int size4); + + /** + * Access operator. Generate an + * object that accesses the + * requested three-dimensional + * subobject of this + * four-dimensional table. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + dealii::internal::TableBaseAccessors::Accessor<4,T,true,3> + operator [] (const unsigned int i) const; + + /** + * Access operator. Generate an + * object that accesses the + * requested three-dimensional + * subobject of this + * four-dimensional table. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + dealii::internal::TableBaseAccessors::Accessor<4,T,false,3> + operator [] (const unsigned int i); + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + typename std::vector::const_reference operator () (const unsigned int i, + const unsigned int j, + const unsigned int k, + const unsigned int l) const; + + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + typename std::vector::reference operator () (const unsigned int i, + const unsigned int j, + const unsigned int k, + const unsigned int l); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::reference + operator () (const TableIndices<4> &indices); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::const_reference + operator () (const TableIndices<4> &indices) const; }; @@ -1239,99 +1239,99 @@ class Table<4,T> : public TableBase<4,T> template class Table<5,T> : public TableBase<5,T> { - public: - /** - * Default constructor. Set all - * dimensions to zero. - */ - Table (); - - /** - * Constructor. Pass down the - * given dimensions to the base - * class. - */ - Table (const unsigned int size1, - const unsigned int size2, - const unsigned int size3, - const unsigned int size4, - const unsigned int size5); - - /** - * Access operator. Generate an - * object that accesses the - * requested four-dimensional - * subobject of this - * five-dimensional table. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - dealii::internal::TableBaseAccessors::Accessor<5,T,true,4> - operator [] (const unsigned int i) const; - - /** - * Access operator. Generate an - * object that accesses the - * requested four-dimensional - * subobject of this - * five-dimensional table. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - dealii::internal::TableBaseAccessors::Accessor<5,T,false,4> - operator [] (const unsigned int i); - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - typename std::vector::const_reference operator () (const unsigned int i, - const unsigned int j, - const unsigned int k, - const unsigned int l, - const unsigned int m) const; - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - typename std::vector::reference operator () (const unsigned int i, - const unsigned int j, - const unsigned int k, - const unsigned int l, - const unsigned int m); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::reference - operator () (const TableIndices<5> &indices); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::const_reference - operator () (const TableIndices<5> &indices) const; +public: + /** + * Default constructor. Set all + * dimensions to zero. + */ + Table (); + + /** + * Constructor. Pass down the + * given dimensions to the base + * class. + */ + Table (const unsigned int size1, + const unsigned int size2, + const unsigned int size3, + const unsigned int size4, + const unsigned int size5); + + /** + * Access operator. Generate an + * object that accesses the + * requested four-dimensional + * subobject of this + * five-dimensional table. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + dealii::internal::TableBaseAccessors::Accessor<5,T,true,4> + operator [] (const unsigned int i) const; + + /** + * Access operator. Generate an + * object that accesses the + * requested four-dimensional + * subobject of this + * five-dimensional table. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + dealii::internal::TableBaseAccessors::Accessor<5,T,false,4> + operator [] (const unsigned int i); + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + typename std::vector::const_reference operator () (const unsigned int i, + const unsigned int j, + const unsigned int k, + const unsigned int l, + const unsigned int m) const; + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + typename std::vector::reference operator () (const unsigned int i, + const unsigned int j, + const unsigned int k, + const unsigned int l, + const unsigned int m); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::reference + operator () (const TableIndices<5> &indices); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::const_reference + operator () (const TableIndices<5> &indices) const; }; @@ -1349,102 +1349,102 @@ class Table<5,T> : public TableBase<5,T> template class Table<6,T> : public TableBase<6,T> { - public: - /** - * Default constructor. Set all - * dimensions to zero. - */ - Table (); - - /** - * Constructor. Pass down the - * given dimensions to the base - * class. - */ - Table (const unsigned int size1, - const unsigned int size2, - const unsigned int size3, - const unsigned int size4, - const unsigned int size5, - const unsigned int size6); - - /** - * Access operator. Generate an - * object that accesses the - * requested five-dimensional - * subobject of this - * six-dimensional table. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - dealii::internal::TableBaseAccessors::Accessor<6,T,true,5> - operator [] (const unsigned int i) const; - - /** - * Access operator. Generate an - * object that accesses the - * requested five-dimensional - * subobject of this - * six-dimensional table. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - dealii::internal::TableBaseAccessors::Accessor<6,T,false,5> - operator [] (const unsigned int i); - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - typename std::vector::const_reference operator () (const unsigned int i, - const unsigned int j, - const unsigned int k, - const unsigned int l, - const unsigned int m, - const unsigned int n) const; - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - typename std::vector::reference operator () (const unsigned int i, - const unsigned int j, - const unsigned int k, - const unsigned int l, - const unsigned int m, - const unsigned int n); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::reference - operator () (const TableIndices<6> &indices); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::const_reference - operator () (const TableIndices<6> &indices) const; +public: + /** + * Default constructor. Set all + * dimensions to zero. + */ + Table (); + + /** + * Constructor. Pass down the + * given dimensions to the base + * class. + */ + Table (const unsigned int size1, + const unsigned int size2, + const unsigned int size3, + const unsigned int size4, + const unsigned int size5, + const unsigned int size6); + + /** + * Access operator. Generate an + * object that accesses the + * requested five-dimensional + * subobject of this + * six-dimensional table. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + dealii::internal::TableBaseAccessors::Accessor<6,T,true,5> + operator [] (const unsigned int i) const; + + /** + * Access operator. Generate an + * object that accesses the + * requested five-dimensional + * subobject of this + * six-dimensional table. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + dealii::internal::TableBaseAccessors::Accessor<6,T,false,5> + operator [] (const unsigned int i); + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + typename std::vector::const_reference operator () (const unsigned int i, + const unsigned int j, + const unsigned int k, + const unsigned int l, + const unsigned int m, + const unsigned int n) const; + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + typename std::vector::reference operator () (const unsigned int i, + const unsigned int j, + const unsigned int k, + const unsigned int l, + const unsigned int m, + const unsigned int n); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::reference + operator () (const TableIndices<6> &indices); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::const_reference + operator () (const TableIndices<6> &indices) const; }; @@ -1461,105 +1461,105 @@ class Table<6,T> : public TableBase<6,T> template class Table<7,T> : public TableBase<7,T> { - public: - /** - * Default constructor. Set all - * dimensions to zero. - */ - Table (); - - /** - * Constructor. Pass down the - * given dimensions to the base - * class. - */ - Table (const unsigned int size1, - const unsigned int size2, - const unsigned int size3, - const unsigned int size4, - const unsigned int size5, - const unsigned int size6, - const unsigned int size7); - - /** - * Access operator. Generate an - * object that accesses the - * requested six-dimensional - * subobject of this - * seven-dimensional table. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - dealii::internal::TableBaseAccessors::Accessor<7,T,true,6> - operator [] (const unsigned int i) const; - - /** - * Access operator. Generate an - * object that accesses the - * requested six-dimensional - * subobject of this - * seven-dimensional table. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - dealii::internal::TableBaseAccessors::Accessor<7,T,false,6> - operator [] (const unsigned int i); - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - typename std::vector::const_reference operator () (const unsigned int i, - const unsigned int j, - const unsigned int k, - const unsigned int l, - const unsigned int m, - const unsigned int n, - const unsigned int o) const; - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - typename std::vector::reference operator () (const unsigned int i, - const unsigned int j, - const unsigned int k, - const unsigned int l, - const unsigned int m, - const unsigned int n, - const unsigned int o); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::reference - operator () (const TableIndices<7> &indices); - - /** - * Make the corresponding - * operator () from the TableBase - * base class available also in - * this class. - */ - typename std::vector::const_reference - operator () (const TableIndices<7> &indices) const; +public: + /** + * Default constructor. Set all + * dimensions to zero. + */ + Table (); + + /** + * Constructor. Pass down the + * given dimensions to the base + * class. + */ + Table (const unsigned int size1, + const unsigned int size2, + const unsigned int size3, + const unsigned int size4, + const unsigned int size5, + const unsigned int size6, + const unsigned int size7); + + /** + * Access operator. Generate an + * object that accesses the + * requested six-dimensional + * subobject of this + * seven-dimensional table. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + dealii::internal::TableBaseAccessors::Accessor<7,T,true,6> + operator [] (const unsigned int i) const; + + /** + * Access operator. Generate an + * object that accesses the + * requested six-dimensional + * subobject of this + * seven-dimensional table. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + dealii::internal::TableBaseAccessors::Accessor<7,T,false,6> + operator [] (const unsigned int i); + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + typename std::vector::const_reference operator () (const unsigned int i, + const unsigned int j, + const unsigned int k, + const unsigned int l, + const unsigned int m, + const unsigned int n, + const unsigned int o) const; + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + typename std::vector::reference operator () (const unsigned int i, + const unsigned int j, + const unsigned int k, + const unsigned int l, + const unsigned int m, + const unsigned int n, + const unsigned int o); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::reference + operator () (const TableIndices<7> &indices); + + /** + * Make the corresponding + * operator () from the TableBase + * base class available also in + * this class. + */ + typename std::vector::const_reference + operator () (const TableIndices<7> &indices) const; }; @@ -1580,120 +1580,120 @@ class Table<7,T> : public TableBase<7,T> template class TransposeTable : public TableBase<2,T> { - public: - /** - * Default constructor. Set all - * dimensions to zero. - */ - TransposeTable (); - - /** - * Constructor. Pass down the - * given dimensions to the base - * class. - */ - TransposeTable (const unsigned int size1, - const unsigned int size2); - - /** - * Reinitialize the object. This - * function is mostly here for - * compatibility with the earlier - * vector2d class. Passes - * down to the base class by - * converting the arguments to - * the data type requested by the - * base class. - */ - void reinit (const unsigned int size1, - const unsigned int size2, - const bool fast = false); - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * only allows read access. - */ - typename std::vector::const_reference operator () (const unsigned int i, - const unsigned int j) const; - - /** - * Direct access to one element - * of the table by specifying all - * indices at the same time. Range - * checks are performed. - * - * This version of the function - * allows read-write access. - */ - typename std::vector::reference operator () (const unsigned int i, - const unsigned int j); - - /** - * Number of rows. This function - * really makes only sense since - * we have a two-dimensional - * object here. - */ - unsigned int n_rows () const; - - /** - * Number of columns. This function - * really makes only sense since - * we have a two-dimensional - * object here. - */ - unsigned int n_cols () const; - - protected: - /** - * Return a read-write reference - * to the element (i,j). - * - * This function does no bounds - * checking and is only to be - * used internally and in - * functions already checked. - * - * These functions are mainly - * here for compatibility with a - * former implementation of these - * table classes for 2d arrays, - * then called vector2d. - */ - typename std::vector::reference el (const unsigned int i, - const unsigned int j); - - /** - * Return the value of the - * element (i,j) as a - * read-only reference. - * - * This function does no bounds - * checking and is only to be - * used internally and in - * functions already checked. - * - * We return the requested value - * as a constant reference rather - * than by value since this - * object may hold data types - * that may be large, and we - * don't know here whether - * copying is expensive or not. - * - * These functions are mainly - * here for compatibility with a - * former implementation of these - * table classes for 2d arrays, - * then called vector2d. - */ - typename std::vector::const_reference el (const unsigned int i, - const unsigned int j) const; +public: + /** + * Default constructor. Set all + * dimensions to zero. + */ + TransposeTable (); + + /** + * Constructor. Pass down the + * given dimensions to the base + * class. + */ + TransposeTable (const unsigned int size1, + const unsigned int size2); + + /** + * Reinitialize the object. This + * function is mostly here for + * compatibility with the earlier + * vector2d class. Passes + * down to the base class by + * converting the arguments to + * the data type requested by the + * base class. + */ + void reinit (const unsigned int size1, + const unsigned int size2, + const bool fast = false); + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * only allows read access. + */ + typename std::vector::const_reference operator () (const unsigned int i, + const unsigned int j) const; + + /** + * Direct access to one element + * of the table by specifying all + * indices at the same time. Range + * checks are performed. + * + * This version of the function + * allows read-write access. + */ + typename std::vector::reference operator () (const unsigned int i, + const unsigned int j); + + /** + * Number of rows. This function + * really makes only sense since + * we have a two-dimensional + * object here. + */ + unsigned int n_rows () const; + + /** + * Number of columns. This function + * really makes only sense since + * we have a two-dimensional + * object here. + */ + unsigned int n_cols () const; + +protected: + /** + * Return a read-write reference + * to the element (i,j). + * + * This function does no bounds + * checking and is only to be + * used internally and in + * functions already checked. + * + * These functions are mainly + * here for compatibility with a + * former implementation of these + * table classes for 2d arrays, + * then called vector2d. + */ + typename std::vector::reference el (const unsigned int i, + const unsigned int j); + + /** + * Return the value of the + * element (i,j) as a + * read-only reference. + * + * This function does no bounds + * checking and is only to be + * used internally and in + * functions already checked. + * + * We return the requested value + * as a constant reference rather + * than by value since this + * object may hold data types + * that may be large, and we + * don't know here whether + * copying is expensive or not. + * + * These functions are mainly + * here for compatibility with a + * former implementation of these + * table classes for 2d arrays, + * then called vector2d. + */ + typename std::vector::const_reference el (const unsigned int i, + const unsigned int j) const; }; @@ -1719,8 +1719,8 @@ TableBase::TableBase (const TableIndices &sizes) template TableBase::TableBase (const TableBase &src) - : - Subscriptor () + : + Subscriptor () { reinit (src.table_size); if (src.n_elements() != 0) @@ -1744,11 +1744,11 @@ template template inline void -TableBase::serialize (Archive & ar, const unsigned int) +TableBase::serialize (Archive &ar, const unsigned int) { - ar & static_cast(*this); + ar &static_cast(*this); - ar & values & table_size; + ar &values &table_size; } @@ -1761,9 +1761,9 @@ namespace internal inline Accessor::Accessor (const TableType &table, const iterator data) - : - table (table), - data (data) + : + table (table), + data (data) {} @@ -1771,9 +1771,9 @@ namespace internal template inline Accessor::Accessor (const Accessor &a) - : - table (a.table), - data (a.data) + : + table (a.table), + data (a.data) {} @@ -1781,13 +1781,13 @@ namespace internal template inline Accessor::Accessor () - : - table (*static_cast(0)), - data (0) + : + table (*static_cast(0)), + data (0) { - // accessor objects are only - // temporary objects, so should - // not need to be copied around + // accessor objects are only + // temporary objects, so should + // not need to be copied around Assert (false, ExcInternalError()); } @@ -1801,16 +1801,16 @@ namespace internal Assert (i < table.size()[N-P], ExcIndexRange (i, 0, table.size()[N-P])); - // access i-th - // subobject. optimize on the - // case i==0 + // access i-th + // subobject. optimize on the + // case i==0 if (i==0) return Accessor (table, data); else { - // note: P>1, otherwise the - // specialization would have - // been taken! + // note: P>1, otherwise the + // specialization would have + // been taken! unsigned int subobject_size = table.size()[N-1]; for (int p=P-1; p>1; --p) subobject_size *= table.size()[N-p]; @@ -1825,9 +1825,9 @@ namespace internal inline Accessor::Accessor (const TableType &table, const iterator data) - : - table (table), - data (data) + : + table (table), + data (data) {} @@ -1835,13 +1835,13 @@ namespace internal template inline Accessor::Accessor () - : - table (*static_cast(0)), - data (0) + : + table (*static_cast(0)), + data (0) { - // accessor objects are only - // temporary objects, so should - // not need to be copied around + // accessor objects are only + // temporary objects, so should + // not need to be copied around Assert (false, ExcInternalError()); } @@ -1850,9 +1850,9 @@ namespace internal template inline Accessor::Accessor (const Accessor &a) - : - table (a.table), - data (a.data) + : + table (a.table), + data (a.data) {} @@ -1910,8 +1910,8 @@ TableBase::~TableBase () template inline -TableBase& -TableBase::operator = (const TableBase& m) +TableBase & +TableBase::operator = (const TableBase &m) { reinit (m.size()); if (!empty()) @@ -1925,8 +1925,8 @@ TableBase::operator = (const TableBase& m) template template inline -TableBase& -TableBase::operator = (const TableBase& m) +TableBase & +TableBase::operator = (const TableBase &m) { reinit (m.size()); if (!empty()) @@ -1940,7 +1940,7 @@ TableBase::operator = (const TableBase& m) template inline bool -TableBase::operator == (const TableBase & T2) const +TableBase::operator == (const TableBase &T2) const { return (values == T2.values); } @@ -1961,7 +1961,7 @@ TableBase::reset_values () template inline void -TableBase::fill (const T& value) +TableBase::fill (const T &value) { if (n_elements() != 0) std::fill (values.begin(), values.end(), value); @@ -1980,39 +1980,39 @@ TableBase::reinit (const TableIndices &new_sizes, const unsigned int new_size = n_elements(); - // if zero size was given: free all - // memory + // if zero size was given: free all + // memory if (new_size == 0) { values.resize (0); - // set all sizes to zero, even - // if one was previously - // nonzero. This simplifies - // some assertions. + // set all sizes to zero, even + // if one was previously + // nonzero. This simplifies + // some assertions. table_size = TableIndices(); return; } - // if new size is nonzero: - // if necessary allocate - // additional memory + // if new size is nonzero: + // if necessary allocate + // additional memory values.resize (new_size); - // reinitialize contents of old or - // new memory. note that we - // actually need to do this here, - // even in the case that we - // reallocated memory, since per - // C++ standard, clause 5.3.4/15 - // the newly allocated objects are - // only default initialized by - // operator new[] if they are - // non-POD type. In other words, if - // we have a table of doubles, then - // their values after calling 'new - // double[val_size]' is - // indetermined. + // reinitialize contents of old or + // new memory. note that we + // actually need to do this here, + // even in the case that we + // reallocated memory, since per + // C++ standard, clause 5.3.4/15 + // the newly allocated objects are + // only default initialized by + // operator new[] if they are + // non-POD type. In other words, if + // we have a table of doubles, then + // their values after calling 'new + // double[val_size]' is + // indetermined. if (fast == false) reset_values (); } @@ -2067,7 +2067,7 @@ template template inline void -TableBase::fill (const T2* entries) +TableBase::fill (const T2 *entries) { Assert (n_elements() != 0, ExcMessage("Trying to fill an empty matrix.")); @@ -2092,27 +2092,27 @@ inline unsigned int TableBase::position (const TableIndices &indices) const { - // specialize this for the - // different numbers of dimensions, - // to make the job somewhat easier - // for the compiler. have the - // general formula nevertheless: + // specialize this for the + // different numbers of dimensions, + // to make the job somewhat easier + // for the compiler. have the + // general formula nevertheless: switch (N) { - case 1: - return indices[0]; - case 2: - return indices[0]*table_size[1] + indices[1]; - case 3: - return ((indices[0]*table_size[1] + indices[1])*table_size[2] - + indices[2]); - default: - { - unsigned int s = indices[0]; - for (unsigned int n=1; n::Table () template inline Table<1,T>::Table (const unsigned int size) - : - TableBase<1,T> (TableIndices<1> (size)) + : + TableBase<1,T> (TableIndices<1> (size)) {} @@ -2276,8 +2276,8 @@ template inline Table<2,T>::Table (const unsigned int size1, const unsigned int size2) - : - TableBase<2,T> (TableIndices<2> (size1, size2)) + : + TableBase<2,T> (TableIndices<2> (size1, size2)) {} @@ -2302,7 +2302,7 @@ Table<2,T>::operator [] (const unsigned int i) const Assert (i < this->table_size[0], ExcIndexRange (i, 0, this->table_size[0])); return dealii::internal::TableBaseAccessors::Accessor<2,T,true,1>(*this, - this->values.begin()+i*n_cols()); + this->values.begin()+i*n_cols()); } @@ -2315,7 +2315,7 @@ Table<2,T>::operator [] (const unsigned int i) Assert (i < this->table_size[0], ExcIndexRange (i, 0, this->table_size[0])); return dealii::internal::TableBaseAccessors::Accessor<2,T,false,1>(*this, - this->values.begin()+i*n_cols()); + this->values.begin()+i*n_cols()); } @@ -2425,8 +2425,8 @@ template inline TransposeTable::TransposeTable (const unsigned int size1, const unsigned int size2) - : - TableBase<2,T> (TableIndices<2> (size2, size1)) + : + TableBase<2,T> (TableIndices<2> (size2, size1)) {} @@ -2530,8 +2530,8 @@ inline Table<3,T>::Table (const unsigned int size1, const unsigned int size2, const unsigned int size3) - : - TableBase<3,T> (TableIndices<3> (size1, size2, size3)) + : + TableBase<3,T> (TableIndices<3> (size1, size2, size3)) {} @@ -2639,8 +2639,8 @@ Table<4,T>::Table (const unsigned int size1, const unsigned int size2, const unsigned int size3, const unsigned int size4) - : - TableBase<4,T> (TableIndices<4> (size1, size2, size3, size4)) + : + TableBase<4,T> (TableIndices<4> (size1, size2, size3, size4)) {} @@ -2759,8 +2759,8 @@ Table<5,T>::Table (const unsigned int size1, const unsigned int size3, const unsigned int size4, const unsigned int size5) - : - TableBase<5,T> (TableIndices<5> (size1, size2, size3, size4, size5)) + : + TableBase<5,T> (TableIndices<5> (size1, size2, size3, size4, size5)) {} @@ -2890,8 +2890,8 @@ Table<6,T>::Table (const unsigned int size1, const unsigned int size4, const unsigned int size5, const unsigned int size6) - : - TableBase<6,T> (TableIndices<6> (size1, size2, size3, size4, size5, size6)) + : + TableBase<6,T> (TableIndices<6> (size1, size2, size3, size4, size5, size6)) {} @@ -3032,8 +3032,8 @@ Table<7,T>::Table (const unsigned int size1, const unsigned int size5, const unsigned int size6, const unsigned int size7) - : - TableBase<7,T> (TableIndices<7> (size1, size2, size3, size4, size5, size6, size7)) + : + TableBase<7,T> (TableIndices<7> (size1, size2, size3, size4, size5, size6, size7)) {} diff --git a/deal.II/include/deal.II/base/table_handler.h b/deal.II/include/deal.II/base/table_handler.h index 8b257f0d35..c760d35523 100644 --- a/deal.II/include/deal.II/base/table_handler.h +++ b/deal.II/include/deal.II/base/table_handler.h @@ -87,7 +87,7 @@ namespace internal /** * Return the value cached using cache_string(). This is just a wrapper around cached_value. */ - const std::string & get_cached_string() const; + const std::string &get_cached_string() const; /** @@ -104,7 +104,7 @@ namespace internal * serialization. */ template - void save (Archive & ar, const unsigned int version) const; + void save (Archive &ar, const unsigned int version) const; /** * Read the data of this object from a @@ -112,7 +112,7 @@ namespace internal * serialization. */ template - void load (Archive & ar, const unsigned int version); + void load (Archive &ar, const unsigned int version); BOOST_SERIALIZATION_SPLIT_MEMBER() @@ -258,495 +258,495 @@ namespace internal */ class TableHandler { - public: +public: + /** + * Set of options how a table should be formatted when output with + * the write_text() function. The following possibilities exist: + * + * - table_with_headers: The table is formatted in such a way + * that the contents are aligned under the key of each column, i.e. the + * key sits atop each column. This is suitable for tables with few columns + * where the entire table can be displayed on the screen. Output looks + * like this: + * @code + * key1 key2 key3 + * 0 0 "" + * 1 0 "" + * 2 13 a + * 1 0 "" + * @endcode + * - table_with_separate_column_description: This is a better + * format when there are many columns and the table as a whole can not + * be displayed on the screen. Here, the column keys are first listed + * one-by-one on lines of their own, and are numbered for better readability. + * In addition, each of these description lines are prefixed by '#' to mark + * these lines as comments for programs that want to read the following + * table as data and should ignore these descriptive lines. GNUPLOT is + * one such program that will automatically ignore lines so prefixed. + * Output with this option looks like this: + * @code + * # 1: key1 + * # 2: key2 + * # 3: key3 + * 0 0 "" + * 1 0 "" + * 2 13 a + * 1 0 "" + * @endcode + * - simple_table_with_separate_column_description: This format + * is very similar to table_with_separate_column_description, + * but it skips aligning the columns with additional white space. This + * increases the performance o fwrite_text() for large tables. Example output: + * @code + * # 1: key1 + * # 2: key2 + * # 3: key3 + * 0 0 "" + * 1 0 "" + * 2 13 a + * 1 0 "" + * @endcode + * + **/ + enum TextOutputFormat + { + table_with_headers, + table_with_separate_column_description, + simple_table_with_separate_column_description + }; + + /** + * Constructor. + */ + TableHandler (); + + /** + * Adds a column (if not yet existent) + * with the key key and adds the + * value of type T to the + * column. Values of type T must + * be convertible to one of int, + * unsigned int, double, + * std::string or a compiler error + * will result. + */ + template + void add_value (const std::string &key, + const T value); + + /** + * Switch auto-fill mode on or off. See the general documentation + * of this class for a description of what auto-fill mode does. + */ + void set_auto_fill_mode (const bool state); + + /** + * Creates a supercolumn (if not + * yet existent) and includes + * column to it. The keys of the + * column and the supercolumn are + * key and superkey, + * respectively. To merge two + * columns c1 and c2 to a + * supercolumn sc hence call + * add_column_to_supercolumn(c1,sc) + * and + * add_column_to_supercolumn(c2,sc). + * + * Concerning the order of the + * columns, the supercolumn + * replaces the first column that + * is added to the supercolumn. + * Within the supercolumn the + * order of output follows the + * order the columns are added to + * the supercolumn. + */ + void add_column_to_supercolumn (const std::string &key, + const std::string &superkey); + + /** + * Change the order of columns and + * supercolumns in the table. + * + * new_order includes the + * keys and superkeys of the + * columns and supercolumns in + * the order the user like to. + * If a superkey is included the + * keys of the subcolumns need + * not to be additionally + * mentioned in this vector. The + * order of subcolumns within a + * supercolumn is not changeable + * and keeps the order in which + * the columns are added to the + * supercolumn. + * + * This function may also be used + * to break big tables with too + * many columns into smaller + * ones. Call this function with + * the first e.g. five columns + * and then write_*. + * Afterwards call this function + * with the next e.g. five + * columns and again write_*, + * and so on. + */ + void set_column_order (const std::vector &new_order); + + /** + * Sets the precision + * e.g. double or float variables + * are written + * with. precision is the + * same as in calling + * out<. + */ + void set_precision (const std::string &key, + const unsigned int precision); + + /** + * Sets the + * scientific_flag. True + * means scientific, false means + * fixed point notation. + */ + void set_scientific (const std::string &key, + const bool scientific); + + /** + * Sets the caption of the column + * key for tex output. You + * may want to chose this + * different from key, if it + * contains formulas or similar + * constructs. + */ + void set_tex_caption (const std::string &key, + const std::string &tex_caption); + + /** + * Sets the tex caption of the entire + * table for tex output. + */ + void set_tex_table_caption (const std::string &table_caption); + + /** + * Sets the label of this + * table for tex output. + */ + void set_tex_table_label (const std::string &table_label); + + /** + * Sets the caption the the + * supercolumn superkey for + * tex output. You may want to + * chose this different from + * superkey, if it contains + * formulas or similar + * constructs. + */ + void set_tex_supercaption (const std::string &superkey, + const std::string &tex_supercaption); + + /** + * Sets the tex output format of + * a column, e.g. c, r, + * l, or p{3cm}. The + * default is c. Also if this + * function is not called for a + * column, the default is preset + * to be c. + */ + void set_tex_format (const std::string &key, + const std::string &format="c"); + + /** + * Write table as formatted text to the + * given stream. The text is formatted in + * such as way that it represents data as + * formatted columns of text. To avoid + * problems when reading these tables + * automatically, for example for + * postprocessing, if an entry in a cell + * of this table is empty (i.e. it has + * been created by calling the + * add_value() function with an empty + * string), then the entry of the table + * is printed as "". + * + * The second argument indicates how + * column keys are to be displayed. See + * the description of TextOutputFormat + * for more information + */ + void write_text (std::ostream &out, + const TextOutputFormat format = table_with_headers) const; + + /** + * Write table as a tex file. If + * with_header is set to false + * (it is true by default), then + * no "\documentclass{...}", + * "\begin{document}" and + * "\end{document}" are used. In + * this way the file can be + * included into an existing tex + * file using a command like + * "\input{table_file}". + */ + void write_tex (std::ostream &file, const bool with_header=true) const; + + /** + * Read or write the data of this + * object to or from a stream for + * the purpose of serialization. + */ + template + void serialize(Archive &ar, const unsigned int version); + + /** @addtogroup Exceptions + * @{ */ + + /** + * Exception + */ + DeclException1 (ExcColumnNotExistent, + std::string, + << "Column <" << arg1 << "> does not exist."); + + /** + * Exception + */ + DeclException1 (ExcSuperColumnNotExistent, + std::string, + << "Supercolumn <" << arg1 << "> does not exist."); + + /** + * Exception + */ + DeclException1 (ExcColumnOrSuperColumnNotExistent, + std::string, + << "Column or supercolumn <" << arg1 << "> does not exist."); + + /** + * Exception + */ + DeclException4 (ExcWrongNumberOfDataEntries, + std::string, int, std::string, int, + << "Column <" << arg1 << "> has got " << arg2 + << " rows, but Column <" << arg3 << "> has got " << arg4 << "."); + + /** + * Exception + */ + DeclException1 (ExcUndefinedTexFormat, + std::string, + << "<" << arg1 << "> is not a tex column format. Use l,c,r."); + //@} +protected: + + /** + * Structure encapsulating all the data + * that is needed to describe one column + * of a table. + */ + struct Column + { /** - * Set of options how a table should be formatted when output with - * the write_text() function. The following possibilities exist: - * - * - table_with_headers: The table is formatted in such a way - * that the contents are aligned under the key of each column, i.e. the - * key sits atop each column. This is suitable for tables with few columns - * where the entire table can be displayed on the screen. Output looks - * like this: - * @code - * key1 key2 key3 - * 0 0 "" - * 1 0 "" - * 2 13 a - * 1 0 "" - * @endcode - * - table_with_separate_column_description: This is a better - * format when there are many columns and the table as a whole can not - * be displayed on the screen. Here, the column keys are first listed - * one-by-one on lines of their own, and are numbered for better readability. - * In addition, each of these description lines are prefixed by '#' to mark - * these lines as comments for programs that want to read the following - * table as data and should ignore these descriptive lines. GNUPLOT is - * one such program that will automatically ignore lines so prefixed. - * Output with this option looks like this: - * @code - * # 1: key1 - * # 2: key2 - * # 3: key3 - * 0 0 "" - * 1 0 "" - * 2 13 a - * 1 0 "" - * @endcode - * - simple_table_with_separate_column_description: This format - * is very similar to table_with_separate_column_description, - * but it skips aligning the columns with additional white space. This - * increases the performance o fwrite_text() for large tables. Example output: - * @code - * # 1: key1 - * # 2: key2 - * # 3: key3 - * 0 0 "" - * 1 0 "" - * 2 13 a - * 1 0 "" - * @endcode - * - **/ - enum TextOutputFormat - { - table_with_headers, - table_with_separate_column_description, - simple_table_with_separate_column_description - }; - - /** - * Constructor. - */ - TableHandler (); - - /** - * Adds a column (if not yet existent) - * with the key key and adds the - * value of type T to the - * column. Values of type T must - * be convertible to one of int, - * unsigned int, double, - * std::string or a compiler error - * will result. - */ - template - void add_value (const std::string &key, - const T value); + * Constructor needed by STL maps. + */ + Column (); + + /** + * Constructor. + */ + Column (const std::string &tex_caption); /** - * Switch auto-fill mode on or off. See the general documentation - * of this class for a description of what auto-fill mode does. + * Pad this column with default constructed elements to the + * number of rows given by the argument. + */ + void pad_column_below (const unsigned int length); + + /** + * Read or write the data of this + * object to or from a stream for + * the purpose of serialization. */ - void set_auto_fill_mode (const bool state); - - /** - * Creates a supercolumn (if not - * yet existent) and includes - * column to it. The keys of the - * column and the supercolumn are - * key and superkey, - * respectively. To merge two - * columns c1 and c2 to a - * supercolumn sc hence call - * add_column_to_supercolumn(c1,sc) - * and - * add_column_to_supercolumn(c2,sc). - * - * Concerning the order of the - * columns, the supercolumn - * replaces the first column that - * is added to the supercolumn. - * Within the supercolumn the - * order of output follows the - * order the columns are added to - * the supercolumn. - */ - void add_column_to_supercolumn (const std::string &key, - const std::string &superkey); - - /** - * Change the order of columns and - * supercolumns in the table. - * - * new_order includes the - * keys and superkeys of the - * columns and supercolumns in - * the order the user like to. - * If a superkey is included the - * keys of the subcolumns need - * not to be additionally - * mentioned in this vector. The - * order of subcolumns within a - * supercolumn is not changeable - * and keeps the order in which - * the columns are added to the - * supercolumn. - * - * This function may also be used - * to break big tables with too - * many columns into smaller - * ones. Call this function with - * the first e.g. five columns - * and then write_*. - * Afterwards call this function - * with the next e.g. five - * columns and again write_*, - * and so on. - */ - void set_column_order (const std::vector &new_order); - - /** - * Sets the precision - * e.g. double or float variables - * are written - * with. precision is the - * same as in calling - * out<. - */ - void set_precision (const std::string &key, - const unsigned int precision); - - /** - * Sets the - * scientific_flag. True - * means scientific, false means - * fixed point notation. - */ - void set_scientific (const std::string &key, - const bool scientific); - - /** - * Sets the caption of the column - * key for tex output. You - * may want to chose this - * different from key, if it - * contains formulas or similar - * constructs. - */ - void set_tex_caption (const std::string &key, - const std::string &tex_caption); - - /** - * Sets the tex caption of the entire - * table for tex output. - */ - void set_tex_table_caption (const std::string &table_caption); - - /** - * Sets the label of this - * table for tex output. - */ - void set_tex_table_label (const std::string &table_label); - - /** - * Sets the caption the the - * supercolumn superkey for - * tex output. You may want to - * chose this different from - * superkey, if it contains - * formulas or similar - * constructs. - */ - void set_tex_supercaption (const std::string &superkey, - const std::string &tex_supercaption); - - /** - * Sets the tex output format of - * a column, e.g. c, r, - * l, or p{3cm}. The - * default is c. Also if this - * function is not called for a - * column, the default is preset - * to be c. - */ - void set_tex_format (const std::string &key, - const std::string &format="c"); - - /** - * Write table as formatted text to the - * given stream. The text is formatted in - * such as way that it represents data as - * formatted columns of text. To avoid - * problems when reading these tables - * automatically, for example for - * postprocessing, if an entry in a cell - * of this table is empty (i.e. it has - * been created by calling the - * add_value() function with an empty - * string), then the entry of the table - * is printed as "". - * - * The second argument indicates how - * column keys are to be displayed. See - * the description of TextOutputFormat - * for more information - */ - void write_text (std::ostream &out, - const TextOutputFormat format = table_with_headers) const; - - /** - * Write table as a tex file. If - * with_header is set to false - * (it is true by default), then - * no "\documentclass{...}", - * "\begin{document}" and - * "\end{document}" are used. In - * this way the file can be - * included into an existing tex - * file using a command like - * "\input{table_file}". - */ - void write_tex (std::ostream &file, const bool with_header=true) const; - - /** - * Read or write the data of this - * object to or from a stream for - * the purpose of serialization. - */ template - void serialize(Archive & ar, const unsigned int version); - - /** @addtogroup Exceptions - * @{ */ - - /** - * Exception - */ - DeclException1 (ExcColumnNotExistent, - std::string, - << "Column <" << arg1 << "> does not exist."); - - /** - * Exception - */ - DeclException1 (ExcSuperColumnNotExistent, - std::string, - << "Supercolumn <" << arg1 << "> does not exist."); - - /** - * Exception - */ - DeclException1 (ExcColumnOrSuperColumnNotExistent, - std::string, - << "Column or supercolumn <" << arg1 << "> does not exist."); - - /** - * Exception - */ - DeclException4 (ExcWrongNumberOfDataEntries, - std::string, int, std::string, int, - << "Column <" << arg1 << "> has got " << arg2 - << " rows, but Column <" << arg3 << "> has got " << arg4 << "."); - - /** - * Exception - */ - DeclException1 (ExcUndefinedTexFormat, - std::string, - << "<" << arg1 << "> is not a tex column format. Use l,c,r."); - //@} - protected: - - /** - * Structure encapsulating all the data - * that is needed to describe one column - * of a table. - */ - struct Column - { - /** - * Constructor needed by STL maps. - */ - Column (); - - /** - * Constructor. - */ - Column (const std::string &tex_caption); - - /** - * Pad this column with default constructed elements to the - * number of rows given by the argument. - */ - void pad_column_below (const unsigned int length); - - /** - * Read or write the data of this - * object to or from a stream for - * the purpose of serialization. - */ - template - void save(Archive & ar, const unsigned int version) const; - template - void load(Archive & ar, const unsigned int version); - BOOST_SERIALIZATION_SPLIT_MEMBER() - - - /** - * Invalidates the string cache of all the entries and recomputes - * the maximum length max_length. - */ - void invalidate_cache(); - - /** - * List of entries within - * this column. Values are - * always immediately - * converted to strings to - * provide a uniform method - * of lookup. - */ - std::vector entries; - - /** - * The caption of the column - * in tex output. By - * default, this is the key - * string that is given to - * the TableHandler by - * TableHandler::add_value(...). This - * may be changed by calling - * TableHandler::set_tex_caption(...). - */ - std::string tex_caption; - - /** - * The column format in tex - * output. By default, this - * is "c", meaning - * `centered'. This may be - * changed by calling - * TableHandler::set_tex_format(...) - * with "c", "r", "l" for - * centered, right or left. - */ - - std::string tex_format; - - /** - * Double or float entries - * are written with this - * precision (set by the - * user). The default is 4. - */ - unsigned int precision; - - /** - * scientific=false means - * fixed point notation. - */ - bool scientific; - - /** - * Flag that may be used by - * derived classes for - * arbitrary purposes. - * - * In particular, the - * ConvergenceTable class uses the - * flag to denote columns for which - * convergence information has - * already been computed, or should - * not be computed at all. - */ - unsigned int flag; - - /** - * This entry caches the maximum length in characters for all entries in this table. - */ - unsigned int max_length; - }; - - /** - * Help function that gives a - * vector of the keys of all - * columns that are mentioned in - * column_order, where each - * supercolumn key is replaced by - * its subcolumn keys. - * - * This function implicitly - * checks the consistency of the - * data. The result is returned - * in sel_columns. - */ - void get_selected_columns (std::vector &sel_columns) const; - - /** - * Builtin function, that gives - * the number of rows in the - * table and that checks if the - * number of rows is equal in - * every column. This function is - * e.g. called before writing - * output. - */ - unsigned int n_rows() const; - - /** - * Stores the column and - * supercolumn keys in the order - * desired by the user. By - * default this is the order of - * adding the columns. This order - * may be changed by - * set_column_order(...). - */ - std::vector column_order; - - /** - * Maps the column keys to the - * columns (not supercolumns). - * - * The field is declared mutable so - * that the write_text() and write_tex() - * functions can be const, even though they - * may pad columns below if auto_fill_mode - * is on. - */ - mutable std::map columns; - - /** - * Maps each supercolumn key to - * the the keys of its subcolumns - * in the right order. It is - * allowed that a supercolumn has - * got the same key as a column. - * - * Note that we do not use a multimap - * here since the order of column - * keys for each supercolumn key is - * relevant. - */ - std::map > supercolumns; - - /** - * Maps the supercolumn keys to - * the captions of the - * supercolumns that are used in - * tex output. - * - * By default these are just the - * supercolumn keys but they may - * be changed by - * set_tex_supercaptions(...). - */ - std::map tex_supercaptions; - - /** - * The caption of the table itself. - */ - std::string tex_table_caption; - /** - * The label of the table. - */ - std::string tex_table_label; + void save(Archive &ar, const unsigned int version) const; + template + void load(Archive &ar, const unsigned int version); + BOOST_SERIALIZATION_SPLIT_MEMBER() + + + /** + * Invalidates the string cache of all the entries and recomputes + * the maximum length max_length. + */ + void invalidate_cache(); + + /** + * List of entries within + * this column. Values are + * always immediately + * converted to strings to + * provide a uniform method + * of lookup. + */ + std::vector entries; + + /** + * The caption of the column + * in tex output. By + * default, this is the key + * string that is given to + * the TableHandler by + * TableHandler::add_value(...). This + * may be changed by calling + * TableHandler::set_tex_caption(...). + */ + std::string tex_caption; + + /** + * The column format in tex + * output. By default, this + * is "c", meaning + * `centered'. This may be + * changed by calling + * TableHandler::set_tex_format(...) + * with "c", "r", "l" for + * centered, right or left. + */ + + std::string tex_format; + + /** + * Double or float entries + * are written with this + * precision (set by the + * user). The default is 4. + */ + unsigned int precision; + + /** + * scientific=false means + * fixed point notation. + */ + bool scientific; + + /** + * Flag that may be used by + * derived classes for + * arbitrary purposes. + * + * In particular, the + * ConvergenceTable class uses the + * flag to denote columns for which + * convergence information has + * already been computed, or should + * not be computed at all. + */ + unsigned int flag; /** - * Flag indicating whether auto-fill mode should be used. + * This entry caches the maximum length in characters for all entries in this table. */ - bool auto_fill_mode; + unsigned int max_length; + }; + + /** + * Help function that gives a + * vector of the keys of all + * columns that are mentioned in + * column_order, where each + * supercolumn key is replaced by + * its subcolumn keys. + * + * This function implicitly + * checks the consistency of the + * data. The result is returned + * in sel_columns. + */ + void get_selected_columns (std::vector &sel_columns) const; + + /** + * Builtin function, that gives + * the number of rows in the + * table and that checks if the + * number of rows is equal in + * every column. This function is + * e.g. called before writing + * output. + */ + unsigned int n_rows() const; + + /** + * Stores the column and + * supercolumn keys in the order + * desired by the user. By + * default this is the order of + * adding the columns. This order + * may be changed by + * set_column_order(...). + */ + std::vector column_order; + + /** + * Maps the column keys to the + * columns (not supercolumns). + * + * The field is declared mutable so + * that the write_text() and write_tex() + * functions can be const, even though they + * may pad columns below if auto_fill_mode + * is on. + */ + mutable std::map columns; + + /** + * Maps each supercolumn key to + * the the keys of its subcolumns + * in the right order. It is + * allowed that a supercolumn has + * got the same key as a column. + * + * Note that we do not use a multimap + * here since the order of column + * keys for each supercolumn key is + * relevant. + */ + std::map > supercolumns; + + /** + * Maps the supercolumn keys to + * the captions of the + * supercolumns that are used in + * tex output. + * + * By default these are just the + * supercolumn keys but they may + * be changed by + * set_tex_supercaptions(...). + */ + std::map tex_supercaptions; + + /** + * The caption of the table itself. + */ + std::string tex_table_caption; + /** + * The label of the table. + */ + std::string tex_table_label; + + /** + * Flag indicating whether auto-fill mode should be used. + */ + bool auto_fill_mode; }; @@ -754,8 +754,8 @@ namespace internal { template TableEntry::TableEntry (const T &t) - : - value (t) + : + value (t) {} @@ -769,44 +769,44 @@ namespace internal // T, then we will get an exception that we can // catch and produce an error message try - { - return boost::get(value); - } + { + return boost::get(value); + } catch (...) - { - Assert(false, ExcMessage ("This TableEntry object does not store a datum of type T")); - throw; - } + { + Assert(false, ExcMessage ("This TableEntry object does not store a datum of type T")); + throw; + } } template - void TableEntry::save (Archive & ar, + void TableEntry::save (Archive &ar, const unsigned int) const { - // write first an identifier for the kind - // of data stored and then the actual - // data, in its correct data type + // write first an identifier for the kind + // of data stored and then the actual + // data, in its correct data type if (const int *p = boost::get(&value)) { char c = 'i'; - ar & c & *p; + ar &c & *p; } else if (const unsigned int *p = boost::get(&value)) { char c = 'u'; - ar & c & *p; + ar &c & *p; } else if (const double *p = boost::get(&value)) { char c = 'd'; - ar & c & *p; + ar &c & *p; } else if (const std::string *p = boost::get(&value)) { char c = 's'; - ar & c & *p; + ar &c & *p; } else Assert (false, ExcInternalError()); @@ -815,52 +815,52 @@ namespace internal template - void TableEntry::load (Archive & ar, + void TableEntry::load (Archive &ar, const unsigned int) { - // following what we do in the save() - // function, first read in the data type - // as a one-character id, and then read - // the data + // following what we do in the save() + // function, first read in the data type + // as a one-character id, and then read + // the data char c; - ar & c; + ar &c; switch (c) { - case 'i': - { - int val; - ar & val; - value = val; - break; - } + case 'i': + { + int val; + ar &val; + value = val; + break; + } - case 'u': - { - unsigned int val; - ar & val; - value = val; - break; - } + case 'u': + { + unsigned int val; + ar &val; + value = val; + break; + } - case 'd': - { - double val; - ar & val; - value = val; - break; - } + case 'd': + { + double val; + ar &val; + value = val; + break; + } - case 's': - { - std::string val; - ar & val; - value = val; - break; - } + case 's': + { + std::string val; + ar &val; + value = val; + break; + } - default: - Assert (false, ExcInternalError()); + default: + Assert (false, ExcInternalError()); } } } @@ -878,25 +878,25 @@ void TableHandler::add_value (const std::string &key, } if (auto_fill_mode == true) - { - // follow the algorithm given in the introduction to this class - // of padding columns as necessary - unsigned int n = 0; - for (std::map< std::string, Column >::iterator p = columns.begin(); p != columns.end(); ++p) - n = (n >= p->second.entries.size() ? n : p->second.entries.size()); + { + // follow the algorithm given in the introduction to this class + // of padding columns as necessary + unsigned int n = 0; + for (std::map< std::string, Column >::iterator p = columns.begin(); p != columns.end(); ++p) + n = (n >= p->second.entries.size() ? n : p->second.entries.size()); - while (columns[key].entries.size()+1 < n) - { - columns[key].entries.push_back (internal::TableEntry(T())); - internal::TableEntry & entry = columns[key].entries.back(); - entry.cache_string(columns[key].scientific, columns[key].precision); - columns[key].max_length = std::max(columns[key].max_length, static_cast(entry.get_cached_string().length())); - } - } + while (columns[key].entries.size()+1 < n) + { + columns[key].entries.push_back (internal::TableEntry(T())); + internal::TableEntry &entry = columns[key].entries.back(); + entry.cache_string(columns[key].scientific, columns[key].precision); + columns[key].max_length = std::max(columns[key].max_length, static_cast(entry.get_cached_string().length())); + } + } // now push the value given to this function columns[key].entries.push_back (internal::TableEntry(value)); - internal::TableEntry & entry = columns[key].entries.back(); + internal::TableEntry &entry = columns[key].entries.back(); entry.cache_string(columns[key].scientific, columns[key].precision); columns[key].max_length = std::max(columns[key].max_length, static_cast(entry.get_cached_string().length())); } @@ -904,38 +904,38 @@ void TableHandler::add_value (const std::string &key, template void -TableHandler::Column::save(Archive & ar, const unsigned int version) const +TableHandler::Column::save(Archive &ar, const unsigned int version) const { - ar & entries & tex_caption - & tex_format & precision - & scientific - & flag - & max_length; + ar &entries &tex_caption + & tex_format &precision + & scientific + & flag + & max_length; } template void -TableHandler::Column::load(Archive & ar, const unsigned int version) +TableHandler::Column::load(Archive &ar, const unsigned int version) { - ar & entries & tex_caption - & tex_format & precision - & scientific - & flag - & max_length; - invalidate_cache(); + ar &entries &tex_caption + & tex_format &precision + & scientific + & flag + & max_length; + invalidate_cache(); } template void -TableHandler::serialize(Archive & ar, +TableHandler::serialize(Archive &ar, const unsigned int) { - ar & column_order & columns - & supercolumns & tex_supercaptions - & tex_table_caption - & tex_table_label - & auto_fill_mode; + ar &column_order &columns + & supercolumns &tex_supercaptions + & tex_table_caption + & tex_table_label + & auto_fill_mode; } diff --git a/deal.II/include/deal.II/base/table_indices.h b/deal.II/include/deal.II/base/table_indices.h index 9aa1a61082..6e4f5414fe 100644 --- a/deal.II/include/deal.II/base/table_indices.h +++ b/deal.II/include/deal.II/base/table_indices.h @@ -35,45 +35,45 @@ DEAL_II_NAMESPACE_OPEN template class TableIndicesBase { - public: - /** - * Access the value of the - * ith index. - */ - unsigned int operator[] (const unsigned int i) const; - - /** - * Compare two index fields for - * equality. - */ - bool operator == (const TableIndicesBase &other) const; - - /** - * Compare two index fields for - * inequality. - */ - bool operator != (const TableIndicesBase &other) const; - - /** - * Sort the indices in ascending - * order. While this operation is not - * very useful for Table objects, it is - * used for the SymmetricTensor class. - */ - void sort (); - - /** - * Write or read the data of this object to or - * from a stream for the purpose of serialization. - */ - template - void serialize (Archive & ar, const unsigned int version); - - protected: - /** - * Store the indices in an array. - */ - unsigned int indices[N]; +public: + /** + * Access the value of the + * ith index. + */ + unsigned int operator[] (const unsigned int i) const; + + /** + * Compare two index fields for + * equality. + */ + bool operator == (const TableIndicesBase &other) const; + + /** + * Compare two index fields for + * inequality. + */ + bool operator != (const TableIndicesBase &other) const; + + /** + * Sort the indices in ascending + * order. While this operation is not + * very useful for Table objects, it is + * used for the SymmetricTensor class. + */ + void sort (); + + /** + * Write or read the data of this object to or + * from a stream for the purpose of serialization. + */ + template + void serialize (Archive &ar, const unsigned int version); + +protected: + /** + * Store the indices in an array. + */ + unsigned int indices[N]; }; @@ -95,18 +95,18 @@ class TableIndicesBase template class TableIndices : public TableIndicesBase { - /** - * Standard constructor, setting - * all indices to zero. - */ - TableIndices(); - /** - * The actual constructor, taking - * @p N arguments of type - * unsigned int to - * initialize the index object. - */ - TableIndices(...); + /** + * Standard constructor, setting + * all indices to zero. + */ + TableIndices(); + /** + * The actual constructor, taking + * @p N arguments of type + * unsigned int to + * initialize the index object. + */ + TableIndices(...); }; @@ -127,18 +127,18 @@ class TableIndices : public TableIndicesBase template <> class TableIndices<1> : public TableIndicesBase<1> { - public: - /** - * Default constructor. Set all - * indices to zero. - */ - TableIndices (); - - /** - * Constructor. Set indices to - * the given values. - */ - TableIndices (const unsigned int index1); +public: + /** + * Default constructor. Set all + * indices to zero. + */ + TableIndices (); + + /** + * Constructor. Set indices to + * the given values. + */ + TableIndices (const unsigned int index1); }; //TODO: Remove the default arguments and trickery below and put all @@ -159,30 +159,30 @@ class TableIndices<1> : public TableIndicesBase<1> template <> class TableIndices<2> : public TableIndicesBase<2> { - public: - /** - * Default constructor. Set all - * indices to zero. - */ - TableIndices (); - - /** - * Constructor. Set indices to - * the given values. - * - * The default values for the - * second and subsequent - * arguments are necessary for - * some neat template tricks in - * SymmetricTensor where we only - * want to set the first index - * and construct the subsequent - * ones later on, i.e. for the - * moment we don't care about the - * later indices. - */ - TableIndices (const unsigned int index1, - const unsigned int index2 = numbers::invalid_unsigned_int); +public: + /** + * Default constructor. Set all + * indices to zero. + */ + TableIndices (); + + /** + * Constructor. Set indices to + * the given values. + * + * The default values for the + * second and subsequent + * arguments are necessary for + * some neat template tricks in + * SymmetricTensor where we only + * want to set the first index + * and construct the subsequent + * ones later on, i.e. for the + * moment we don't care about the + * later indices. + */ + TableIndices (const unsigned int index1, + const unsigned int index2 = numbers::invalid_unsigned_int); }; @@ -202,31 +202,31 @@ class TableIndices<2> : public TableIndicesBase<2> template <> class TableIndices<3> : public TableIndicesBase<3> { - public: - /** - * Default constructor. Set all - * indices to zero. - */ - TableIndices (); - - /** - * Constructor. Set indices to - * the given values. - * - * The default values for the - * second and subsequent - * arguments are necessary for - * some neat template tricks in - * SymmetricTensor where we only - * want to set the first index - * and construct the subsequent - * ones later on, i.e. for the - * moment we don't care about the - * later indices. - */ - TableIndices (const unsigned int index1, - const unsigned int index2 = numbers::invalid_unsigned_int, - const unsigned int index3 = numbers::invalid_unsigned_int); +public: + /** + * Default constructor. Set all + * indices to zero. + */ + TableIndices (); + + /** + * Constructor. Set indices to + * the given values. + * + * The default values for the + * second and subsequent + * arguments are necessary for + * some neat template tricks in + * SymmetricTensor where we only + * want to set the first index + * and construct the subsequent + * ones later on, i.e. for the + * moment we don't care about the + * later indices. + */ + TableIndices (const unsigned int index1, + const unsigned int index2 = numbers::invalid_unsigned_int, + const unsigned int index3 = numbers::invalid_unsigned_int); }; @@ -245,32 +245,32 @@ class TableIndices<3> : public TableIndicesBase<3> template <> class TableIndices<4> : public TableIndicesBase<4> { - public: - /** - * Default constructor. Set all - * indices to zero. - */ - TableIndices (); - - /** - * Constructor. Set indices to - * the given values. - * - * The default values for the - * second and subsequent - * arguments are necessary for - * some neat template tricks in - * SymmetricTensor where we only - * want to set the first index - * and construct the subsequent - * ones later on, i.e. for the - * moment we don't care about the - * later indices. - */ - TableIndices (const unsigned int index1, - const unsigned int index2 = numbers::invalid_unsigned_int, - const unsigned int index3 = numbers::invalid_unsigned_int, - const unsigned int index4 = numbers::invalid_unsigned_int); +public: + /** + * Default constructor. Set all + * indices to zero. + */ + TableIndices (); + + /** + * Constructor. Set indices to + * the given values. + * + * The default values for the + * second and subsequent + * arguments are necessary for + * some neat template tricks in + * SymmetricTensor where we only + * want to set the first index + * and construct the subsequent + * ones later on, i.e. for the + * moment we don't care about the + * later indices. + */ + TableIndices (const unsigned int index1, + const unsigned int index2 = numbers::invalid_unsigned_int, + const unsigned int index3 = numbers::invalid_unsigned_int, + const unsigned int index4 = numbers::invalid_unsigned_int); }; @@ -289,33 +289,33 @@ class TableIndices<4> : public TableIndicesBase<4> template <> class TableIndices<5> : public TableIndicesBase<5> { - public: - /** - * Default constructor. Set all - * indices to zero. - */ - TableIndices (); - - /** - * Constructor. Set indices to - * the given values. - * - * The default values for the - * second and subsequent - * arguments are necessary for - * some neat template tricks in - * SymmetricTensor where we only - * want to set the first index - * and construct the subsequent - * ones later on, i.e. for the - * moment we don't care about the - * later indices. - */ - TableIndices (const unsigned int index1, - const unsigned int index2 = numbers::invalid_unsigned_int, - const unsigned int index3 = numbers::invalid_unsigned_int, - const unsigned int index4 = numbers::invalid_unsigned_int, - const unsigned int index5 = numbers::invalid_unsigned_int); +public: + /** + * Default constructor. Set all + * indices to zero. + */ + TableIndices (); + + /** + * Constructor. Set indices to + * the given values. + * + * The default values for the + * second and subsequent + * arguments are necessary for + * some neat template tricks in + * SymmetricTensor where we only + * want to set the first index + * and construct the subsequent + * ones later on, i.e. for the + * moment we don't care about the + * later indices. + */ + TableIndices (const unsigned int index1, + const unsigned int index2 = numbers::invalid_unsigned_int, + const unsigned int index3 = numbers::invalid_unsigned_int, + const unsigned int index4 = numbers::invalid_unsigned_int, + const unsigned int index5 = numbers::invalid_unsigned_int); }; @@ -334,34 +334,34 @@ class TableIndices<5> : public TableIndicesBase<5> template <> class TableIndices<6> : public TableIndicesBase<6> { - public: - /** - * Default constructor. Set all - * indices to zero. - */ - TableIndices (); - - /** - * Constructor. Set indices to - * the given values. - * - * The default values for the - * second and subsequent - * arguments are necessary for - * some neat template tricks in - * SymmetricTensor where we only - * want to set the first index - * and construct the subsequent - * ones later on, i.e. for the - * moment we don't care about the - * later indices. - */ - TableIndices (const unsigned int index1, - const unsigned int index2 = numbers::invalid_unsigned_int, - const unsigned int index3 = numbers::invalid_unsigned_int, - const unsigned int index4 = numbers::invalid_unsigned_int, - const unsigned int index5 = numbers::invalid_unsigned_int, - const unsigned int index6 = numbers::invalid_unsigned_int); +public: + /** + * Default constructor. Set all + * indices to zero. + */ + TableIndices (); + + /** + * Constructor. Set indices to + * the given values. + * + * The default values for the + * second and subsequent + * arguments are necessary for + * some neat template tricks in + * SymmetricTensor where we only + * want to set the first index + * and construct the subsequent + * ones later on, i.e. for the + * moment we don't care about the + * later indices. + */ + TableIndices (const unsigned int index1, + const unsigned int index2 = numbers::invalid_unsigned_int, + const unsigned int index3 = numbers::invalid_unsigned_int, + const unsigned int index4 = numbers::invalid_unsigned_int, + const unsigned int index5 = numbers::invalid_unsigned_int, + const unsigned int index6 = numbers::invalid_unsigned_int); }; @@ -380,35 +380,35 @@ class TableIndices<6> : public TableIndicesBase<6> template <> class TableIndices<7> : public TableIndicesBase<7> { - public: - /** - * Default constructor. Set all - * indices to zero. - */ - TableIndices (); - - /** - * Constructor. Set indices to - * the given values. - * - * The default values for the - * second and subsequent - * arguments are necessary for - * some neat template tricks in - * SymmetricTensor where we only - * want to set the first index - * and construct the subsequent - * ones later on, i.e. for the - * moment we don't care about the - * later indices. - */ - TableIndices (const unsigned int index1, - const unsigned int index2 = numbers::invalid_unsigned_int, - const unsigned int index3 = numbers::invalid_unsigned_int, - const unsigned int index4 = numbers::invalid_unsigned_int, - const unsigned int index5 = numbers::invalid_unsigned_int, - const unsigned int index6 = numbers::invalid_unsigned_int, - const unsigned int index7 = numbers::invalid_unsigned_int); +public: + /** + * Default constructor. Set all + * indices to zero. + */ + TableIndices (); + + /** + * Constructor. Set indices to + * the given values. + * + * The default values for the + * second and subsequent + * arguments are necessary for + * some neat template tricks in + * SymmetricTensor where we only + * want to set the first index + * and construct the subsequent + * ones later on, i.e. for the + * moment we don't care about the + * later indices. + */ + TableIndices (const unsigned int index1, + const unsigned int index2 = numbers::invalid_unsigned_int, + const unsigned int index3 = numbers::invalid_unsigned_int, + const unsigned int index4 = numbers::invalid_unsigned_int, + const unsigned int index5 = numbers::invalid_unsigned_int, + const unsigned int index6 = numbers::invalid_unsigned_int, + const unsigned int index7 = numbers::invalid_unsigned_int); }; @@ -453,9 +453,9 @@ template template inline void -TableIndicesBase::serialize (Archive & ar, const unsigned int) +TableIndicesBase::serialize (Archive &ar, const unsigned int) { - ar & indices; + ar &indices; } @@ -484,7 +484,7 @@ inline void TableIndicesBase<3>::sort () { - // bubble sort for 3 elements + // bubble sort for 3 elements if (indices[1] < indices[0]) std::swap (indices[0], indices[1]); if (indices[2] < indices[1]) @@ -576,9 +576,9 @@ inline TableIndices<5>::TableIndices () { this->indices[0] = this->indices[1] - = this->indices[2] - = this->indices[3] - = this->indices[4] = 0; + = this->indices[2] + = this->indices[3] + = this->indices[4] = 0; } @@ -603,8 +603,8 @@ inline TableIndices<6>::TableIndices () { this->indices[0] = this->indices[1] = this->indices[2] - = this->indices[3] = this->indices[4] - = this->indices[5] = 0; + = this->indices[3] = this->indices[4] + = this->indices[5] = 0; } @@ -631,8 +631,8 @@ inline TableIndices<7>::TableIndices () { this->indices[0] = this->indices[1] = this->indices[2] - = this->indices[3] = this->indices[4] - = this->indices[5] = this->indices[6] = 0; + = this->indices[3] = this->indices[4] + = this->indices[5] = this->indices[6] = 0; } diff --git a/deal.II/include/deal.II/base/template_constraints.h b/deal.II/include/deal.II/base/template_constraints.h index faa2271e64..19deafc271 100644 --- a/deal.II/include/deal.II/base/template_constraints.h +++ b/deal.II/include/deal.II/base/template_constraints.h @@ -78,7 +78,7 @@ template struct constraint_and_return_value; */ template struct constraint_and_return_value { - typedef T type; + typedef T type; }; @@ -137,7 +137,7 @@ template struct constraint_and_return_value template struct identity { - typedef T type; + typedef T type; }; @@ -162,99 +162,99 @@ struct identity */ struct PointerComparison { - /** - * Comparison function for pointers of - * the same type. Returns @p true if the - * two pointers are equal. - */ - template - static bool equal (const T *p1, const T *p2); - - /** - * Comparison function for pointers of - * different types. The C++ language does - * not allow comparing these pointers - * using operator==. However, - * since the two pointers have different - * types, we know that they can't be the - * same, so we always return @p false. - */ - template - static bool equal (const T*, const U*); + /** + * Comparison function for pointers of + * the same type. Returns @p true if the + * two pointers are equal. + */ + template + static bool equal (const T *p1, const T *p2); + + /** + * Comparison function for pointers of + * different types. The C++ language does + * not allow comparing these pointers + * using operator==. However, + * since the two pointers have different + * types, we know that they can't be the + * same, so we always return @p false. + */ + template + static bool equal (const T *, const U *); }; namespace internal { -/** - * A type that is sometimes used for template tricks. For example, in - * some situations one would like to do this: - * - * @verbatim - * template - * class X { - * // do something on subdim-dimensional sub-objects of the big - * // dim-dimensional thing (for example on vertices/lines/quads of - * // cells): - * template void f(); - * }; - * - * template - * template <> - * void X::f<0> () { ...operate on the vertices of a cell... } - * - * template void f(X &x) { - * x.f (); - * } - * @endverbatim - * - * The problem is: the language doesn't allow us to specialize - * X::f() without specializing the outer class first. One - * of the common tricks is therefore to use something like this: - * - * @verbatim - * template struct int2type {}; - * - * template - * class X { - * // do something on subdim-dimensional sub-objects of the big - * // dim-dimensional thing (for example on vertices/lines/quads of - * // cells): - * void f(int2type<0>); - * void f(int2type<1>); - * void f(int2type<2>); - * void f(int2type<3>); - * }; - * - * template - * void X::f (int2type<0>) { ...operate on the vertices of a cell... } - * - * template - * void X::f (int2type<1>) { ...operate on the lines of a cell... } - * - * template void f(X &x) { - * x.f (int2type()); - * } - * @endverbatim - * - * Note that we have replaced specialization of X::f() by - * overloading, but that from inside the function g(), we - * can still select which of the different X::f() we want - * based on the subdim template argument. - * - * @author Wolfgang Bangerth, 2006 - */ + /** + * A type that is sometimes used for template tricks. For example, in + * some situations one would like to do this: + * + * @verbatim + * template + * class X { + * // do something on subdim-dimensional sub-objects of the big + * // dim-dimensional thing (for example on vertices/lines/quads of + * // cells): + * template void f(); + * }; + * + * template + * template <> + * void X::f<0> () { ...operate on the vertices of a cell... } + * + * template void f(X &x) { + * x.f (); + * } + * @endverbatim + * + * The problem is: the language doesn't allow us to specialize + * X::f() without specializing the outer class first. One + * of the common tricks is therefore to use something like this: + * + * @verbatim + * template struct int2type {}; + * + * template + * class X { + * // do something on subdim-dimensional sub-objects of the big + * // dim-dimensional thing (for example on vertices/lines/quads of + * // cells): + * void f(int2type<0>); + * void f(int2type<1>); + * void f(int2type<2>); + * void f(int2type<3>); + * }; + * + * template + * void X::f (int2type<0>) { ...operate on the vertices of a cell... } + * + * template + * void X::f (int2type<1>) { ...operate on the lines of a cell... } + * + * template void f(X &x) { + * x.f (int2type()); + * } + * @endverbatim + * + * Note that we have replaced specialization of X::f() by + * overloading, but that from inside the function g(), we + * can still select which of the different X::f() we want + * based on the subdim template argument. + * + * @author Wolfgang Bangerth, 2006 + */ template struct int2type {}; -/** - * The equivalent of the int2type class for boolean arguments. - * - * @author Wolfgang Bangerth, 2009 - */ + /** + * The equivalent of the int2type class for boolean arguments. + * + * @author Wolfgang Bangerth, 2009 + */ template struct bool2type {}; @@ -281,7 +281,7 @@ namespace internal template struct types_are_equal { - static const bool value = false; + static const bool value = false; }; @@ -293,7 +293,7 @@ struct types_are_equal template struct types_are_equal { - static const bool value = true; + static const bool value = true; }; @@ -304,7 +304,7 @@ struct types_are_equal template inline bool -PointerComparison::equal (const T*, const U*) +PointerComparison::equal (const T *, const U *) { return false; } diff --git a/deal.II/include/deal.II/base/tensor.h b/deal.II/include/deal.II/base/tensor.h index e95edccfbf..c151cb197f 100644 --- a/deal.II/include/deal.II/base/tensor.h +++ b/deal.II/include/deal.II/base/tensor.h @@ -45,270 +45,270 @@ template class Tensor<1,dim,Number>; template class Tensor { - public: - /** - * Provide a way to get the - * dimension of an object without - * explicit knowledge of it's - * data type. Implementation is - * this way instead of providing - * a function dimension() - * because now it is possible to - * get the dimension at compile - * time without the expansion and - * preevaluation of an inlined - * function; the compiler may - * therefore produce more - * efficient code and you may use - * this value to declare other - * data types. - */ - static const unsigned int dimension = dim; - - /** - * Publish the rank of this tensor to - * the outside world. - */ - static const unsigned int rank = rank_; - - /** - * Type of stored objects. This - * is a tensor of lower rank. - */ - typedef Tensor value_type; - - /** - * Declare a type that has holds - * real-valued numbers with the same - * precision as the template argument to - * this class. For std::complex, - * this corresponds to type number, and - * it is equal to Number for all other - * cases. See also the respective field - * in Vector. - * - * This typedef is used to - * represent the return type of - * norms. - */ - typedef typename numbers::NumberTraits::real_type real_type; - - /** - * Declare an array type which - * can be used to initialize an - * object of this type - * statically. - */ - typedef typename Tensor::array_type array_type[dim]; - - /** - * Constructor. Initialize all entries - * to zero. - */ - Tensor (); - - /** - * Copy constructor, where the data is - * copied from a C-style array. - */ - Tensor (const array_type &initializer); - - /** - * Conversion operator from tensor of - * tensors. - */ - Tensor (const Tensor<1,dim,Tensor > &tensor_in); - - /** - * Conversion operator to tensor of - * tensors. - */ - operator Tensor<1,dim,Tensor > () const; - - /** - * Read-Write access operator. - */ - Tensor &operator [] (const unsigned int i); - - /** - * Read-only access operator. - */ - const Tensor &operator [] (const unsigned int i) const; - - /** - * Assignment operator. - */ - Tensor & operator = (const Tensor &); - - /** - * This operator assigns a scalar - * to a tensor. To avoid - * confusion with what exactly it - * means to assign a scalar value - * to a tensor, zero is the only - * value allowed for d, - * allowing the intuitive - * notation t=0 to reset - * all elements of the tensor to - * zero. - */ - Tensor & operator = (const Number d); - - /** - * Test for equality of two tensors. - */ - bool operator == (const Tensor &) const; - - /** - * Test for inequality of two tensors. - */ - bool operator != (const Tensor &) const; - - /** - * Add another tensor. - */ - Tensor & operator += (const Tensor &); - - /** - * Subtract another tensor. - */ - Tensor & operator -= (const Tensor &); - - /** - * Scale the tensor by factor, - * i.e. multiply all components by - * factor. - */ - Tensor & operator *= (const Number factor); - - /** - * Scale the vector by - * 1/factor. - */ - Tensor & operator /= (const Number factor); - - /** - * Add two tensors. If possible, you - * should use operator += - * instead since this does not need the - * creation of a temporary. - */ - Tensor operator + (const Tensor &) const; - - /** - * Subtract two tensors. If possible, - * you should use operator -= - * instead since this does not need the - * creation of a temporary. - */ - Tensor operator - (const Tensor &) const; - - /** - * Unary minus operator. Negate all - * entries of a tensor. - */ - Tensor operator - () const; - - /** - * Return the Frobenius-norm of a tensor, - * i.e. the square root of the sum of - * squares of all entries. - */ - real_type norm () const; - - /** - * Return the square of the - * Frobenius-norm of a tensor, - * i.e. the - * sum of squares of all entries. - * - * This function mainly exists - * because it makes computing the - * norm simpler recursively, but - * may also be useful in other - * contexts. - */ - real_type norm_square () const; - - /** - * Fill a vector with all tensor elements. - * - * This function unrolls all - * tensor entries into a single, - * linearly numbered vector. As - * usual in C++, the rightmost - * index of the tensor marches fastest. - */ - template - void unroll (Vector & result) const; - - - /** - * Reset all values to zero. - * - * Note that this is partly inconsistent - * with the semantics of the @p clear() - * member functions of the STL and of - * several other classes within deal.II - * which not only reset the values of - * stored elements to zero, but release - * all memory and return the object into - * a virginial state. However, since the - * size of objects of the present type is - * determined by its template parameters, - * resizing is not an option, and indeed - * the state where all elements have a - * zero value is the state right after - * construction of such an object. - */ - void clear (); - - /** - * Determine an estimate for the - * memory consumption (in bytes) - * of this object. - */ - static std::size_t memory_consumption (); - - /** - * Exception. - */ - DeclException1 (ExcInvalidTensorIndex, - int, - << "Invalid tensor index " << arg1); - - /** - * Read or write the data of this object to or - * from a stream for the purpose of serialization - */ - template - void serialize(Archive & ar, const unsigned int version); - - private: - /** - * Array of tensors holding the - * subelements. - */ - Tensor subtensor[dim]; - - /** - * Help function for unroll. - */ - template - void unroll_recursion(Vector &result, - unsigned int &start_index) const; - - // make the following class a - // friend to this class. in principle, - // it would suffice if otherrank==rank+1, - // but then the compiler complains - // that this be an explicit specialization - // which is not what we want - // - // also, it would be sufficient to make - // the function unroll_loops a friend, - // but that seems to be impossible as well. - template friend class Tensor; +public: + /** + * Provide a way to get the + * dimension of an object without + * explicit knowledge of it's + * data type. Implementation is + * this way instead of providing + * a function dimension() + * because now it is possible to + * get the dimension at compile + * time without the expansion and + * preevaluation of an inlined + * function; the compiler may + * therefore produce more + * efficient code and you may use + * this value to declare other + * data types. + */ + static const unsigned int dimension = dim; + + /** + * Publish the rank of this tensor to + * the outside world. + */ + static const unsigned int rank = rank_; + + /** + * Type of stored objects. This + * is a tensor of lower rank. + */ + typedef Tensor value_type; + + /** + * Declare a type that has holds + * real-valued numbers with the same + * precision as the template argument to + * this class. For std::complex, + * this corresponds to type number, and + * it is equal to Number for all other + * cases. See also the respective field + * in Vector. + * + * This typedef is used to + * represent the return type of + * norms. + */ + typedef typename numbers::NumberTraits::real_type real_type; + + /** + * Declare an array type which + * can be used to initialize an + * object of this type + * statically. + */ + typedef typename Tensor::array_type array_type[dim]; + + /** + * Constructor. Initialize all entries + * to zero. + */ + Tensor (); + + /** + * Copy constructor, where the data is + * copied from a C-style array. + */ + Tensor (const array_type &initializer); + + /** + * Conversion operator from tensor of + * tensors. + */ + Tensor (const Tensor<1,dim,Tensor > &tensor_in); + + /** + * Conversion operator to tensor of + * tensors. + */ + operator Tensor<1,dim,Tensor > () const; + + /** + * Read-Write access operator. + */ + Tensor &operator [] (const unsigned int i); + + /** + * Read-only access operator. + */ + const Tensor &operator [] (const unsigned int i) const; + + /** + * Assignment operator. + */ + Tensor &operator = (const Tensor &); + + /** + * This operator assigns a scalar + * to a tensor. To avoid + * confusion with what exactly it + * means to assign a scalar value + * to a tensor, zero is the only + * value allowed for d, + * allowing the intuitive + * notation t=0 to reset + * all elements of the tensor to + * zero. + */ + Tensor &operator = (const Number d); + + /** + * Test for equality of two tensors. + */ + bool operator == (const Tensor &) const; + + /** + * Test for inequality of two tensors. + */ + bool operator != (const Tensor &) const; + + /** + * Add another tensor. + */ + Tensor &operator += (const Tensor &); + + /** + * Subtract another tensor. + */ + Tensor &operator -= (const Tensor &); + + /** + * Scale the tensor by factor, + * i.e. multiply all components by + * factor. + */ + Tensor &operator *= (const Number factor); + + /** + * Scale the vector by + * 1/factor. + */ + Tensor &operator /= (const Number factor); + + /** + * Add two tensors. If possible, you + * should use operator += + * instead since this does not need the + * creation of a temporary. + */ + Tensor operator + (const Tensor &) const; + + /** + * Subtract two tensors. If possible, + * you should use operator -= + * instead since this does not need the + * creation of a temporary. + */ + Tensor operator - (const Tensor &) const; + + /** + * Unary minus operator. Negate all + * entries of a tensor. + */ + Tensor operator - () const; + + /** + * Return the Frobenius-norm of a tensor, + * i.e. the square root of the sum of + * squares of all entries. + */ + real_type norm () const; + + /** + * Return the square of the + * Frobenius-norm of a tensor, + * i.e. the + * sum of squares of all entries. + * + * This function mainly exists + * because it makes computing the + * norm simpler recursively, but + * may also be useful in other + * contexts. + */ + real_type norm_square () const; + + /** + * Fill a vector with all tensor elements. + * + * This function unrolls all + * tensor entries into a single, + * linearly numbered vector. As + * usual in C++, the rightmost + * index of the tensor marches fastest. + */ + template + void unroll (Vector &result) const; + + + /** + * Reset all values to zero. + * + * Note that this is partly inconsistent + * with the semantics of the @p clear() + * member functions of the STL and of + * several other classes within deal.II + * which not only reset the values of + * stored elements to zero, but release + * all memory and return the object into + * a virginial state. However, since the + * size of objects of the present type is + * determined by its template parameters, + * resizing is not an option, and indeed + * the state where all elements have a + * zero value is the state right after + * construction of such an object. + */ + void clear (); + + /** + * Determine an estimate for the + * memory consumption (in bytes) + * of this object. + */ + static std::size_t memory_consumption (); + + /** + * Exception. + */ + DeclException1 (ExcInvalidTensorIndex, + int, + << "Invalid tensor index " << arg1); + + /** + * Read or write the data of this object to or + * from a stream for the purpose of serialization + */ + template + void serialize(Archive &ar, const unsigned int version); + +private: + /** + * Array of tensors holding the + * subelements. + */ + Tensor subtensor[dim]; + + /** + * Help function for unroll. + */ + template + void unroll_recursion(Vector &result, + unsigned int &start_index) const; + + // make the following class a + // friend to this class. in principle, + // it would suffice if otherrank==rank+1, + // but then the compiler complains + // that this be an explicit specialization + // which is not what we want + // + // also, it would be sufficient to make + // the function unroll_loops a friend, + // but that seems to be impossible as well. + template friend class Tensor; }; @@ -351,7 +351,7 @@ Tensor::Tensor template inline Tensor::operator - Tensor<1,dim,Tensor > () const +Tensor<1,dim,Tensor > () const { return Tensor<1,dim,Tensor > (subtensor); } @@ -360,7 +360,7 @@ Tensor::operator template inline -typename Tensor::value_type& +typename Tensor::value_type & Tensor::operator[] (const unsigned int i) { Assert (i::operator[] (const unsigned int i) template inline -const typename Tensor::value_type& +const typename Tensor::value_type & Tensor::operator[] (const unsigned int i) const { Assert (i template inline void -Tensor::serialize(Archive & ar, const unsigned int) +Tensor::serialize(Archive &ar, const unsigned int) { - ar & subtensor; + ar &subtensor; } #endif // DOXYGEN @@ -619,7 +619,7 @@ Tensor::serialize(Archive & ar, const unsigned int) */ template inline -std::ostream & operator << (std::ostream &out, const Tensor &p) +std::ostream &operator << (std::ostream &out, const Tensor &p) { for (unsigned int i=0; i &p */ template inline -std::ostream & operator << (std::ostream &out, const Tensor &p) +std::ostream &operator << (std::ostream &out, const Tensor &p) { out << p[0]; @@ -701,7 +701,7 @@ operator * (const Tensor<1,dim,Number> &src1, template inline Number double_contract (const Tensor<2, dim, Number> &src1, - const Tensor<2, dim, Number> &src2) + const Tensor<2, dim, Number> &src2) { Number res = 0.; for (unsigned int i=0; i &dest, switch (index1) { - case 1: - switch (index2) - { - case 1: - for (unsigned int i=0; i::ExcInvalidTensorIndex (index2))); - }; - break; - case 2: - switch (index2) - { - case 1: - for (unsigned int i=0; i::ExcInvalidTensorIndex (index2))); - }; - break; - - default: - Assert (false, (typename Tensor<2,dim,Number>::ExcInvalidTensorIndex (index1))); + case 1: + switch (index2) + { + case 1: + for (unsigned int i=0; i::ExcInvalidTensorIndex (index2))); + }; + break; + case 2: + switch (index2) + { + case 1: + for (unsigned int i=0; i::ExcInvalidTensorIndex (index2))); + }; + break; + + default: + Assert (false, (typename Tensor<2,dim,Number>::ExcInvalidTensorIndex (index1))); }; } @@ -969,30 +969,30 @@ void contract (Tensor<2,dim,Number> &dest, switch (index1) { - case 1: - for (unsigned int i=0; i::ExcInvalidTensorIndex (index1))); + case 1: + for (unsigned int i=0; i::ExcInvalidTensorIndex (index1))); }; } @@ -1042,78 +1042,78 @@ void contract (Tensor<3,dim,Number> &dest, switch (index1) { - case 1: - switch (index2) - { - case 1: - for (unsigned int i=0; i::ExcInvalidTensorIndex (index2))); - } - - break; - case 2: - switch (index2) - { - case 1: - for (unsigned int i=0; i::ExcInvalidTensorIndex (index2))); - } - - break; - case 3: - switch (index2) - { - case 1: - for (unsigned int i=0; i::ExcInvalidTensorIndex (index2))); - } - - break; - default: - Assert (false, - (typename Tensor<3,dim,Number>::ExcInvalidTensorIndex (index1))); + case 1: + switch (index2) + { + case 1: + for (unsigned int i=0; i::ExcInvalidTensorIndex (index2))); + } + + break; + case 2: + switch (index2) + { + case 1: + for (unsigned int i=0; i::ExcInvalidTensorIndex (index2))); + } + + break; + case 3: + switch (index2) + { + case 1: + for (unsigned int i=0; i::ExcInvalidTensorIndex (index2))); + } + + break; + default: + Assert (false, + (typename Tensor<3,dim,Number>::ExcInvalidTensorIndex (index1))); } } @@ -1260,9 +1260,9 @@ void double_contract (Tensor<2,dim,Number> &dest, */ template inline -Number contract3 (const Tensor<1,dim,Number>& u, - const Tensor<2,dim,Number>& A, - const Tensor<1,dim,Number>& v) +Number contract3 (const Tensor<1,dim,Number> &u, + const Tensor<2,dim,Number> &A, + const Tensor<1,dim,Number> &v) { Number result = 0.; @@ -1291,7 +1291,7 @@ contract3 (const Tensor<1,dim,Number> &t1, for (unsigned int i=0; i &t1, for (unsigned int i=0; i inline Number determinant (const Tensor<2,3,Number> &t) { - // use exactly the same expression with the - // same order of operations as for the inverse - // to let the compiler use common - // subexpression elimination when using - // determinant and inverse in nearby code + // use exactly the same expression with the + // same order of operations as for the inverse + // to let the compiler use common + // subexpression elimination when using + // determinant and inverse in nearby code const Number t4 = t[0][0]*t[1][1], t6 = t[0][0]*t[1][2], t8 = t[0][1]*t[1][0], @@ -1618,14 +1618,14 @@ template inline Number determinant (const Tensor<2,dim,Number> &t) { - // compute the determinant using the - // Laplace expansion of the - // determinant. this may not be the most - // efficient algorithm, but it does for - // small n. - // - // for some algorithmic simplicity, we use - // the expansion along the last row + // compute the determinant using the + // Laplace expansion of the + // determinant. this may not be the most + // efficient algorithm, but it does for + // small n. + // + // for some algorithmic simplicity, we use + // the expansion along the last row Number det = 0; for (unsigned int k=0; k &t) Number return_tensor [dim][dim]; switch (dim) { - case 1: - return_tensor[0][0] = 1.0/t[0][0]; - break; + case 1: + return_tensor[0][0] = 1.0/t[0][0]; + break; - case 2: - // this is Maple output, - // thus a bit unstructured - { - const Number det = t[0][0]*t[1][1]-t[1][0]*t[0][1]; - const Number t4 = 1.0/det; - return_tensor[0][0] = t[1][1]*t4; - return_tensor[0][1] = -t[0][1]*t4; - return_tensor[1][0] = -t[1][0]*t4; - return_tensor[1][1] = t[0][0]*t4; - break; - } + case 2: + // this is Maple output, + // thus a bit unstructured + { + const Number det = t[0][0]*t[1][1]-t[1][0]*t[0][1]; + const Number t4 = 1.0/det; + return_tensor[0][0] = t[1][1]*t4; + return_tensor[0][1] = -t[0][1]*t4; + return_tensor[1][0] = -t[1][0]*t4; + return_tensor[1][1] = t[0][0]*t4; + break; + } - case 3: - { - const Number t4 = t[0][0]*t[1][1], - t6 = t[0][0]*t[1][2], - t8 = t[0][1]*t[1][0], - t00 = t[0][2]*t[1][0], - t01 = t[0][1]*t[2][0], - t04 = t[0][2]*t[2][0], - det = (t4*t[2][2]-t6*t[2][1]-t8*t[2][2]+ - t00*t[2][1]+t01*t[1][2]-t04*t[1][1]), - t07 = 1.0/det; - return_tensor[0][0] = (t[1][1]*t[2][2]-t[1][2]*t[2][1])*t07; - return_tensor[0][1] = (t[0][2]*t[2][1]-t[0][1]*t[2][2])*t07; - return_tensor[0][2] = (t[0][1]*t[1][2]-t[0][2]*t[1][1])*t07; - return_tensor[1][0] = (t[1][2]*t[2][0]-t[1][0]*t[2][2])*t07; - return_tensor[1][1] = (t[0][0]*t[2][2]-t04)*t07; - return_tensor[1][2] = (t00-t6)*t07; - return_tensor[2][0] = (t[1][0]*t[2][1]-t[1][1]*t[2][0])*t07; - return_tensor[2][1] = (t01-t[0][0]*t[2][1])*t07; - return_tensor[2][2] = (t4-t8)*t07; - - break; - } + case 3: + { + const Number t4 = t[0][0]*t[1][1], + t6 = t[0][0]*t[1][2], + t8 = t[0][1]*t[1][0], + t00 = t[0][2]*t[1][0], + t01 = t[0][1]*t[2][0], + t04 = t[0][2]*t[2][0], + det = (t4*t[2][2]-t6*t[2][1]-t8*t[2][2]+ + t00*t[2][1]+t01*t[1][2]-t04*t[1][1]), + t07 = 1.0/det; + return_tensor[0][0] = (t[1][1]*t[2][2]-t[1][2]*t[2][1])*t07; + return_tensor[0][1] = (t[0][2]*t[2][1]-t[0][1]*t[2][2])*t07; + return_tensor[0][2] = (t[0][1]*t[1][2]-t[0][2]*t[1][1])*t07; + return_tensor[1][0] = (t[1][2]*t[2][0]-t[1][0]*t[2][2])*t07; + return_tensor[1][1] = (t[0][0]*t[2][2]-t04)*t07; + return_tensor[1][2] = (t00-t6)*t07; + return_tensor[2][0] = (t[1][0]*t[2][1]-t[1][1]*t[2][0])*t07; + return_tensor[2][1] = (t01-t[0][0]*t[2][1])*t07; + return_tensor[2][2] = (t4-t8)*t07; + + break; + } - // if desired, take over the - // inversion of a 4x4 tensor - // from the FullMatrix - default: - AssertThrow (false, ExcNotImplemented()); + // if desired, take over the + // inversion of a 4x4 tensor + // from the FullMatrix + default: + AssertThrow (false, ExcNotImplemented()); } return Tensor<2,dim,Number>(return_tensor); } @@ -1813,8 +1813,9 @@ Tensor<2,3,Number> transpose (const Tensor<2,3,Number> &t) { const Number x[3][3] = {{t[0][0], t[1][0], t[2][0]}, - {t[0][1], t[1][1], t[2][1]}, - {t[0][2], t[1][2], t[2][2]}}; + {t[0][1], t[1][1], t[2][1]}, + {t[0][2], t[1][2], t[2][2]} + }; return Tensor<2,3,Number>(x); } diff --git a/deal.II/include/deal.II/base/tensor_base.h b/deal.II/include/deal.II/base/tensor_base.h index 0aae6643f3..6574ddc6f2 100644 --- a/deal.II/include/deal.II/base/tensor_base.h +++ b/deal.II/include/deal.II/base/tensor_base.h @@ -63,236 +63,236 @@ template class Tensor<1,dim,Number>; template class Tensor<0,dim,Number> { - public: - /** - * Provide a way to get the - * dimension of an object without - * explicit knowledge of it's - * data type. Implementation is - * this way instead of providing - * a function dimension() - * because now it is possible to - * get the dimension at compile - * time without the expansion and - * preevaluation of an inlined - * function; the compiler may - * therefore produce more - * efficient code and you may use - * this value to declare other - * data types. - */ - static const unsigned int dimension = dim; - - /** - * Publish the rank of this tensor to - * the outside world. - */ - static const unsigned int rank = 0; - - /** - * Type of stored objects. This - * is a Number for a rank 1 tensor. - */ - - typedef Number value_type; - - /** - * Declare a type that has holds - * real-valued numbers with the same - * precision as the template argument to - * this class. For std::complex, - * this corresponds to type number, and - * it is equal to Number for all other - * cases. See also the respective field - * in Vector. - * - * This typedef is used to - * represent the return type of - * norms. - */ - typedef typename numbers::NumberTraits::real_type real_type; - - /** - * Constructor. Set to zero. - */ - Tensor (); - - /** - * Copy constructor, where the - * data is copied from a C-style - * array. - */ - Tensor (const value_type &initializer); - - /** - * Copy constructor. - */ - Tensor (const Tensor<0,dim,Number> &); - - /** - * Conversion to Number. Since - * rank-0 tensors are scalars, - * this is a natural operation. - */ - operator Number () const; - - /** - * Conversion to Number. Since - * rank-0 tensors are scalars, - * this is a natural operation. - * - * This is the non-const - * conversion operator that - * returns a writable reference. - */ - operator Number& (); - - /** - * Assignment operator. - */ - Tensor<0,dim,Number> & operator = (const Tensor<0,dim,Number> &); - - /** - * Assignment operator. - */ - Tensor<0,dim,Number> & operator = (const Number d); - - /** - * Test for equality of two - * tensors. - */ - bool operator == (const Tensor<0,dim,Number> &) const; - - /** - * Test for inequality of two - * tensors. - */ - bool operator != (const Tensor<0,dim,Number> &) const; - - /** - * Add another vector, i.e. move - * this point by the given - * offset. - */ - Tensor<0,dim,Number> & operator += (const Tensor<0,dim,Number> &); - - /** - * Subtract another vector. - */ - Tensor<0,dim,Number> & operator -= (const Tensor<0,dim,Number> &); - - /** - * Scale the vector by - * factor, i.e. multiply all - * coordinates by factor. - */ - Tensor<0,dim,Number> & operator *= (const Number factor); - - /** - * Scale the vector by 1/factor. - */ - Tensor<0,dim,Number> & operator /= (const Number factor); - - /** - * Returns the scalar product of - * two vectors. - */ - Number operator * (const Tensor<0,dim,Number> &) const; - - /** - * Add two tensors. If possible, - * use operator += instead - * since this does not need to - * copy a point at least once. - */ - Tensor<0,dim,Number> operator + (const Tensor<0,dim,Number> &) const; - - /** - * Subtract two tensors. If - * possible, use operator += - * instead since this does not - * need to copy a point at least - * once. - */ - Tensor<0,dim,Number> operator - (const Tensor<0,dim,Number> &) const; - - /** - * Tensor with inverted entries. - */ - Tensor<0,dim,Number> operator - () const; - - /** - * Return the Frobenius-norm of a - * tensor, i.e. the square root - * of the sum of squares of all - * entries. For the present case - * of rank-1 tensors, this equals - * the usual - * l2 norm of - * the vector. - */ - real_type norm () const; - - /** - * Return the square of the - * Frobenius-norm of a tensor, - * i.e. the square root of the - * sum of squares of all entries. - * - * This function mainly exists - * because it makes computing the - * norm simpler recursively, but - * may also be useful in other - * contexts. - */ - real_type norm_square () const; - - /** - * Reset all values to zero. - * - * Note that this is partly inconsistent - * with the semantics of the @p clear() - * member functions of the STL and of - * several other classes within deal.II - * which not only reset the values of - * stored elements to zero, but release - * all memory and return the object into - * a virginial state. However, since the - * size of objects of the present type is - * determined by its template parameters, - * resizing is not an option, and indeed - * the state where all elements have a - * zero value is the state right after - * construction of such an object. - */ - void clear (); - - /** - * Only tensors with a positive - * dimension are implemented. This - * exception is thrown by the - * constructor if the template - * argument dim is zero or - * less. - * - * @ingroup Exceptions - */ - DeclException1 (ExcDimTooSmall, - int, - << "dim must be positive, but was " << arg1); - - /** - * Read or write the data of this object to or - * from a stream for the purpose of serialization - */ - template - void serialize(Archive & ar, const unsigned int version); - - private: - /** - * The value of this scalar object. - */ - Number value; +public: + /** + * Provide a way to get the + * dimension of an object without + * explicit knowledge of it's + * data type. Implementation is + * this way instead of providing + * a function dimension() + * because now it is possible to + * get the dimension at compile + * time without the expansion and + * preevaluation of an inlined + * function; the compiler may + * therefore produce more + * efficient code and you may use + * this value to declare other + * data types. + */ + static const unsigned int dimension = dim; + + /** + * Publish the rank of this tensor to + * the outside world. + */ + static const unsigned int rank = 0; + + /** + * Type of stored objects. This + * is a Number for a rank 1 tensor. + */ + + typedef Number value_type; + + /** + * Declare a type that has holds + * real-valued numbers with the same + * precision as the template argument to + * this class. For std::complex, + * this corresponds to type number, and + * it is equal to Number for all other + * cases. See also the respective field + * in Vector. + * + * This typedef is used to + * represent the return type of + * norms. + */ + typedef typename numbers::NumberTraits::real_type real_type; + + /** + * Constructor. Set to zero. + */ + Tensor (); + + /** + * Copy constructor, where the + * data is copied from a C-style + * array. + */ + Tensor (const value_type &initializer); + + /** + * Copy constructor. + */ + Tensor (const Tensor<0,dim,Number> &); + + /** + * Conversion to Number. Since + * rank-0 tensors are scalars, + * this is a natural operation. + */ + operator Number () const; + + /** + * Conversion to Number. Since + * rank-0 tensors are scalars, + * this is a natural operation. + * + * This is the non-const + * conversion operator that + * returns a writable reference. + */ + operator Number &(); + + /** + * Assignment operator. + */ + Tensor<0,dim,Number> &operator = (const Tensor<0,dim,Number> &); + + /** + * Assignment operator. + */ + Tensor<0,dim,Number> &operator = (const Number d); + + /** + * Test for equality of two + * tensors. + */ + bool operator == (const Tensor<0,dim,Number> &) const; + + /** + * Test for inequality of two + * tensors. + */ + bool operator != (const Tensor<0,dim,Number> &) const; + + /** + * Add another vector, i.e. move + * this point by the given + * offset. + */ + Tensor<0,dim,Number> &operator += (const Tensor<0,dim,Number> &); + + /** + * Subtract another vector. + */ + Tensor<0,dim,Number> &operator -= (const Tensor<0,dim,Number> &); + + /** + * Scale the vector by + * factor, i.e. multiply all + * coordinates by factor. + */ + Tensor<0,dim,Number> &operator *= (const Number factor); + + /** + * Scale the vector by 1/factor. + */ + Tensor<0,dim,Number> &operator /= (const Number factor); + + /** + * Returns the scalar product of + * two vectors. + */ + Number operator * (const Tensor<0,dim,Number> &) const; + + /** + * Add two tensors. If possible, + * use operator += instead + * since this does not need to + * copy a point at least once. + */ + Tensor<0,dim,Number> operator + (const Tensor<0,dim,Number> &) const; + + /** + * Subtract two tensors. If + * possible, use operator += + * instead since this does not + * need to copy a point at least + * once. + */ + Tensor<0,dim,Number> operator - (const Tensor<0,dim,Number> &) const; + + /** + * Tensor with inverted entries. + */ + Tensor<0,dim,Number> operator - () const; + + /** + * Return the Frobenius-norm of a + * tensor, i.e. the square root + * of the sum of squares of all + * entries. For the present case + * of rank-1 tensors, this equals + * the usual + * l2 norm of + * the vector. + */ + real_type norm () const; + + /** + * Return the square of the + * Frobenius-norm of a tensor, + * i.e. the square root of the + * sum of squares of all entries. + * + * This function mainly exists + * because it makes computing the + * norm simpler recursively, but + * may also be useful in other + * contexts. + */ + real_type norm_square () const; + + /** + * Reset all values to zero. + * + * Note that this is partly inconsistent + * with the semantics of the @p clear() + * member functions of the STL and of + * several other classes within deal.II + * which not only reset the values of + * stored elements to zero, but release + * all memory and return the object into + * a virginial state. However, since the + * size of objects of the present type is + * determined by its template parameters, + * resizing is not an option, and indeed + * the state where all elements have a + * zero value is the state right after + * construction of such an object. + */ + void clear (); + + /** + * Only tensors with a positive + * dimension are implemented. This + * exception is thrown by the + * constructor if the template + * argument dim is zero or + * less. + * + * @ingroup Exceptions + */ + DeclException1 (ExcDimTooSmall, + int, + << "dim must be positive, but was " << arg1); + + /** + * Read or write the data of this object to or + * from a stream for the purpose of serialization + */ + template + void serialize(Archive &ar, const unsigned int version); + +private: + /** + * The value of this scalar object. + */ + Number value; }; @@ -322,345 +322,345 @@ class Tensor<0,dim,Number> template class Tensor<1,dim,Number> { - public: - /** - * Provide a way to get the - * dimension of an object without - * explicit knowledge of it's - * data type. Implementation is - * this way instead of providing - * a function dimension() - * because now it is possible to - * get the dimension at compile - * time without the expansion and - * preevaluation of an inlined - * function; the compiler may - * therefore produce more - * efficient code and you may use - * this value to declare other - * data types. - */ - static const unsigned int dimension = dim; - - /** - * Publish the rank of this tensor to - * the outside world. - */ - static const unsigned int rank = 1; - - /** - * Type of stored objects. This - * is a Number for a rank 1 tensor. - */ - - typedef Number value_type; - - /** - * Declare a type that has holds - * real-valued numbers with the same - * precision as the template argument to - * this class. For std::complex, - * this corresponds to type number, and - * it is equal to Number for all other - * cases. See also the respective field - * in Vector. - * - * This typedef is used to - * represent the return type of - * norms. - */ - typedef typename numbers::NumberTraits::real_type real_type; - - /** - * Declare an array type which can - * be used to initialize statically - * an object of this type. - * - * Avoid warning about zero-sized - * array for dim==0 by - * choosing lunatic value that is - * likely to overflow memory - * limits. - */ - typedef Number array_type[(dim!=0) ? dim : 100000000]; - - /** - * Constructor. Initialize all entries - * to zero if initialize==true; this - * is the default behaviour. - */ - explicit Tensor (const bool initialize = true); - - /** - * Copy constructor, where the - * data is copied from a C-style - * array. - */ - Tensor (const array_type &initializer); - - /** - * Copy constructor. - */ - Tensor (const Tensor<1,dim,Number> &); - - /** - * Read access to the indexth - * coordinate. - * - * Note that the derived - * Point class also provides - * access through the () - * operator for - * backcompatibility. - */ - Number operator [] (const unsigned int index) const; - - /** - * Read and write access to the - * indexth coordinate. - * - * Note that the derived - * Point class also provides - * access through the () - * operator for - * backcompatibility. - */ - Number & operator [] (const unsigned int index); - - /** - * Assignment operator. - */ - Tensor<1,dim,Number> & operator = (const Tensor<1,dim,Number> &); - - /** - * This operator assigns a scalar - * to a tensor. To avoid - * confusion with what exactly it - * means to assign a scalar value - * to a tensor, zero is the only - * value allowed for d, - * allowing the intuitive - * notation t=0 to reset - * all elements of the tensor to - * zero. - */ - Tensor<1,dim,Number> & operator = (const Number d); - - /** - * Test for equality of two - * tensors. - */ - bool operator == (const Tensor<1,dim,Number> &) const; - - /** - * Test for inequality of two - * tensors. - */ - bool operator != (const Tensor<1,dim,Number> &) const; - - /** - * Add another vector, i.e. move - * this point by the given - * offset. - */ - Tensor<1,dim,Number> & operator += (const Tensor<1,dim,Number> &); - - /** - * Subtract another vector. - */ - Tensor<1,dim,Number> & operator -= (const Tensor<1,dim,Number> &); - - /** - * Scale the vector by - * factor, i.e. multiply all - * coordinates by factor. - */ - Tensor<1,dim,Number> & operator *= (const Number factor); - - /** - * Scale the vector by 1/factor. - */ - Tensor<1,dim,Number> & operator /= (const Number factor); - - /** - * Returns the scalar product of - * two vectors. - */ - Number operator * (const Tensor<1,dim,Number> &) const; - - /** - * Add two tensors. If possible, - * use operator += instead - * since this does not need to - * copy a point at least once. - */ - Tensor<1,dim,Number> operator + (const Tensor<1,dim,Number> &) const; - - /** - * Subtract two tensors. If - * possible, use operator += - * instead since this does not - * need to copy a point at least - * once. - */ - Tensor<1,dim,Number> operator - (const Tensor<1,dim,Number> &) const; - - /** - * Tensor with inverted entries. - */ - Tensor<1,dim,Number> operator - () const; - - /** - * Return the Frobenius-norm of a - * tensor, i.e. the square root - * of the sum of squares of all - * entries. For the present case - * of rank-1 tensors, this equals - * the usual - * l2 norm of - * the vector. - */ - real_type norm () const; - - /** - * Return the square of the - * Frobenius-norm of a tensor, - * i.e. the square root of the - * sum of squares of all entries. - * - * This function mainly exists - * because it makes computing the - * norm simpler recursively, but - * may also be useful in other - * contexts. - */ - real_type norm_square () const; - - /** - * Reset all values to zero. - * - * Note that this is partly inconsistent - * with the semantics of the @p clear() - * member functions of the STL and of - * several other classes within deal.II - * which not only reset the values of - * stored elements to zero, but release - * all memory and return the object into - * a virginial state. However, since the - * size of objects of the present type is - * determined by its template parameters, - * resizing is not an option, and indeed - * the state where all elements have a - * zero value is the state right after - * construction of such an object. - */ - void clear (); - - /** - * Fill a vector with all tensor elements. - * - * This function unrolls all - * tensor entries into a single, - * linearly numbered vector. As - * usual in C++, the rightmost - * index marches fastest. - */ - template - void unroll (Vector &result) const; - - /** - * Determine an estimate for - * the memory consumption (in - * bytes) of this - * object. - */ - static std::size_t memory_consumption (); - - /** - * Only tensors with a positive - * dimension are implemented. This - * exception is thrown by the - * constructor if the template - * argument dim is zero or - * less. - * - * @ingroup Exceptions - */ - DeclException1 (ExcDimTooSmall, - int, - << "dim must be positive, but was " << arg1); - - /** - * Read or write the data of this object to or - * from a stream for the purpose of serialization - */ - template - void serialize(Archive & ar, const unsigned int version); - - private: - /** - * Store the values in a simple - * array. For dim==0 store - * one element, because otherways - * the compiler would choke. We - * catch this case in the - * constructor to disallow the - * creation of such an object. - */ - Number values[(dim!=0) ? (dim) : (dim+1)]; - - /** - * Help function for unroll. If - * we have detected an access - * control bug in the compiler, - * this function is declared - * public, otherwise private. Do - * not attempt to use this - * function from outside in any - * case, even if it should be - * public for your compiler. - */ - template - void unroll_recursion (Vector &result, - unsigned int &start_index) const; - - private: - /** - * Make the following classes - * friends to this class. In - * principle, it would suffice if - * otherrank==2, but that is not - * possible in C++ at present. - * - * Also, it would be sufficient - * to make the function - * unroll_loops a friend, but - * that seems to be impossible as - * well. - */ - template friend class dealii::Tensor; - - /** - * Point is allowed access to - * the coordinates. This is - * supposed to improve speed. - */ - friend class Point; +public: + /** + * Provide a way to get the + * dimension of an object without + * explicit knowledge of it's + * data type. Implementation is + * this way instead of providing + * a function dimension() + * because now it is possible to + * get the dimension at compile + * time without the expansion and + * preevaluation of an inlined + * function; the compiler may + * therefore produce more + * efficient code and you may use + * this value to declare other + * data types. + */ + static const unsigned int dimension = dim; + + /** + * Publish the rank of this tensor to + * the outside world. + */ + static const unsigned int rank = 1; + + /** + * Type of stored objects. This + * is a Number for a rank 1 tensor. + */ + + typedef Number value_type; + + /** + * Declare a type that has holds + * real-valued numbers with the same + * precision as the template argument to + * this class. For std::complex, + * this corresponds to type number, and + * it is equal to Number for all other + * cases. See also the respective field + * in Vector. + * + * This typedef is used to + * represent the return type of + * norms. + */ + typedef typename numbers::NumberTraits::real_type real_type; + + /** + * Declare an array type which can + * be used to initialize statically + * an object of this type. + * + * Avoid warning about zero-sized + * array for dim==0 by + * choosing lunatic value that is + * likely to overflow memory + * limits. + */ + typedef Number array_type[(dim!=0) ? dim : 100000000]; + + /** + * Constructor. Initialize all entries + * to zero if initialize==true; this + * is the default behaviour. + */ + explicit Tensor (const bool initialize = true); + + /** + * Copy constructor, where the + * data is copied from a C-style + * array. + */ + Tensor (const array_type &initializer); + + /** + * Copy constructor. + */ + Tensor (const Tensor<1,dim,Number> &); + + /** + * Read access to the indexth + * coordinate. + * + * Note that the derived + * Point class also provides + * access through the () + * operator for + * backcompatibility. + */ + Number operator [] (const unsigned int index) const; + + /** + * Read and write access to the + * indexth coordinate. + * + * Note that the derived + * Point class also provides + * access through the () + * operator for + * backcompatibility. + */ + Number &operator [] (const unsigned int index); + + /** + * Assignment operator. + */ + Tensor<1,dim,Number> &operator = (const Tensor<1,dim,Number> &); + + /** + * This operator assigns a scalar + * to a tensor. To avoid + * confusion with what exactly it + * means to assign a scalar value + * to a tensor, zero is the only + * value allowed for d, + * allowing the intuitive + * notation t=0 to reset + * all elements of the tensor to + * zero. + */ + Tensor<1,dim,Number> &operator = (const Number d); + + /** + * Test for equality of two + * tensors. + */ + bool operator == (const Tensor<1,dim,Number> &) const; + + /** + * Test for inequality of two + * tensors. + */ + bool operator != (const Tensor<1,dim,Number> &) const; + + /** + * Add another vector, i.e. move + * this point by the given + * offset. + */ + Tensor<1,dim,Number> &operator += (const Tensor<1,dim,Number> &); + + /** + * Subtract another vector. + */ + Tensor<1,dim,Number> &operator -= (const Tensor<1,dim,Number> &); + + /** + * Scale the vector by + * factor, i.e. multiply all + * coordinates by factor. + */ + Tensor<1,dim,Number> &operator *= (const Number factor); + + /** + * Scale the vector by 1/factor. + */ + Tensor<1,dim,Number> &operator /= (const Number factor); + + /** + * Returns the scalar product of + * two vectors. + */ + Number operator * (const Tensor<1,dim,Number> &) const; + + /** + * Add two tensors. If possible, + * use operator += instead + * since this does not need to + * copy a point at least once. + */ + Tensor<1,dim,Number> operator + (const Tensor<1,dim,Number> &) const; + + /** + * Subtract two tensors. If + * possible, use operator += + * instead since this does not + * need to copy a point at least + * once. + */ + Tensor<1,dim,Number> operator - (const Tensor<1,dim,Number> &) const; + + /** + * Tensor with inverted entries. + */ + Tensor<1,dim,Number> operator - () const; + + /** + * Return the Frobenius-norm of a + * tensor, i.e. the square root + * of the sum of squares of all + * entries. For the present case + * of rank-1 tensors, this equals + * the usual + * l2 norm of + * the vector. + */ + real_type norm () const; + + /** + * Return the square of the + * Frobenius-norm of a tensor, + * i.e. the square root of the + * sum of squares of all entries. + * + * This function mainly exists + * because it makes computing the + * norm simpler recursively, but + * may also be useful in other + * contexts. + */ + real_type norm_square () const; + + /** + * Reset all values to zero. + * + * Note that this is partly inconsistent + * with the semantics of the @p clear() + * member functions of the STL and of + * several other classes within deal.II + * which not only reset the values of + * stored elements to zero, but release + * all memory and return the object into + * a virginial state. However, since the + * size of objects of the present type is + * determined by its template parameters, + * resizing is not an option, and indeed + * the state where all elements have a + * zero value is the state right after + * construction of such an object. + */ + void clear (); + + /** + * Fill a vector with all tensor elements. + * + * This function unrolls all + * tensor entries into a single, + * linearly numbered vector. As + * usual in C++, the rightmost + * index marches fastest. + */ + template + void unroll (Vector &result) const; + + /** + * Determine an estimate for + * the memory consumption (in + * bytes) of this + * object. + */ + static std::size_t memory_consumption (); + + /** + * Only tensors with a positive + * dimension are implemented. This + * exception is thrown by the + * constructor if the template + * argument dim is zero or + * less. + * + * @ingroup Exceptions + */ + DeclException1 (ExcDimTooSmall, + int, + << "dim must be positive, but was " << arg1); + + /** + * Read or write the data of this object to or + * from a stream for the purpose of serialization + */ + template + void serialize(Archive &ar, const unsigned int version); + +private: + /** + * Store the values in a simple + * array. For dim==0 store + * one element, because otherways + * the compiler would choke. We + * catch this case in the + * constructor to disallow the + * creation of such an object. + */ + Number values[(dim!=0) ? (dim) : (dim+1)]; + + /** + * Help function for unroll. If + * we have detected an access + * control bug in the compiler, + * this function is declared + * public, otherwise private. Do + * not attempt to use this + * function from outside in any + * case, even if it should be + * public for your compiler. + */ + template + void unroll_recursion (Vector &result, + unsigned int &start_index) const; + +private: + /** + * Make the following classes + * friends to this class. In + * principle, it would suffice if + * otherrank==2, but that is not + * possible in C++ at present. + * + * Also, it would be sufficient + * to make the function + * unroll_loops a friend, but + * that seems to be impossible as + * well. + */ + template friend class dealii::Tensor; + + /** + * Point is allowed access to + * the coordinates. This is + * supposed to improve speed. + */ + friend class Point; }; - /** - * Prints the value of this scalar. - */ +/** + * Prints the value of this scalar. + */ template -std::ostream & operator << (std::ostream &out, const Tensor<0,dim,Number> &p); +std::ostream &operator << (std::ostream &out, const Tensor<0,dim,Number> &p); - /** - * Prints the values of this tensor in the - * form x1 x2 x3 etc. - */ +/** + * Prints the values of this tensor in the + * form x1 x2 x3 etc. + */ template -std::ostream & operator << (std::ostream &out, const Tensor<1,dim,Number> &p); +std::ostream &operator << (std::ostream &out, const Tensor<1,dim,Number> &p); #ifndef DOXYGEN @@ -712,7 +712,7 @@ Tensor<0,dim,Number>::operator Number () const template inline -Tensor<0,dim,Number>::operator Number & () +Tensor<0,dim,Number>::operator Number &() { return value; } @@ -721,7 +721,7 @@ Tensor<0,dim,Number>::operator Number & () template inline -Tensor<0,dim,Number> & Tensor<0,dim,Number>::operator = (const Tensor<0,dim,Number> &p) +Tensor<0,dim,Number> &Tensor<0,dim,Number>::operator = (const Tensor<0,dim,Number> &p) { value = p.value; return *this; @@ -731,7 +731,7 @@ Tensor<0,dim,Number> & Tensor<0,dim,Number>::operator = (const Tensor<0,dim,Numb template inline -Tensor<0,dim,Number> & Tensor<0,dim,Number>::operator = (const Number d) +Tensor<0,dim,Number> &Tensor<0,dim,Number>::operator = (const Number d) { value = d; return *this; @@ -759,7 +759,7 @@ bool Tensor<0,dim,Number>::operator != (const Tensor<0,dim,Number> &p) const template inline -Tensor<0,dim,Number> & Tensor<0,dim,Number>::operator += (const Tensor<0,dim,Number> &p) +Tensor<0,dim,Number> &Tensor<0,dim,Number>::operator += (const Tensor<0,dim,Number> &p) { value += p.value; return *this; @@ -769,7 +769,7 @@ Tensor<0,dim,Number> & Tensor<0,dim,Number>::operator += (const Tensor<0,dim,Num template inline -Tensor<0,dim,Number> & Tensor<0,dim,Number>::operator -= (const Tensor<0,dim,Number> &p) +Tensor<0,dim,Number> &Tensor<0,dim,Number>::operator -= (const Tensor<0,dim,Number> &p) { value -= p.value; return *this; @@ -779,7 +779,7 @@ Tensor<0,dim,Number> & Tensor<0,dim,Number>::operator -= (const Tensor<0,dim,Num template inline -Tensor<0,dim,Number> & Tensor<0,dim,Number>::operator *= (const Number s) +Tensor<0,dim,Number> &Tensor<0,dim,Number>::operator *= (const Number s) { value *= s; return *this; @@ -789,7 +789,7 @@ Tensor<0,dim,Number> & Tensor<0,dim,Number>::operator *= (const Number s) template inline -Tensor<0,dim,Number> & Tensor<0,dim,Number>::operator /= (const Number s) +Tensor<0,dim,Number> &Tensor<0,dim,Number>::operator /= (const Number s) { value /= s; return *this; @@ -865,9 +865,9 @@ void Tensor<0,dim,Number>::clear () template template inline -void Tensor<0,dim,Number>::serialize(Archive & ar, const unsigned int) +void Tensor<0,dim,Number>::serialize(Archive &ar, const unsigned int) { - ar & value; + ar &value; } /*---------------------------- Inline functions: Tensor<1,dim,Number> ------------------------*/ @@ -878,11 +878,11 @@ inline Tensor<1,dim,Number>::Tensor (const bool initialize) { if (initialize) - // need to create an object Number() to - // initialize to zero to avoid confusion with - // Tensor::operator=(scalar) when using - // something like - // Tensor<1,dim,Tensor<1,dim,Number> >. + // need to create an object Number() to + // initialize to zero to avoid confusion with + // Tensor::operator=(scalar) when using + // something like + // Tensor<1,dim,Tensor<1,dim,Number> >. for (unsigned int i=0; i!=dim; ++i) values[i] = Number(); } @@ -917,16 +917,16 @@ template <> inline Tensor<1,0,double>::Tensor (const Tensor<1,0,double> &) { - // at some places in the library, - // we have Point<0> for formal - // reasons (e.g., we sometimes have - // Quadrature for faces, so - // we have Quadrature<0> for dim=1, - // and then we have Point<0>). To - // avoid warnings in the above - // function that the loop end check - // always fails, we implement this - // function here + // at some places in the library, + // we have Point<0> for formal + // reasons (e.g., we sometimes have + // Quadrature for faces, so + // we have Quadrature<0> for dim=1, + // and then we have Point<0>). To + // avoid warnings in the above + // function that the loop end check + // always fails, we implement this + // function here } @@ -943,7 +943,7 @@ Number Tensor<1,dim,Number>::operator [] (const unsigned int index) const template inline -Number & Tensor<1,dim,Number>::operator [] (const unsigned int index) +Number &Tensor<1,dim,Number>::operator [] (const unsigned int index) { Assert (index::operator [] (const unsigned int index) template <> inline -Tensor<1,0,double> & Tensor<1,0,double>::operator = (const Tensor<1,0,double> &) +Tensor<1,0,double> &Tensor<1,0,double>::operator = (const Tensor<1,0,double> &) { - // at some places in the library, - // we have Point<0> for formal - // reasons (e.g., we sometimes have - // Quadrature for faces, so - // we have Quadrature<0> for dim=1, - // and then we have Point<0>). To - // avoid warnings in the above - // function that the loop end check - // always fails, we implement this - // function here + // at some places in the library, + // we have Point<0> for formal + // reasons (e.g., we sometimes have + // Quadrature for faces, so + // we have Quadrature<0> for dim=1, + // and then we have Point<0>). To + // avoid warnings in the above + // function that the loop end check + // always fails, we implement this + // function here return *this; } @@ -985,7 +985,7 @@ Tensor<1,dim,Number>::operator = (const Tensor<1,dim,Number> &p) template inline -Tensor<1,dim,Number> & Tensor<1,dim,Number>::operator = (const Number d) +Tensor<1,dim,Number> &Tensor<1,dim,Number>::operator = (const Number d) { Assert (d==Number(0), ExcMessage ("Only assignment with zero is allowed")); (void) d; @@ -1030,7 +1030,7 @@ bool Tensor<1,dim,Number>::operator != (const Tensor<1,dim,Number> &p) const template inline -Tensor<1,dim,Number> & Tensor<1,dim,Number>::operator += (const Tensor<1,dim,Number> &p) +Tensor<1,dim,Number> &Tensor<1,dim,Number>::operator += (const Tensor<1,dim,Number> &p) { for (unsigned int i=0; i & Tensor<1,dim,Number>::operator += (const Tensor<1,dim,Num template inline -Tensor<1,dim,Number> & Tensor<1,dim,Number>::operator -= (const Tensor<1,dim,Number> &p) +Tensor<1,dim,Number> &Tensor<1,dim,Number>::operator -= (const Tensor<1,dim,Number> &p) { for (unsigned int i=0; i & Tensor<1,dim,Number>::operator -= (const Tensor<1,dim,Num template inline -Tensor<1,dim,Number> & Tensor<1,dim,Number>::operator *= (const Number s) +Tensor<1,dim,Number> &Tensor<1,dim,Number>::operator *= (const Number s) { for (unsigned int i=0; i & Tensor<1,dim,Number>::operator *= (const Number s) template inline -Tensor<1,dim,Number> & Tensor<1,dim,Number>::operator /= (const Number s) +Tensor<1,dim,Number> &Tensor<1,dim,Number>::operator /= (const Number s) { for (unsigned int i=0; i::operator * (const Tensor<1,dim,Number> &p) const { - // unroll by hand since this is a - // frequently called function and - // some compilers don't want to - // always unroll the loop in the - // general template + // unroll by hand since this is a + // frequently called function and + // some compilers don't want to + // always unroll the loop in the + // general template switch (dim) { case 1: @@ -1210,9 +1210,9 @@ Tensor<1,dim,Number>::memory_consumption () template template inline -void Tensor<1,dim,Number>::serialize(Archive & ar, const unsigned int) +void Tensor<1,dim,Number>::serialize(Archive &ar, const unsigned int) { - ar & values; + ar &values; } #endif // DOXYGEN @@ -1225,7 +1225,7 @@ void Tensor<1,dim,Number>::serialize(Archive & ar, const unsigned int) */ template inline -std::ostream & operator << (std::ostream &out, const Tensor<0,dim,Number> &p) +std::ostream &operator << (std::ostream &out, const Tensor<0,dim,Number> &p) { out << static_cast(p); return out; @@ -1241,7 +1241,7 @@ std::ostream & operator << (std::ostream &out, const Tensor<0,dim,Number> &p) */ template inline -std::ostream & operator << (std::ostream &out, const Tensor<1,dim,Number> &p) +std::ostream &operator << (std::ostream &out, const Tensor<1,dim,Number> &p) { for (unsigned int i=0; i &p) * @relates Tensor<1,dim,Number> */ inline -std::ostream & operator << (std::ostream &out, const Tensor<1,1,double> &p) +std::ostream &operator << (std::ostream &out, const Tensor<1,1,double> &p) { out << p[0]; diff --git a/deal.II/include/deal.II/base/tensor_function.h b/deal.II/include/deal.II/base/tensor_function.h index 253a161b09..81b0dc008c 100644 --- a/deal.II/include/deal.II/base/tensor_function.h +++ b/deal.II/include/deal.II/base/tensor_function.h @@ -53,81 +53,81 @@ DEAL_II_NAMESPACE_OPEN */ template class TensorFunction : public FunctionTime, - public Subscriptor + public Subscriptor { - public: - /** - * Define typedefs for the return - * types of the value - * functions. - */ - typedef Tensor value_type; - - typedef Tensor gradient_type; - - /** - * Constructor. May take an - * initial value for the time - * variable, which defaults to - * zero. - */ - TensorFunction (const double initial_time = 0.0); - - /** - * Virtual destructor; absolutely - * necessary in this case, as - * classes are usually not used - * by their true type, but rather - * through pointers to this base - * class. - */ - virtual ~TensorFunction (); - - /** - * Return the value of the function - * at the given point. - */ - virtual value_type value (const Point &p) const; - - /** - * Set values to the point - * values of the function at the - * points. It is assumed - * that values already has - * the right size, i.e. the same - * size as the points array. - */ - virtual void value_list (const std::vector > &points, - std::vector &values) const; - - /** - * Return the gradient of the - * function at the given point. - */ - virtual gradient_type gradient (const Point &p) const; - - /** - * Set gradients to the - * gradients of the function at - * the points. It is assumed - * that values already has - * the right size, i.e. the same - * size as the points array. - */ - virtual void gradient_list (const std::vector > &points, - std::vector &gradients) const; - - /** - * Exception - */ - DeclException0 (ExcPureFunctionCalled); - /** - * Exception - */ - DeclException2 (ExcVectorHasWrongSize, - int, int, - << "The vector has size " << arg1 << " but should have " - << arg2 << " elements."); +public: + /** + * Define typedefs for the return + * types of the value + * functions. + */ + typedef Tensor value_type; + + typedef Tensor gradient_type; + + /** + * Constructor. May take an + * initial value for the time + * variable, which defaults to + * zero. + */ + TensorFunction (const double initial_time = 0.0); + + /** + * Virtual destructor; absolutely + * necessary in this case, as + * classes are usually not used + * by their true type, but rather + * through pointers to this base + * class. + */ + virtual ~TensorFunction (); + + /** + * Return the value of the function + * at the given point. + */ + virtual value_type value (const Point &p) const; + + /** + * Set values to the point + * values of the function at the + * points. It is assumed + * that values already has + * the right size, i.e. the same + * size as the points array. + */ + virtual void value_list (const std::vector > &points, + std::vector &values) const; + + /** + * Return the gradient of the + * function at the given point. + */ + virtual gradient_type gradient (const Point &p) const; + + /** + * Set gradients to the + * gradients of the function at + * the points. It is assumed + * that values already has + * the right size, i.e. the same + * size as the points array. + */ + virtual void gradient_list (const std::vector > &points, + std::vector &gradients) const; + + /** + * Exception + */ + DeclException0 (ExcPureFunctionCalled); + /** + * Exception + */ + DeclException2 (ExcVectorHasWrongSize, + int, int, + << "The vector has size " << arg1 << " but should have " + << arg2 << " elements."); }; diff --git a/deal.II/include/deal.II/base/tensor_product_polynomials.h b/deal.II/include/deal.II/base/tensor_product_polynomials.h index 7837939db6..38dd584d1f 100644 --- a/deal.II/include/deal.II/base/tensor_product_polynomials.h +++ b/deal.II/include/deal.II/base/tensor_product_polynomials.h @@ -60,221 +60,221 @@ DEAL_II_NAMESPACE_OPEN template class TensorProductPolynomials { - public: - /** - * Access to the dimension of - * this object, for checking and - * automatic setting of dimension - * in other classes. - */ - static const unsigned int dimension = dim; - - /** - * Constructor. pols is - * a vector of objects that - * should be derived or otherwise - * convertible to one-dimensional - * polynomial objects. It will be - * copied element by element into - * a private variable. - */ - template - TensorProductPolynomials (const std::vector &pols); - - /** - * Prints the list of the indices - * to out. - */ - void output_indices(std::ostream &out) const; - - /** - * Sets the ordering of the - * polynomials. Requires - * renumber.size()==n(). - * Stores a copy of - * renumber. - */ - void set_numbering(const std::vector &renumber); - - /** - * Gives read access to the - * renumber vector. - */ - const std::vector &get_numbering() const; - - /** - * Gives read access to the - * inverse renumber vector. - */ - const std::vector &get_numbering_inverse() const; - - /** - * Computes the value and the - * first and second derivatives - * of each tensor product - * polynomial at unit_point. - * - * The size of the vectors must - * either be equal 0 or equal - * n(). In the first case, the - * function will not compute - * these values. - * - * If you need values or - * derivatives of all tensor - * product polynomials then use - * this function, rather than - * using any of the - * compute_value(), - * compute_grad() or - * compute_grad_grad() - * functions, see below, in a - * loop over all tensor product - * polynomials. - */ - void compute (const Point &unit_point, - std::vector &values, - std::vector > &grads, - std::vector > &grad_grads) const; - - /** - * Computes the value of the - * ith tensor product - * polynomial at - * unit_point. Here i is - * given in tensor product - * numbering. - * - * Note, that using this function - * within a loop over all tensor - * product polynomials is not - * efficient, because then each - * point value of the underlying - * (one-dimensional) polynomials - * is (unnecessarily) computed - * several times. Instead use - * the compute() function with - * values.size()==n() - * to get the point values of all - * tensor polynomials all at once - * and in a much more efficient - * way. - */ - double compute_value (const unsigned int i, - const Point &p) const; - - /** - * Computes the grad of the - * ith tensor product - * polynomial at - * unit_point. Here i is - * given in tensor product - * numbering. - * - * Note, that using this function - * within a loop over all tensor - * product polynomials is not - * efficient, because then each - * derivative value of the - * underlying (one-dimensional) - * polynomials is (unnecessarily) - * computed several times. - * Instead use the compute() - * function, see above, with - * grads.size()==n() - * to get the point value of all - * tensor polynomials all at once - * and in a much more efficient - * way. - */ - Tensor<1,dim> compute_grad (const unsigned int i, - const Point &p) const; - - /** - * Computes the second - * derivative (grad_grad) of the - * ith tensor product - * polynomial at - * unit_point. Here i is - * given in tensor product - * numbering. - * - * Note, that using this function - * within a loop over all tensor - * product polynomials is not - * efficient, because then each - * derivative value of the - * underlying (one-dimensional) - * polynomials is (unnecessarily) - * computed several times. - * Instead use the compute() - * function, see above, with - * grad_grads.size()==n() - * to get the point value of all - * tensor polynomials all at once - * and in a much more efficient - * way. - */ - Tensor<2,dim> compute_grad_grad (const unsigned int i, - const Point &p) const; - - /** - * Returns the number of tensor - * product polynomials. For n - * 1d polynomials this is ndim. - */ - unsigned int n () const; - - - private: - /** - * Copy of the vector pols of - * polynomials given to the - * constructor. - */ - std::vector > polynomials; - - /** - * Number of tensor product - * polynomials. See n(). - */ - unsigned int n_tensor_pols; - - /** - * Index map for reordering the - * polynomials. - */ - std::vector index_map; - - /** - * Index map for reordering the - * polynomials. - */ - std::vector index_map_inverse; - - /** - * Each tensor product polynomial - * i is a product of - * one-dimensional polynomials in - * each space direction. Compute - * the indices of these - * one-dimensional polynomials - * for each space direction, - * given the index i. - */ - // fix to avoid compiler warnings about zero - // length arrays - void compute_index (const unsigned int i, - unsigned int (&indices)[(dim>0?dim:1)]) const; - - /** - * Computes - * xdim for - * unsigned int x. Used in - * the constructor. - */ - static - unsigned int x_to_the_dim (const unsigned int x); +public: + /** + * Access to the dimension of + * this object, for checking and + * automatic setting of dimension + * in other classes. + */ + static const unsigned int dimension = dim; + + /** + * Constructor. pols is + * a vector of objects that + * should be derived or otherwise + * convertible to one-dimensional + * polynomial objects. It will be + * copied element by element into + * a private variable. + */ + template + TensorProductPolynomials (const std::vector &pols); + + /** + * Prints the list of the indices + * to out. + */ + void output_indices(std::ostream &out) const; + + /** + * Sets the ordering of the + * polynomials. Requires + * renumber.size()==n(). + * Stores a copy of + * renumber. + */ + void set_numbering(const std::vector &renumber); + + /** + * Gives read access to the + * renumber vector. + */ + const std::vector &get_numbering() const; + + /** + * Gives read access to the + * inverse renumber vector. + */ + const std::vector &get_numbering_inverse() const; + + /** + * Computes the value and the + * first and second derivatives + * of each tensor product + * polynomial at unit_point. + * + * The size of the vectors must + * either be equal 0 or equal + * n(). In the first case, the + * function will not compute + * these values. + * + * If you need values or + * derivatives of all tensor + * product polynomials then use + * this function, rather than + * using any of the + * compute_value(), + * compute_grad() or + * compute_grad_grad() + * functions, see below, in a + * loop over all tensor product + * polynomials. + */ + void compute (const Point &unit_point, + std::vector &values, + std::vector > &grads, + std::vector > &grad_grads) const; + + /** + * Computes the value of the + * ith tensor product + * polynomial at + * unit_point. Here i is + * given in tensor product + * numbering. + * + * Note, that using this function + * within a loop over all tensor + * product polynomials is not + * efficient, because then each + * point value of the underlying + * (one-dimensional) polynomials + * is (unnecessarily) computed + * several times. Instead use + * the compute() function with + * values.size()==n() + * to get the point values of all + * tensor polynomials all at once + * and in a much more efficient + * way. + */ + double compute_value (const unsigned int i, + const Point &p) const; + + /** + * Computes the grad of the + * ith tensor product + * polynomial at + * unit_point. Here i is + * given in tensor product + * numbering. + * + * Note, that using this function + * within a loop over all tensor + * product polynomials is not + * efficient, because then each + * derivative value of the + * underlying (one-dimensional) + * polynomials is (unnecessarily) + * computed several times. + * Instead use the compute() + * function, see above, with + * grads.size()==n() + * to get the point value of all + * tensor polynomials all at once + * and in a much more efficient + * way. + */ + Tensor<1,dim> compute_grad (const unsigned int i, + const Point &p) const; + + /** + * Computes the second + * derivative (grad_grad) of the + * ith tensor product + * polynomial at + * unit_point. Here i is + * given in tensor product + * numbering. + * + * Note, that using this function + * within a loop over all tensor + * product polynomials is not + * efficient, because then each + * derivative value of the + * underlying (one-dimensional) + * polynomials is (unnecessarily) + * computed several times. + * Instead use the compute() + * function, see above, with + * grad_grads.size()==n() + * to get the point value of all + * tensor polynomials all at once + * and in a much more efficient + * way. + */ + Tensor<2,dim> compute_grad_grad (const unsigned int i, + const Point &p) const; + + /** + * Returns the number of tensor + * product polynomials. For n + * 1d polynomials this is ndim. + */ + unsigned int n () const; + + +private: + /** + * Copy of the vector pols of + * polynomials given to the + * constructor. + */ + std::vector > polynomials; + + /** + * Number of tensor product + * polynomials. See n(). + */ + unsigned int n_tensor_pols; + + /** + * Index map for reordering the + * polynomials. + */ + std::vector index_map; + + /** + * Index map for reordering the + * polynomials. + */ + std::vector index_map_inverse; + + /** + * Each tensor product polynomial + * i is a product of + * one-dimensional polynomials in + * each space direction. Compute + * the indices of these + * one-dimensional polynomials + * for each space direction, + * given the index i. + */ + // fix to avoid compiler warnings about zero + // length arrays + void compute_index (const unsigned int i, + unsigned int (&indices)[(dim>0?dim:1)]) const; + + /** + * Computes + * xdim for + * unsigned int x. Used in + * the constructor. + */ + static + unsigned int x_to_the_dim (const unsigned int x); }; #ifndef DOXYGEN @@ -322,181 +322,181 @@ TensorProductPolynomials::get_numbering_inverse() const template class AnisotropicPolynomials { - public: - /** - * Constructor. pols is a - * table of one-dimensional - * polynomials. The number of - * rows in this table should be - * equal to the space dimension, - * with the elements of each row - * giving the polynomials that - * shall be used in this - * particular coordinate - * direction. These polynomials - * may vary between coordinates, - * as well as their number. - */ - AnisotropicPolynomials (const std::vector > > &pols); - - /** - * Computes the value and the - * first and second derivatives - * of each tensor product - * polynomial at unit_point. - * - * The size of the vectors must - * either be equal 0 or equal - * n_tensor_pols. In the - * first case, the function will - * not compute these values. - * - * If you need values or - * derivatives of all tensor - * product polynomials then use - * this function, rather than - * using any of the - * compute_value, - * compute_grad or - * compute_grad_grad - * functions, see below, in a - * loop over all tensor product - * polynomials. - */ - void compute (const Point &unit_point, - std::vector &values, - std::vector > &grads, - std::vector > &grad_grads) const; - - /** - * Computes the value of the - * ith tensor product - * polynomial at - * unit_point. Here i is - * given in tensor product - * numbering. - * - * Note, that using this function - * within a loop over all tensor - * product polynomials is not - * efficient, because then each - * point value of the underlying - * (one-dimensional) polynomials - * is (unnecessarily) computed - * several times. Instead use - * the compute function, see - * above, with - * values.size()==n_tensor_pols - * to get the point values of all - * tensor polynomials all at once - * and in a much more efficient - * way. - */ - double compute_value (const unsigned int i, - const Point &p) const; - - /** - * Computes the grad of the - * ith tensor product - * polynomial at - * unit_point. Here i is - * given in tensor product - * numbering. - * - * Note, that using this function - * within a loop over all tensor - * product polynomials is not - * efficient, because then each - * derivative value of the - * underlying (one-dimensional) - * polynomials is (unnecessarily) - * computed several times. - * Instead use the compute - * function, see above, with - * grads.size()==n_tensor_pols - * to get the point value of all - * tensor polynomials all at once - * and in a much more efficient - * way. - */ - Tensor<1,dim> compute_grad (const unsigned int i, - const Point &p) const; - - /** - * Computes the second - * derivative (grad_grad) of the - * ith tensor product - * polynomial at - * unit_point. Here i is - * given in tensor product - * numbering. - * - * Note, that using this function - * within a loop over all tensor - * product polynomials is not - * efficient, because then each - * derivative value of the - * underlying (one-dimensional) - * polynomials is (unnecessarily) - * computed several times. - * Instead use the compute - * function, see above, with - * grad_grads.size()==n_tensor_pols - * to get the point value of all - * tensor polynomials all at once - * and in a much more efficient - * way. - */ - Tensor<2,dim> compute_grad_grad (const unsigned int i, - const Point &p) const; - - /** - * Returns the number of tensor - * product polynomials. It is the - * product of the number of - * polynomials in each coordinate - * direction. - */ - unsigned int n () const; - - private: - /** - * Copy of the vector pols of - * polynomials given to the - * constructor. - */ - std::vector > > polynomials; - - /** - * Number of tensor product - * polynomials. This is - * Nx*Ny*Nz, or with terms - * dropped if the number of space - * dimensions is less than 3. - */ - unsigned int n_tensor_pols; - - /** - * Each tensor product polynomial - * @þ{i} is a product of - * one-dimensional polynomials in - * each space direction. Compute - * the indices of these - * one-dimensional polynomials - * for each space direction, - * given the index i. - */ - void compute_index (const unsigned int i, - unsigned int (&indices)[dim]) const; - - /** - * Given the input to the - * constructor, compute - * n_tensor_pols. - */ - static - unsigned int - get_n_tensor_pols (const std::vector > > &pols); +public: + /** + * Constructor. pols is a + * table of one-dimensional + * polynomials. The number of + * rows in this table should be + * equal to the space dimension, + * with the elements of each row + * giving the polynomials that + * shall be used in this + * particular coordinate + * direction. These polynomials + * may vary between coordinates, + * as well as their number. + */ + AnisotropicPolynomials (const std::vector > > &pols); + + /** + * Computes the value and the + * first and second derivatives + * of each tensor product + * polynomial at unit_point. + * + * The size of the vectors must + * either be equal 0 or equal + * n_tensor_pols. In the + * first case, the function will + * not compute these values. + * + * If you need values or + * derivatives of all tensor + * product polynomials then use + * this function, rather than + * using any of the + * compute_value, + * compute_grad or + * compute_grad_grad + * functions, see below, in a + * loop over all tensor product + * polynomials. + */ + void compute (const Point &unit_point, + std::vector &values, + std::vector > &grads, + std::vector > &grad_grads) const; + + /** + * Computes the value of the + * ith tensor product + * polynomial at + * unit_point. Here i is + * given in tensor product + * numbering. + * + * Note, that using this function + * within a loop over all tensor + * product polynomials is not + * efficient, because then each + * point value of the underlying + * (one-dimensional) polynomials + * is (unnecessarily) computed + * several times. Instead use + * the compute function, see + * above, with + * values.size()==n_tensor_pols + * to get the point values of all + * tensor polynomials all at once + * and in a much more efficient + * way. + */ + double compute_value (const unsigned int i, + const Point &p) const; + + /** + * Computes the grad of the + * ith tensor product + * polynomial at + * unit_point. Here i is + * given in tensor product + * numbering. + * + * Note, that using this function + * within a loop over all tensor + * product polynomials is not + * efficient, because then each + * derivative value of the + * underlying (one-dimensional) + * polynomials is (unnecessarily) + * computed several times. + * Instead use the compute + * function, see above, with + * grads.size()==n_tensor_pols + * to get the point value of all + * tensor polynomials all at once + * and in a much more efficient + * way. + */ + Tensor<1,dim> compute_grad (const unsigned int i, + const Point &p) const; + + /** + * Computes the second + * derivative (grad_grad) of the + * ith tensor product + * polynomial at + * unit_point. Here i is + * given in tensor product + * numbering. + * + * Note, that using this function + * within a loop over all tensor + * product polynomials is not + * efficient, because then each + * derivative value of the + * underlying (one-dimensional) + * polynomials is (unnecessarily) + * computed several times. + * Instead use the compute + * function, see above, with + * grad_grads.size()==n_tensor_pols + * to get the point value of all + * tensor polynomials all at once + * and in a much more efficient + * way. + */ + Tensor<2,dim> compute_grad_grad (const unsigned int i, + const Point &p) const; + + /** + * Returns the number of tensor + * product polynomials. It is the + * product of the number of + * polynomials in each coordinate + * direction. + */ + unsigned int n () const; + +private: + /** + * Copy of the vector pols of + * polynomials given to the + * constructor. + */ + std::vector > > polynomials; + + /** + * Number of tensor product + * polynomials. This is + * Nx*Ny*Nz, or with terms + * dropped if the number of space + * dimensions is less than 3. + */ + unsigned int n_tensor_pols; + + /** + * Each tensor product polynomial + * @þ{i} is a product of + * one-dimensional polynomials in + * each space direction. Compute + * the indices of these + * one-dimensional polynomials + * for each space direction, + * given the index i. + */ + void compute_index (const unsigned int i, + unsigned int (&indices)[dim]) const; + + /** + * Given the input to the + * constructor, compute + * n_tensor_pols. + */ + static + unsigned int + get_n_tensor_pols (const std::vector > > &pols); }; /** @} */ @@ -539,16 +539,16 @@ template template TensorProductPolynomials:: TensorProductPolynomials(const std::vector &pols) - : - polynomials (pols.begin(), pols.end()), - n_tensor_pols(x_to_the_dim(pols.size())), - index_map(n_tensor_pols), - index_map_inverse(n_tensor_pols) + : + polynomials (pols.begin(), pols.end()), + n_tensor_pols(x_to_the_dim(pols.size())), + index_map(n_tensor_pols), + index_map_inverse(n_tensor_pols) { - // per default set this index map - // to identity. This map can be - // changed by the user through the - // set_numbering() function + // per default set this index map + // to identity. This map can be + // changed by the user through the + // set_numbering() function for (unsigned int i=0; imutable to * allow such access. */ - T & get (); + T &get (); /** * Conversion operator that simply converts the thread-local object @@ -104,7 +104,7 @@ namespace Threads * calling the get() member function; it's purpose is to make the * TLS object look more like the object it is storing. */ - operator T & (); + operator T &(); /** * Copy the given argument into the storage space used to represent @@ -118,7 +118,7 @@ namespace Threads * * @return The current object, after the changes have been made **/ - ThreadLocalStorage & operator = (const T &t); + ThreadLocalStorage &operator = (const T &t); private: #if DEAL_II_USE_MT == 1 @@ -145,16 +145,16 @@ namespace Threads template inline ThreadLocalStorage::ThreadLocalStorage(const T &t) - : - data (t) + : + data (t) {} template inline ThreadLocalStorage::ThreadLocalStorage(const ThreadLocalStorage &t) - : - data (t) + : + data (t) {} @@ -174,7 +174,7 @@ namespace Threads template inline - ThreadLocalStorage::operator T& () + ThreadLocalStorage::operator T &() { return get(); } diff --git a/deal.II/include/deal.II/base/thread_management.h b/deal.II/include/deal.II/base/thread_management.h index d2f83f0333..2a5a358407 100644 --- a/deal.II/include/deal.II/base/thread_management.h +++ b/deal.II/include/deal.II/base/thread_management.h @@ -58,552 +58,573 @@ DEAL_II_NAMESPACE_OPEN */ namespace Threads { -/** - * This class is used instead of a true lock class when not using - * multithreading. It allows to write programs such that they start new - * threads and/or lock objects in multithreading mode, and use dummy thread - * management and synchronisation classes instead when running in - * single-thread mode. Specifically, the new_thread functions only - * call the function but wait for it to return instead of running in on - * another thread, and the mutices do nothing really. The only reason to - * provide such a function is that the program can be compiled both in MT and - * non-MT mode without difference. - * - * @author Wolfgang Bangerth, 2000, 2003 - */ + /** + * This class is used instead of a true lock class when not using + * multithreading. It allows to write programs such that they start new + * threads and/or lock objects in multithreading mode, and use dummy thread + * management and synchronisation classes instead when running in + * single-thread mode. Specifically, the new_thread functions only + * call the function but wait for it to return instead of running in on + * another thread, and the mutices do nothing really. The only reason to + * provide such a function is that the program can be compiled both in MT and + * non-MT mode without difference. + * + * @author Wolfgang Bangerth, 2000, 2003 + */ class DummyThreadMutex { + public: + /** + * Scoped lock class. When you + * declare an object of this + * type, you have to pass it a + * mutex, which is locked in + * the constructor of this + * class and unlocked in the + * destructor. The lock is thus + * held during the entire + * lifetime of this object, + * i.e. until the end of the + * present scope, which + * explains where the name + * comes from. This pattern of + * using locks with mutexes + * follows the + * resource-acquisition-is-initialization + * pattern, and was used first + * for mutexes by Doug + * Schmidt. It has the + * advantage that locking a + * mutex this way is + * thread-safe, i.e. when an + * exception is thrown between + * the locking and unlocking + * point, the destructor makes + * sure that the mutex is + * unlocked; this would not + * automatically be the case + * when you lock and unlock the + * mutex "by hand", i.e. using + * acquire and release. + */ + class ScopedLock + { public: - /** - * Scoped lock class. When you - * declare an object of this - * type, you have to pass it a - * mutex, which is locked in - * the constructor of this - * class and unlocked in the - * destructor. The lock is thus - * held during the entire - * lifetime of this object, - * i.e. until the end of the - * present scope, which - * explains where the name - * comes from. This pattern of - * using locks with mutexes - * follows the - * resource-acquisition-is-initialization - * pattern, and was used first - * for mutexes by Doug - * Schmidt. It has the - * advantage that locking a - * mutex this way is - * thread-safe, i.e. when an - * exception is thrown between - * the locking and unlocking - * point, the destructor makes - * sure that the mutex is - * unlocked; this would not - * automatically be the case - * when you lock and unlock the - * mutex "by hand", i.e. using - * acquire and release. - */ - class ScopedLock - { - public: - /** - * Constructor. Lock the - * mutex. Since this is a - * dummy mutex class, this - * of course does nothing. - */ - ScopedLock (DummyThreadMutex &) {} - - /** - * Destructor. Unlock the - * mutex. Since this is a - * dummy mutex class, this - * of course does nothing. - */ - ~ScopedLock () {} - }; - - /** - * Simulate acquisition of the - * mutex. As this class does - * nothing really, this - * function does nothing as - * well. - */ - inline void acquire () const {} - - /** - * Simulate release of the - * mutex. As this class does - * nothing really, this - * function does nothing as - * well. - */ - inline void release () const {} + /** + * Constructor. Lock the + * mutex. Since this is a + * dummy mutex class, this + * of course does nothing. + */ + ScopedLock (DummyThreadMutex &) {} + + /** + * Destructor. Unlock the + * mutex. Since this is a + * dummy mutex class, this + * of course does nothing. + */ + ~ScopedLock () {} + }; + + /** + * Simulate acquisition of the + * mutex. As this class does + * nothing really, this + * function does nothing as + * well. + */ + inline void acquire () const {} + + /** + * Simulate release of the + * mutex. As this class does + * nothing really, this + * function does nothing as + * well. + */ + inline void release () const {} }; -/** - * This class is used in single threaded mode instead of a class - * implementing real condition variable semantics. It allows to write - * programs such that they start new threads and/or lock objects in - * multithreading mode, and use dummy thread management and - * synchronisation classes instead when running in single-thread - * mode. Specifically, the new_thread functions only call the function - * but wait for it to return instead of running in on another thread, - * and the mutices do nothing really. The only reason to provide such - * a function is that the program can be compiled both in MT and - * non-MT mode without difference. - * - * In this particular case, just as with mutexes, the functions do - * nothing, and by this provide the same semantics of condition - * variables as in multi-threaded mode. - * - * @author Wolfgang Bangerth, 2003 - */ + /** + * This class is used in single threaded mode instead of a class + * implementing real condition variable semantics. It allows to write + * programs such that they start new threads and/or lock objects in + * multithreading mode, and use dummy thread management and + * synchronisation classes instead when running in single-thread + * mode. Specifically, the new_thread functions only call the function + * but wait for it to return instead of running in on another thread, + * and the mutices do nothing really. The only reason to provide such + * a function is that the program can be compiled both in MT and + * non-MT mode without difference. + * + * In this particular case, just as with mutexes, the functions do + * nothing, and by this provide the same semantics of condition + * variables as in multi-threaded mode. + * + * @author Wolfgang Bangerth, 2003 + */ class DummyThreadCondition { - public: - /** - * Signal to a single listener - * that a condition has been - * met, i.e. that some data - * will now be available. Since - * in single threaded mode, - * this function of course does - * nothing. - */ - inline void signal () const {} - - /** - * Signal to multiple listener - * that a condition has been - * met, i.e. that some data - * will now be available. Since - * in single threaded mode, - * this function of course does - * nothing. - */ - inline void broadcast () const {} - - /** - * Wait for the condition to be - * signalled. Signal variables - * need to be guarded by a - * mutex which needs to be - * given to this function as an - * argument, see the man page - * of posix_cond_wait for a - * description of the - * mechanisms. Since in single - * threaded mode, this function - * of course does nothing, but - * returns immediately. - */ - inline void wait (DummyThreadMutex &) const {} + public: + /** + * Signal to a single listener + * that a condition has been + * met, i.e. that some data + * will now be available. Since + * in single threaded mode, + * this function of course does + * nothing. + */ + inline void signal () const {} + + /** + * Signal to multiple listener + * that a condition has been + * met, i.e. that some data + * will now be available. Since + * in single threaded mode, + * this function of course does + * nothing. + */ + inline void broadcast () const {} + + /** + * Wait for the condition to be + * signalled. Signal variables + * need to be guarded by a + * mutex which needs to be + * given to this function as an + * argument, see the man page + * of posix_cond_wait for a + * description of the + * mechanisms. Since in single + * threaded mode, this function + * of course does nothing, but + * returns immediately. + */ + inline void wait (DummyThreadMutex &) const {} }; -/** - * This class is used instead of a true barrier class when not using - * multithreading. It allows to write programs such that they use the - * same class names in multithreading and non-MT mode and thus may be - * compiled with or without thread-support without the need to use - * conditional compilation. Since a barrier class only makes sense in - * non-multithread mode if only one thread is to be synchronised - * (otherwise, the barrier could not be left, since the one thread is - * waiting for some other part of the program to reach a certain point - * of execution), the constructor of this class throws an exception if - * the count argument denoting the number of threads that need to - * be synchronised is not equal to one. - * - * @author Wolfgang Bangerth, 2001 - */ + /** + * This class is used instead of a true barrier class when not using + * multithreading. It allows to write programs such that they use the + * same class names in multithreading and non-MT mode and thus may be + * compiled with or without thread-support without the need to use + * conditional compilation. Since a barrier class only makes sense in + * non-multithread mode if only one thread is to be synchronised + * (otherwise, the barrier could not be left, since the one thread is + * waiting for some other part of the program to reach a certain point + * of execution), the constructor of this class throws an exception if + * the count argument denoting the number of threads that need to + * be synchronised is not equal to one. + * + * @author Wolfgang Bangerth, 2001 + */ class DummyBarrier { - public: - /** - * Constructor. Since barriers - * are only useful in - * single-threaded mode if the - * number of threads to be - * synchronised is one, this - * constructor raises an - * exception if the count - * argument is one. - */ - DummyBarrier (const unsigned int count, - const char *name = 0, - void *arg = 0); - - /** - * Wait for all threads to - * reach this point. Since - * there may only be one - * thread, return immediately, - * i.e. this function is a - * no-op. - */ - int wait () const { return 0; } - - /** - * Dump the state of this - * object. Here: do nothing. - */ - void dump () const {} - - /** @addtogroup Exceptions - * @{ */ - - /** - * Exception. - */ - DeclException1 (ExcBarrierSizeNotUseful, - int, - << "In single-thread mode, other barrier sizes than 1 are not " - << "useful. You gave " << arg1); - - //@} + public: + /** + * Constructor. Since barriers + * are only useful in + * single-threaded mode if the + * number of threads to be + * synchronised is one, this + * constructor raises an + * exception if the count + * argument is one. + */ + DummyBarrier (const unsigned int count, + const char *name = 0, + void *arg = 0); + + /** + * Wait for all threads to + * reach this point. Since + * there may only be one + * thread, return immediately, + * i.e. this function is a + * no-op. + */ + int wait () const + { + return 0; + } + + /** + * Dump the state of this + * object. Here: do nothing. + */ + void dump () const {} + + /** @addtogroup Exceptions + * @{ */ + + /** + * Exception. + */ + DeclException1 (ExcBarrierSizeNotUseful, + int, + << "In single-thread mode, other barrier sizes than 1 are not " + << "useful. You gave " << arg1); + + //@} }; #if DEAL_II_USE_MT == 1 - /** - * Class implementing a - * Mutex. Mutexes are used to lock - * data structures to ensure that - * only a single thread of - * execution can access them at the - * same time. - * - *

Copy semantics

- * - * When copied, the receiving - * object does not receive any - * state from the object being - * copied, i.e. an entirely new - * mutex is created. This is - * consistent with expectations if - * a mutex is used as a member - * variable to lock the other - * member variables of a class: in - * that case, the mutex of the - * copied-to object should only - * guard the members of the - * copied-to object, not the - * members of both the copied-to - * and copied-from object. - * - * @author Wolfgang Bangerth, 2002, 2003, 2009 - */ + /** + * Class implementing a + * Mutex. Mutexes are used to lock + * data structures to ensure that + * only a single thread of + * execution can access them at the + * same time. + * + *

Copy semantics

+ * + * When copied, the receiving + * object does not receive any + * state from the object being + * copied, i.e. an entirely new + * mutex is created. This is + * consistent with expectations if + * a mutex is used as a member + * variable to lock the other + * member variables of a class: in + * that case, the mutex of the + * copied-to object should only + * guard the members of the + * copied-to object, not the + * members of both the copied-to + * and copied-from object. + * + * @author Wolfgang Bangerth, 2002, 2003, 2009 + */ class Mutex { + public: + /** + * Scoped lock class. When you + * declare an object of this + * type, you have to pass it a + * mutex, which is locked in + * the constructor of this + * class and unlocked in the + * destructor. The lock is thus + * held during the entire + * lifetime of this object, + * i.e. until the end of the + * present scope, which + * explains where the name + * comes from. This pattern of + * using locks with mutexes + * follows the + * resource-acquisition-is-initialization + * pattern, and was used first + * for mutexes by Doug + * Schmidt. It has the + * advantage that locking a + * mutex this way is + * thread-safe, i.e. when an + * exception is thrown between + * the locking and unlocking + * point, the destructor makes + * sure that the mutex is + * unlocked; this would not + * automatically be the case + * when you lock and unlock the + * mutex "by hand", i.e. using + * acquire and release. + */ + class ScopedLock + { public: - /** - * Scoped lock class. When you - * declare an object of this - * type, you have to pass it a - * mutex, which is locked in - * the constructor of this - * class and unlocked in the - * destructor. The lock is thus - * held during the entire - * lifetime of this object, - * i.e. until the end of the - * present scope, which - * explains where the name - * comes from. This pattern of - * using locks with mutexes - * follows the - * resource-acquisition-is-initialization - * pattern, and was used first - * for mutexes by Doug - * Schmidt. It has the - * advantage that locking a - * mutex this way is - * thread-safe, i.e. when an - * exception is thrown between - * the locking and unlocking - * point, the destructor makes - * sure that the mutex is - * unlocked; this would not - * automatically be the case - * when you lock and unlock the - * mutex "by hand", i.e. using - * acquire and release. - */ - class ScopedLock + /** + * Constructor. Lock the + * mutex. + */ + ScopedLock (Mutex &m) : mutex(m) + { + mutex.acquire(); + } + + /** + * Destructor. Unlock the + * mutex. Since this is a + * dummy mutex class, this + * of course does nothing. + */ + ~ScopedLock () { - public: - /** - * Constructor. Lock the - * mutex. - */ - ScopedLock (Mutex &m) : mutex(m) { mutex.acquire(); } - - /** - * Destructor. Unlock the - * mutex. Since this is a - * dummy mutex class, this - * of course does nothing. - */ - ~ScopedLock () { mutex.release (); } - - private: - /** - * Store the address of the - * mutex object. - */ - Mutex &mutex; - }; - - /** - * Default constructor. - */ - Mutex () - {} - - /** - * Copy constructor. As - * discussed in this class's - * documentation, no state is - * copied from the object given - * as argument. - */ - Mutex (const Mutex &) - : - mutex() - {} - - - /** - * Acquire a mutex. - */ - inline void acquire () { mutex.lock(); } - - /** - * Release the mutex again. - */ - inline void release () { mutex.unlock(); } + mutex.release (); + } private: - /** - * Data object storing the - * mutex data - */ - std_cxx1x::mutex mutex; - - /** - * Make the class implementing - * condition variables a friend, since - * it needs to access the mutex. - */ - friend class ConditionVariable; + /** + * Store the address of the + * mutex object. + */ + Mutex &mutex; + }; + + /** + * Default constructor. + */ + Mutex () + {} + + /** + * Copy constructor. As + * discussed in this class's + * documentation, no state is + * copied from the object given + * as argument. + */ + Mutex (const Mutex &) + : + mutex() + {} + + + /** + * Acquire a mutex. + */ + inline void acquire () + { + mutex.lock(); + } + + /** + * Release the mutex again. + */ + inline void release () + { + mutex.unlock(); + } + + private: + /** + * Data object storing the + * mutex data + */ + std_cxx1x::mutex mutex; + + /** + * Make the class implementing + * condition variables a friend, since + * it needs to access the mutex. + */ + friend class ConditionVariable; }; - /** - * Class implementing a condition - * variable. The semantics of this - * class and its member functions - * are the same as those of the - * POSIX functions. - * - * @author Wolfgang Bangerth, 2003 - */ + /** + * Class implementing a condition + * variable. The semantics of this + * class and its member functions + * are the same as those of the + * POSIX functions. + * + * @author Wolfgang Bangerth, 2003 + */ class ConditionVariable { - public: - /** - * Signal to a single listener - * that a condition has been - * met, i.e. that some data - * will now be available. - */ - inline void signal () { condition_variable.notify_one(); } - - /** - * Signal to multiple listener - * that a condition has been - * met, i.e. that some data - * will now be available. - */ - inline void broadcast () { condition_variable.notify_all(); } - - /** - * Wait for the condition to be - * signalled. Signal variables - * need to be guarded by a - * mutex which needs to be - * given to this function as an - * argument, see the man page - * of pthread_cond_wait for a - * description of the - * mechanisms. - * - * The mutex is assumed held at the - * entry to this function but is - * released upon exit. - */ - inline void wait (Mutex &mutex) - { - std_cxx1x::unique_lock lock(mutex.mutex, - std_cxx1x::adopt_lock); - condition_variable.wait (lock); - } + public: + /** + * Signal to a single listener + * that a condition has been + * met, i.e. that some data + * will now be available. + */ + inline void signal () + { + condition_variable.notify_one(); + } - private: - /** - * Data object storing the - * necessary data. - */ - std_cxx1x::condition_variable condition_variable; + /** + * Signal to multiple listener + * that a condition has been + * met, i.e. that some data + * will now be available. + */ + inline void broadcast () + { + condition_variable.notify_all(); + } + + /** + * Wait for the condition to be + * signalled. Signal variables + * need to be guarded by a + * mutex which needs to be + * given to this function as an + * argument, see the man page + * of pthread_cond_wait for a + * description of the + * mechanisms. + * + * The mutex is assumed held at the + * entry to this function but is + * released upon exit. + */ + inline void wait (Mutex &mutex) + { + std_cxx1x::unique_lock lock(mutex.mutex, + std_cxx1x::adopt_lock); + condition_variable.wait (lock); + } + + private: + /** + * Data object storing the + * necessary data. + */ + std_cxx1x::condition_variable condition_variable; }; - /** - * Implementation of a thread - * barrier class, based on the - * POSIX thread functions. POSIX - * barriers are a relatively new - * feature and are not supported on - * all systems. - * - * If the configuration detected - * the absence of these functions, - * then barriers will not be - * available, and creating objects - * of this class will result in an - * exception been thrown unless the - * count given for the parties - * waiting for the barrier is equal - * to one (as in this case waiting - * for the barrier is a - * no-operation, and we can - * dispense with the POSIX - * functions at all). The rest of - * the threading functionality will - * be available in its full extent, - * though, even if POSIX barriers - * are not available. - * - * @author Wolfgang Bangerth, 2002 - */ + /** + * Implementation of a thread + * barrier class, based on the + * POSIX thread functions. POSIX + * barriers are a relatively new + * feature and are not supported on + * all systems. + * + * If the configuration detected + * the absence of these functions, + * then barriers will not be + * available, and creating objects + * of this class will result in an + * exception been thrown unless the + * count given for the parties + * waiting for the barrier is equal + * to one (as in this case waiting + * for the barrier is a + * no-operation, and we can + * dispense with the POSIX + * functions at all). The rest of + * the threading functionality will + * be available in its full extent, + * though, even if POSIX barriers + * are not available. + * + * @author Wolfgang Bangerth, 2002 + */ class PosixThreadBarrier { - public: - /** - * Constructor. Initialize the - * underlying POSIX barrier data - * structure. - */ - PosixThreadBarrier (const unsigned int count, - const char *name = 0, - void *arg = 0); - - /** - * Destructor. Release all - * resources. - */ - ~PosixThreadBarrier (); - - /** - * Wait for all threads to - * reach this point. The return - * value is zero for all - * participating threads except - * for one, for which the - * return value is some - * non-zero value. The - * operating system picks the - * special thread by some not - * further known method. - */ - int wait (); - - private: - /** - * Data object storing the - * POSIX data which we need to - * call the POSIX functions. - */ + public: + /** + * Constructor. Initialize the + * underlying POSIX barrier data + * structure. + */ + PosixThreadBarrier (const unsigned int count, + const char *name = 0, + void *arg = 0); + + /** + * Destructor. Release all + * resources. + */ + ~PosixThreadBarrier (); + + /** + * Wait for all threads to + * reach this point. The return + * value is zero for all + * participating threads except + * for one, for which the + * return value is some + * non-zero value. The + * operating system picks the + * special thread by some not + * further known method. + */ + int wait (); + + private: + /** + * Data object storing the + * POSIX data which we need to + * call the POSIX functions. + */ #ifndef DEAL_II_USE_MT_POSIX_NO_BARRIERS - pthread_barrier_t barrier; + pthread_barrier_t barrier; #else - unsigned int count; + unsigned int count; #endif }; - /** - * Provide a backward compatible name (we - * used ThreadMutex up to release 6.1, but - * since it is already in a namespace - * Threads this seems redundant). - * - * @deprecated - */ + /** + * Provide a backward compatible name (we + * used ThreadMutex up to release 6.1, but + * since it is already in a namespace + * Threads this seems redundant). + * + * @deprecated + */ typedef Mutex ThreadMutex; - /** - * Provide a backward compatible name (we - * used ThreadCondition up to release 6.1, - * but since it is already in a namespace - * Threads this seems redundant). - * - * @deprecated - */ + /** + * Provide a backward compatible name (we + * used ThreadCondition up to release 6.1, + * but since it is already in a namespace + * Threads this seems redundant). + * + * @deprecated + */ typedef ConditionVariable ThreadCondition; - /** - * If using POSIX functions, then - * alias the POSIX wrapper classes - * to the names we use throughout - * the library. - */ + /** + * If using POSIX functions, then + * alias the POSIX wrapper classes + * to the names we use throughout + * the library. + */ typedef PosixThreadBarrier Barrier; #else - /** - * In non-multithread mode, the - * mutex and thread management - * classes are aliased to dummy - * classes that actually do - * nothing, in particular not lock - * objects. Likewise for the - * barrier class. - */ + /** + * In non-multithread mode, the + * mutex and thread management + * classes are aliased to dummy + * classes that actually do + * nothing, in particular not lock + * objects. Likewise for the + * barrier class. + */ typedef DummyThreadMutex Mutex; typedef DummyThreadMutex ThreadMutex; - /** - * In non-multithread mode, the - * mutex and thread management - * classes are aliased to dummy - * classes that actually do - * nothing, in particular not lock - * objects. Likewise for the - * barrier class. - */ + /** + * In non-multithread mode, the + * mutex and thread management + * classes are aliased to dummy + * classes that actually do + * nothing, in particular not lock + * objects. Likewise for the + * barrier class. + */ typedef DummyThreadCondition ConditionVariable; typedef DummyThreadCondition ThreadCondition; - /** - * In non-multithread mode, the - * mutex and thread management - * classes are aliased to dummy - * classes that actually do - * nothing, in particular not lock - * objects. Likewise for the - * barrier class. - */ + /** + * In non-multithread mode, the + * mutex and thread management + * classes are aliased to dummy + * classes that actually do + * nothing, in particular not lock + * objects. Likewise for the + * barrier class. + */ typedef DummyBarrier Barrier; #endif @@ -613,215 +634,215 @@ namespace Threads namespace Threads { - /** - * Return the number of presently - * existing threads. This function - * may be useful in a situation - * where a large number of threads - * are concurrently, and you want - * to decide whether creation of - * another thread is reasonable or - * whether running the respective - * operation sequentially is more - * useful since already many more - * threads than processors are - * running. - * - * Note that the function returns - * the total number of threads, not - * those actually running. Some of - * the threads may be waiting for - * locks and mutexes, or may be - * sleeping until they are - * delivered with data to work on. - * - * Upon program start, this number - * is one. It is increased each - * time a thread is created using - * the Threads::new_thread - * function. It is decreased once - * a thread terminates by returning - * from the function that was - * spawned. - * - * Note that this means that only - * threads created and terminated - * through the interfaces of this - * namespace are taken care of. If - * threads are created by directly - * calling the respective functions - * of the operating system - * (e.g. pthread_create - * for the POSIX thread interface), - * or if they are killed - * (e.g. either through - * pthread_exit from the - * spawned thread, or - * pthread_kill from - * another thread), then these - * events are not registered and - * counted for the result of this - * function. Likewise, threads that - * the Threading Building Blocks - * library may have created to work - * on tasks created using the - * Threads::new_task functions are - * not counted. - * - * @ingroup threads - */ + /** + * Return the number of presently + * existing threads. This function + * may be useful in a situation + * where a large number of threads + * are concurrently, and you want + * to decide whether creation of + * another thread is reasonable or + * whether running the respective + * operation sequentially is more + * useful since already many more + * threads than processors are + * running. + * + * Note that the function returns + * the total number of threads, not + * those actually running. Some of + * the threads may be waiting for + * locks and mutexes, or may be + * sleeping until they are + * delivered with data to work on. + * + * Upon program start, this number + * is one. It is increased each + * time a thread is created using + * the Threads::new_thread + * function. It is decreased once + * a thread terminates by returning + * from the function that was + * spawned. + * + * Note that this means that only + * threads created and terminated + * through the interfaces of this + * namespace are taken care of. If + * threads are created by directly + * calling the respective functions + * of the operating system + * (e.g. pthread_create + * for the POSIX thread interface), + * or if they are killed + * (e.g. either through + * pthread_exit from the + * spawned thread, or + * pthread_kill from + * another thread), then these + * events are not registered and + * counted for the result of this + * function. Likewise, threads that + * the Threading Building Blocks + * library may have created to work + * on tasks created using the + * Threads::new_task functions are + * not counted. + * + * @ingroup threads + */ unsigned int n_existing_threads (); - /** - * Return a number used as id of - * this thread. This number is - * generated using the system call - * getpid, or, if it - * exists gettid. The - * result of either is converted to - * an integer and returned by this - * function. - * - * @todo As of now, none of our - * systems seems to support - * gettid, so that part of - * the code is untested yet. - * - * @ingroup threads - */ + /** + * Return a number used as id of + * this thread. This number is + * generated using the system call + * getpid, or, if it + * exists gettid. The + * result of either is converted to + * an integer and returned by this + * function. + * + * @todo As of now, none of our + * systems seems to support + * gettid, so that part of + * the code is untested yet. + * + * @ingroup threads + */ unsigned int this_thread_id (); - /** - * Split the range [begin,end) - * into n_intervals subintervals - * of equal size. The last interval - * will be a little bit larger, if - * the number of elements in the - * whole range is not exactly - * divisible by n_intervals. The - * type of the iterators has to - * fulfill the requirements of a - * forward iterator, - * i.e. operator++ must be - * available, and of course it must - * be assignable. - * - * A list of subintervals is - * returned as a vector of pairs of - * iterators, where each pair - * denotes the range - * [begin[i],end[i]). - * - * @ingroup threads - */ + /** + * Split the range [begin,end) + * into n_intervals subintervals + * of equal size. The last interval + * will be a little bit larger, if + * the number of elements in the + * whole range is not exactly + * divisible by n_intervals. The + * type of the iterators has to + * fulfill the requirements of a + * forward iterator, + * i.e. operator++ must be + * available, and of course it must + * be assignable. + * + * A list of subintervals is + * returned as a vector of pairs of + * iterators, where each pair + * denotes the range + * [begin[i],end[i]). + * + * @ingroup threads + */ template std::vector > split_range (const ForwardIterator &begin, const ForwardIterator &end, const unsigned int n_intervals); - /** - * Split the interval [begin,end) - * into subintervals of (almost) - * equal size. This function works - * mostly as the one before, with - * the difference that instead of - * iterators, now values are taken - * that define the whole interval. - * - * @ingroup threads - */ + /** + * Split the interval [begin,end) + * into subintervals of (almost) + * equal size. This function works + * mostly as the one before, with + * the difference that instead of + * iterators, now values are taken + * that define the whole interval. + * + * @ingroup threads + */ std::vector > split_interval (const unsigned int begin, const unsigned int end, const unsigned int n_intervals); - /** - * @cond internal - */ - - /** - * A namespace in which helper - * functions and the like for the - * threading subsystem are - * implemented. The members of this - * namespace are not meant for - * public use. - * - * @author Wolfgang Bangerth, 2003 - */ + /** + * @cond internal + */ + + /** + * A namespace in which helper + * functions and the like for the + * threading subsystem are + * implemented. The members of this + * namespace are not meant for + * public use. + * + * @author Wolfgang Bangerth, 2003 + */ namespace internal { - /** - * @internal - * If in a sub-thread an - * exception is thrown, it is not - * propagated to the main - * thread. Therefore, the - * exception handler that is - * provided by the applications - * main function or some of its - * other parts will not be able - * to catch these - * exceptions. Therefore, we have - * to provide an exception - * handler in the top function of - * each sub-thread that at least - * catches the exception and - * prints some information, - * rather than letting the - * operating system to just kill - * the program without a - * message. In each of the - * functions we use as entry - * points to new threads, we - * therefore install a try-catch - * block, and if an exception of - * type std::exception is - * caught, it passes over control - * to this function, which will - * then provide some output. - */ + /** + * @internal + * If in a sub-thread an + * exception is thrown, it is not + * propagated to the main + * thread. Therefore, the + * exception handler that is + * provided by the applications + * main function or some of its + * other parts will not be able + * to catch these + * exceptions. Therefore, we have + * to provide an exception + * handler in the top function of + * each sub-thread that at least + * catches the exception and + * prints some information, + * rather than letting the + * operating system to just kill + * the program without a + * message. In each of the + * functions we use as entry + * points to new threads, we + * therefore install a try-catch + * block, and if an exception of + * type std::exception is + * caught, it passes over control + * to this function, which will + * then provide some output. + */ void handle_std_exception (const std::exception &exc); - /** - * @internal - * Same as above, but the type of - * the exception is not derived - * from std::exception, so - * there is little way to provide - * something more useful. - */ + /** + * @internal + * Same as above, but the type of + * the exception is not derived + * from std::exception, so + * there is little way to provide + * something more useful. + */ void handle_unknown_exception (); - /** - * @internal - * The following function is used - * for internal bookkeeping of the - * number of existing threads. It - * is not thought for use in - * application programs, but only - * for use in the template - * functions below. - */ + /** + * @internal + * The following function is used + * for internal bookkeeping of the + * number of existing threads. It + * is not thought for use in + * application programs, but only + * for use in the template + * functions below. + */ void register_thread (); - /** - * @internal - * The following function is used - * for internal bookkeeping of the - * number of existing threads. It - * is not thought for use in - * application programs, but only - * for use in the template - * functions below. - */ + /** + * @internal + * The following function is used + * for internal bookkeeping of the + * number of existing threads. It + * is not thought for use in + * application programs, but only + * for use in the template + * functions below. + */ void deregister_thread (); } - /** - * @endcond - */ + /** + * @endcond + */ } // end declarations of namespace Threads @@ -837,19 +858,19 @@ namespace Threads { typedef std::pair IteratorPair; - // in non-multithreaded mode, we - // often have the case that this - // function is called with - // n_intervals==1, so have a - // shortcut here to handle that - // case efficiently + // in non-multithreaded mode, we + // often have the case that this + // function is called with + // n_intervals==1, so have a + // shortcut here to handle that + // case efficiently if (n_intervals==1) return (std::vector (1, IteratorPair(begin, end))); - // if more than one interval - // requested, do the full work + // if more than one interval + // requested, do the full work const unsigned int n_elements = std::distance (begin, end); const unsigned int n_elements_per_interval = n_elements / n_intervals; const unsigned int residual = n_elements % n_intervals; @@ -862,20 +883,20 @@ namespace Threads if (i != n_intervals-1) { return_values[i].second = return_values[i].first; - // note: the cast is - // performed to avoid a - // warning of gcc that in - // the library `dist>=0' - // is checked (dist has a - // template type, which - // here is unsigned if no - // cast is performed) + // note: the cast is + // performed to avoid a + // warning of gcc that in + // the library `dist>=0' + // is checked (dist has a + // template type, which + // here is unsigned if no + // cast is performed) std::advance (return_values[i].second, static_cast(n_elements_per_interval)); - // distribute residual in - // division equally among - // the first few - // subintervals + // distribute residual in + // division equally among + // the first few + // subintervals if (i < residual) ++return_values[i].second; @@ -894,68 +915,80 @@ namespace Threads { namespace internal { - /** - * @internal - * Given an arbitrary type RT, - * store an element of it and grant - * access to it through functions - * get() and set(). There are - * specializations for reference - * types (which cannot be set), and - * for type void. - */ + /** + * @internal + * Given an arbitrary type RT, + * store an element of it and grant + * access to it through functions + * get() and set(). There are + * specializations for reference + * types (which cannot be set), and + * for type void. + */ template struct return_value { - private: - RT value; - public: - inline return_value () : value() {} + private: + RT value; + public: + inline return_value () : value() {} - inline RT get () const { return value; } - inline void set (RT v) { value = v; } + inline RT get () const + { + return value; + } + inline void set (RT v) + { + value = v; + } }; - /** - * @internal - * Given an arbitrary type RT, - * store an element of it and grant - * access to it through functions - * get() and set(). This is the - * specialization for reference - * types: since they cannot be set - * after construction time, we - * store a pointer instead, that - * holds the address of the object - * being referenced. - */ + /** + * @internal + * Given an arbitrary type RT, + * store an element of it and grant + * access to it through functions + * get() and set(). This is the + * specialization for reference + * types: since they cannot be set + * after construction time, we + * store a pointer instead, that + * holds the address of the object + * being referenced. + */ template struct return_value { - private: - RT * value; - public: - inline return_value () : value(0) {} + private: + RT *value; + public: + inline return_value () : value(0) {} - inline RT & get () const { return *value; } - inline void set (RT & v) { value = &v; } + inline RT &get () const + { + return *value; + } + inline void set (RT &v) + { + value = &v; + } }; - /** - * @internal - * Given an arbitrary type RT, - * store an element of it and grant - * access to it through functions - * get() and set(). This is the - * specialization for type void: - * there is obviously nothing to - * store, so no function set(), and - * a function get() that returns - * void. - */ + /** + * @internal + * Given an arbitrary type RT, + * store an element of it and grant + * access to it through functions + * get() and set(). This is the + * specialization for type void: + * there is obviously nothing to + * store, so no function set(), and + * a function get() that returns + * void. + */ template <> struct return_value { - static inline void get () {} + static inline void get () {} }; } @@ -982,246 +1015,246 @@ namespace Threads namespace internal { - /** - * @internal - * Construct a pointer to non-member - * function based on the template - * arguments, and whether the - * second argument is a const or - * non-const class, dependening on - * which the member function will - * also me const or - * non-const. There are - * specializations of this class - * for each number of arguments. - */ + /** + * @internal + * Construct a pointer to non-member + * function based on the template + * arguments, and whether the + * second argument is a const or + * non-const class, dependening on + * which the member function will + * also me const or + * non-const. There are + * specializations of this class + * for each number of arguments. + */ template ::value> + int length = std_cxx1x::tuple_size::value> struct fun_ptr_helper; - /** - * @internal - * Construct a pointer to non-member - * function based on the template - * arguments. This is the - * specialization for 0 arguments. - */ + /** + * @internal + * Construct a pointer to non-member + * function based on the template + * arguments. This is the + * specialization for 0 arguments. + */ template struct fun_ptr_helper { - typedef RT (type) (); + typedef RT (type) (); }; - /** - * @internal - * Construct a pointer to non-member - * function based on the template - * arguments. This is the - * specialization for 1 argument. - */ + /** + * @internal + * Construct a pointer to non-member + * function based on the template + * arguments. This is the + * specialization for 1 argument. + */ template struct fun_ptr_helper { - typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type); + typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type); }; - /** - * @internal - * Construct a pointer to non-member - * function based on the template - * arguments. This is the - * specialization for 2 arguments. - */ + /** + * @internal + * Construct a pointer to non-member + * function based on the template + * arguments. This is the + * specialization for 2 arguments. + */ template struct fun_ptr_helper { - typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, - typename std_cxx1x::tuple_element<1,ArgList>::type); + typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, + typename std_cxx1x::tuple_element<1,ArgList>::type); }; - /** - * @internal - * Construct a pointer to non-member - * function based on the template - * arguments. This is the - * specialization for 3 arguments. - */ + /** + * @internal + * Construct a pointer to non-member + * function based on the template + * arguments. This is the + * specialization for 3 arguments. + */ template struct fun_ptr_helper { - typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, - typename std_cxx1x::tuple_element<1,ArgList>::type, - typename std_cxx1x::tuple_element<2,ArgList>::type); + typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, + typename std_cxx1x::tuple_element<1,ArgList>::type, + typename std_cxx1x::tuple_element<2,ArgList>::type); }; - /** - * @internal - * Construct a pointer to non-member - * function based on the template - * arguments. This is the - * specialization for 4 arguments. - */ + /** + * @internal + * Construct a pointer to non-member + * function based on the template + * arguments. This is the + * specialization for 4 arguments. + */ template struct fun_ptr_helper { - typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, - typename std_cxx1x::tuple_element<1,ArgList>::type, - typename std_cxx1x::tuple_element<2,ArgList>::type, - typename std_cxx1x::tuple_element<3,ArgList>::type); + typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, + typename std_cxx1x::tuple_element<1,ArgList>::type, + typename std_cxx1x::tuple_element<2,ArgList>::type, + typename std_cxx1x::tuple_element<3,ArgList>::type); }; - /** - * @internal - * Construct a pointer to non-member - * function based on the template - * arguments. This is the - * specialization for 5 arguments. - */ + /** + * @internal + * Construct a pointer to non-member + * function based on the template + * arguments. This is the + * specialization for 5 arguments. + */ template struct fun_ptr_helper { - typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, - typename std_cxx1x::tuple_element<1,ArgList>::type, - typename std_cxx1x::tuple_element<2,ArgList>::type, - typename std_cxx1x::tuple_element<3,ArgList>::type, - typename std_cxx1x::tuple_element<4,ArgList>::type); + typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, + typename std_cxx1x::tuple_element<1,ArgList>::type, + typename std_cxx1x::tuple_element<2,ArgList>::type, + typename std_cxx1x::tuple_element<3,ArgList>::type, + typename std_cxx1x::tuple_element<4,ArgList>::type); }; - /** - * @internal - * Construct a pointer to non-member - * function based on the template - * arguments. This is the - * specialization for 6 arguments. - */ + /** + * @internal + * Construct a pointer to non-member + * function based on the template + * arguments. This is the + * specialization for 6 arguments. + */ template struct fun_ptr_helper { - typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, - typename std_cxx1x::tuple_element<1,ArgList>::type, - typename std_cxx1x::tuple_element<2,ArgList>::type, - typename std_cxx1x::tuple_element<3,ArgList>::type, - typename std_cxx1x::tuple_element<4,ArgList>::type, - typename std_cxx1x::tuple_element<5,ArgList>::type); + typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, + typename std_cxx1x::tuple_element<1,ArgList>::type, + typename std_cxx1x::tuple_element<2,ArgList>::type, + typename std_cxx1x::tuple_element<3,ArgList>::type, + typename std_cxx1x::tuple_element<4,ArgList>::type, + typename std_cxx1x::tuple_element<5,ArgList>::type); }; - /** - * @internal - * Construct a pointer to non-member - * function based on the template - * arguments. This is the - * specialization for 7 arguments. - */ + /** + * @internal + * Construct a pointer to non-member + * function based on the template + * arguments. This is the + * specialization for 7 arguments. + */ template struct fun_ptr_helper { - typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, - typename std_cxx1x::tuple_element<1,ArgList>::type, - typename std_cxx1x::tuple_element<2,ArgList>::type, - typename std_cxx1x::tuple_element<3,ArgList>::type, - typename std_cxx1x::tuple_element<4,ArgList>::type, - typename std_cxx1x::tuple_element<5,ArgList>::type, - typename std_cxx1x::tuple_element<6,ArgList>::type); + typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, + typename std_cxx1x::tuple_element<1,ArgList>::type, + typename std_cxx1x::tuple_element<2,ArgList>::type, + typename std_cxx1x::tuple_element<3,ArgList>::type, + typename std_cxx1x::tuple_element<4,ArgList>::type, + typename std_cxx1x::tuple_element<5,ArgList>::type, + typename std_cxx1x::tuple_element<6,ArgList>::type); }; - /** - * @internal - * Construct a pointer to non-member - * function based on the template - * arguments. This is the - * specialization for 8 arguments. - */ + /** + * @internal + * Construct a pointer to non-member + * function based on the template + * arguments. This is the + * specialization for 8 arguments. + */ template struct fun_ptr_helper { - typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, - typename std_cxx1x::tuple_element<1,ArgList>::type, - typename std_cxx1x::tuple_element<2,ArgList>::type, - typename std_cxx1x::tuple_element<3,ArgList>::type, - typename std_cxx1x::tuple_element<4,ArgList>::type, - typename std_cxx1x::tuple_element<5,ArgList>::type, - typename std_cxx1x::tuple_element<6,ArgList>::type, - typename std_cxx1x::tuple_element<7,ArgList>::type); + typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, + typename std_cxx1x::tuple_element<1,ArgList>::type, + typename std_cxx1x::tuple_element<2,ArgList>::type, + typename std_cxx1x::tuple_element<3,ArgList>::type, + typename std_cxx1x::tuple_element<4,ArgList>::type, + typename std_cxx1x::tuple_element<5,ArgList>::type, + typename std_cxx1x::tuple_element<6,ArgList>::type, + typename std_cxx1x::tuple_element<7,ArgList>::type); }; - /** - * @internal - * Construct a pointer to non-member - * function based on the template - * arguments. This is the - * specialization for 9 arguments. - */ + /** + * @internal + * Construct a pointer to non-member + * function based on the template + * arguments. This is the + * specialization for 9 arguments. + */ template struct fun_ptr_helper { - typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, - typename std_cxx1x::tuple_element<1,ArgList>::type, - typename std_cxx1x::tuple_element<2,ArgList>::type, - typename std_cxx1x::tuple_element<3,ArgList>::type, - typename std_cxx1x::tuple_element<4,ArgList>::type, - typename std_cxx1x::tuple_element<5,ArgList>::type, - typename std_cxx1x::tuple_element<6,ArgList>::type, - typename std_cxx1x::tuple_element<7,ArgList>::type, - typename std_cxx1x::tuple_element<8,ArgList>::type); + typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, + typename std_cxx1x::tuple_element<1,ArgList>::type, + typename std_cxx1x::tuple_element<2,ArgList>::type, + typename std_cxx1x::tuple_element<3,ArgList>::type, + typename std_cxx1x::tuple_element<4,ArgList>::type, + typename std_cxx1x::tuple_element<5,ArgList>::type, + typename std_cxx1x::tuple_element<6,ArgList>::type, + typename std_cxx1x::tuple_element<7,ArgList>::type, + typename std_cxx1x::tuple_element<8,ArgList>::type); }; - /** - * @internal - * Construct a pointer to non-member - * function based on the template - * arguments. This is the - * specialization for 10 arguments. - */ + /** + * @internal + * Construct a pointer to non-member + * function based on the template + * arguments. This is the + * specialization for 10 arguments. + */ template struct fun_ptr_helper { - typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, - typename std_cxx1x::tuple_element<1,ArgList>::type, - typename std_cxx1x::tuple_element<2,ArgList>::type, - typename std_cxx1x::tuple_element<3,ArgList>::type, - typename std_cxx1x::tuple_element<4,ArgList>::type, - typename std_cxx1x::tuple_element<5,ArgList>::type, - typename std_cxx1x::tuple_element<6,ArgList>::type, - typename std_cxx1x::tuple_element<7,ArgList>::type, - typename std_cxx1x::tuple_element<8,ArgList>::type, - typename std_cxx1x::tuple_element<9,ArgList>::type); + typedef RT (type) (typename std_cxx1x::tuple_element<0,ArgList>::type, + typename std_cxx1x::tuple_element<1,ArgList>::type, + typename std_cxx1x::tuple_element<2,ArgList>::type, + typename std_cxx1x::tuple_element<3,ArgList>::type, + typename std_cxx1x::tuple_element<4,ArgList>::type, + typename std_cxx1x::tuple_element<5,ArgList>::type, + typename std_cxx1x::tuple_element<6,ArgList>::type, + typename std_cxx1x::tuple_element<7,ArgList>::type, + typename std_cxx1x::tuple_element<8,ArgList>::type, + typename std_cxx1x::tuple_element<9,ArgList>::type); }; - /** - * @internal - * Construct a pointer to - * non-member function based on the - * template arguments. We do this - * by dispatching to the - * fun_ptr_helper classes that are - * overloaded on the number of - * elements. - * - * Note that the last template - * argument for the - * fun_ptr_helper class is - * automatically computed in the - * default argument to the general - * template. - */ + /** + * @internal + * Construct a pointer to + * non-member function based on the + * template arguments. We do this + * by dispatching to the + * fun_ptr_helper classes that are + * overloaded on the number of + * elements. + * + * Note that the last template + * argument for the + * fun_ptr_helper class is + * automatically computed in the + * default argument to the general + * template. + */ template struct fun_ptr { - typedef typename fun_ptr_helper::type type; + typedef typename fun_ptr_helper::type type; }; } @@ -1231,489 +1264,489 @@ namespace Threads { #if (DEAL_II_USE_MT == 1) - /** - * A class that represents threads. For - * each thread, we create exactly one of - * these objects -- exactly one because - * it carries the returned value of the - * function called on the thread. - * - * While we have only one of these - * objects per thread, several - * Threads::Thread objects may refer to - * this descriptor. - * - * Note, however, that since this - * object holds the location - * where we store the return - * value of the thread, the - * object has to live at least as - * long as the thread - * executes. This means that even - * if all Threads::Thread objects - * that referred to this - * descriptor (through a - * std::shared_ptr) have gone out - * of scope, we must still hold - * on to the object. We do this - * by having the descriptor keep - * a pointer to itself and reset - * it to zero once it is done -- - * effectly keeping the use - * pointer above zero as long as - * work is going on. - * - * To enable the current class to - * obtain a shared_ptr from - * itself, we derive from the - * class - * std::enable_shared_from_this. - */ + /** + * A class that represents threads. For + * each thread, we create exactly one of + * these objects -- exactly one because + * it carries the returned value of the + * function called on the thread. + * + * While we have only one of these + * objects per thread, several + * Threads::Thread objects may refer to + * this descriptor. + * + * Note, however, that since this + * object holds the location + * where we store the return + * value of the thread, the + * object has to live at least as + * long as the thread + * executes. This means that even + * if all Threads::Thread objects + * that referred to this + * descriptor (through a + * std::shared_ptr) have gone out + * of scope, we must still hold + * on to the object. We do this + * by having the descriptor keep + * a pointer to itself and reset + * it to zero once it is done -- + * effectly keeping the use + * pointer above zero as long as + * work is going on. + * + * To enable the current class to + * obtain a shared_ptr from + * itself, we derive from the + * class + * std::enable_shared_from_this. + */ template struct ThreadDescriptor : public std_cxx1x::enable_shared_from_this > { - /** - * A pointer to the current - * object, kept nonzero as - * long as the thread is - * executing to avoid - * destroying the current - * object while we are still - * expecting to write - * something into the return - * value location. - */ - std_cxx1x::shared_ptr > self; - - /** - * An object that represents the - * thread started. - */ - std_cxx1x::thread thread; - - /** - * An object that will hold the value - * returned by the function called on - * the thread. - */ - return_value ret_val; - - /** - * A bool variable that is initially - * false, is set to true when a new - * thread is started, and is set back - * to false once join() has been - * called. - * - * We use this variable to make sure - * we can call join() twice on the - * same thread. For some reason, the - * C++ standard library throws a - * std::system_error exception if one - * tries to call std::thread::join - * twice (and in fact, before the - * second call, std::thread::joinable - * returns false) but this is a - * somewhat desirable thing to do - * because one doesn't have to keep - * track whether join() has been - * called before. Using this - * variable, whenever we have called - * join() before, the variable is set - * to true and we can skip over - * calling std::thread::join() a - * second time. - * - * Further, note that we need no - * mutex for this variable: threads - * can only be join from the thread - * that created it - * originally. Consequently, - * everything that happens in a - * function that does not create - * threads (such as the join() - * function below) looks atomic to - * the outside world. Since we clear - * and test thread_is_active in the - * same function as we call - * std::thread::join, these actions - * are atomic and need no mutex. Of - * course, two threads may call - * join() on the same thread object - * at the same time, but this action - * is undefined anyway since they can - * not both join the same thread. - */ - bool thread_is_active; - - /** - * Default constructor. - */ - ThreadDescriptor () - : - thread_is_active (false) - {} - - - /** - * Start the thread and let - * it put its return value - * into the ret_val object. - * - * Note that we cannot - * already do this in the - * constructor of this class: - * we will call - * enable_shared_from_this::shared_from_this - * which requires that there - * is already a shared_ptr - * that owns this object; - * however, at the time of - * creation, there can of - * course be no shared_ptr - * that owns the object -- it - * is, after all, just being - * created and can not - * already have been handed - * off to a shared_ptr; as a - * consequence, we first - * create the object, give it - * to a shared_ptr, and then - * call shared_from_this. - */ - void start (const std_cxx1x::function &function) - { - // set the self pointer - // and then start the - // thread - self = this->shared_from_this (); - thread_is_active = true; - thread = std_cxx1x::thread (thread_entry_point, function, this); - } + /** + * A pointer to the current + * object, kept nonzero as + * long as the thread is + * executing to avoid + * destroying the current + * object while we are still + * expecting to write + * something into the return + * value location. + */ + std_cxx1x::shared_ptr > self; + + /** + * An object that represents the + * thread started. + */ + std_cxx1x::thread thread; + + /** + * An object that will hold the value + * returned by the function called on + * the thread. + */ + return_value ret_val; + + /** + * A bool variable that is initially + * false, is set to true when a new + * thread is started, and is set back + * to false once join() has been + * called. + * + * We use this variable to make sure + * we can call join() twice on the + * same thread. For some reason, the + * C++ standard library throws a + * std::system_error exception if one + * tries to call std::thread::join + * twice (and in fact, before the + * second call, std::thread::joinable + * returns false) but this is a + * somewhat desirable thing to do + * because one doesn't have to keep + * track whether join() has been + * called before. Using this + * variable, whenever we have called + * join() before, the variable is set + * to true and we can skip over + * calling std::thread::join() a + * second time. + * + * Further, note that we need no + * mutex for this variable: threads + * can only be join from the thread + * that created it + * originally. Consequently, + * everything that happens in a + * function that does not create + * threads (such as the join() + * function below) looks atomic to + * the outside world. Since we clear + * and test thread_is_active in the + * same function as we call + * std::thread::join, these actions + * are atomic and need no mutex. Of + * course, two threads may call + * join() on the same thread object + * at the same time, but this action + * is undefined anyway since they can + * not both join the same thread. + */ + bool thread_is_active; + + /** + * Default constructor. + */ + ThreadDescriptor () + : + thread_is_active (false) + {} + + + /** + * Start the thread and let + * it put its return value + * into the ret_val object. + * + * Note that we cannot + * already do this in the + * constructor of this class: + * we will call + * enable_shared_from_this::shared_from_this + * which requires that there + * is already a shared_ptr + * that owns this object; + * however, at the time of + * creation, there can of + * course be no shared_ptr + * that owns the object -- it + * is, after all, just being + * created and can not + * already have been handed + * off to a shared_ptr; as a + * consequence, we first + * create the object, give it + * to a shared_ptr, and then + * call shared_from_this. + */ + void start (const std_cxx1x::function &function) + { + // set the self pointer + // and then start the + // thread + self = this->shared_from_this (); + thread_is_active = true; + thread = std_cxx1x::thread (thread_entry_point, function, this); + } - /** - * Wait for the thread to end. - */ - void join () + /** + * Wait for the thread to end. + */ + void join () + { + // see if the thread hasn't been + // joined yet. if it has, then + // join() is a no-op + if (thread_is_active == true) { - // see if the thread hasn't been - // joined yet. if it has, then - // join() is a no-op - if (thread_is_active == true) - { - Assert (thread.joinable(), ExcInternalError()); - thread.join (); - thread_is_active = false; - } + Assert (thread.joinable(), ExcInternalError()); + thread.join (); + thread_is_active = false; } + } - private: + private: - /** - * The function that runs on the - * thread. - */ - static - void thread_entry_point (const std_cxx1x::function function, - ThreadDescriptor *descriptor) + /** + * The function that runs on the + * thread. + */ + static + void thread_entry_point (const std_cxx1x::function function, + ThreadDescriptor *descriptor) + { + // call the function + // in question. since an + // exception that is + // thrown from one of the + // called functions will + // not propagate to the + // main thread, it will + // kill the program if + // not treated here + // before we return to + // the operating system's + // thread library + internal::register_thread (); + try + { + call (function, descriptor->ret_val); + } + catch (const std::exception &exc) { - // call the function - // in question. since an - // exception that is - // thrown from one of the - // called functions will - // not propagate to the - // main thread, it will - // kill the program if - // not treated here - // before we return to - // the operating system's - // thread library - internal::register_thread (); - try - { - call (function, descriptor->ret_val); - } - catch (const std::exception &exc) - { - internal::handle_std_exception (exc); - } - catch (...) - { - internal::handle_unknown_exception (); - } - internal::deregister_thread (); - - // once we are done, - // we can release our - // hold on the thread - // descriptor - // object. if all - // Threads::Thread - // object that - // pointed here - // should have gone - // out of scope - // already, then this - // will trigger - // destruction of the - // descriptor object - Assert (descriptor->self.use_count () > 0, - ExcInternalError()); - descriptor->self.reset (); + internal::handle_std_exception (exc); } + catch (...) + { + internal::handle_unknown_exception (); + } + internal::deregister_thread (); + + // once we are done, + // we can release our + // hold on the thread + // descriptor + // object. if all + // Threads::Thread + // object that + // pointed here + // should have gone + // out of scope + // already, then this + // will trigger + // destruction of the + // descriptor object + Assert (descriptor->self.use_count () > 0, + ExcInternalError()); + descriptor->self.reset (); + } }; #else - /** - * A class that represents threads. For - * each thread, we create exactly one of - * these objects -- exactly one because - * it carries the returned value of the - * function called on the thread. - * - * While we have only one of these - * objects per thread, several - * Threads::Thread objects may refer to - * this descriptor. - */ + /** + * A class that represents threads. For + * each thread, we create exactly one of + * these objects -- exactly one because + * it carries the returned value of the + * function called on the thread. + * + * While we have only one of these + * objects per thread, several + * Threads::Thread objects may refer to + * this descriptor. + */ template struct ThreadDescriptor { - /** - * An object that will hold the value - * returned by the function called on - * the thread. - */ - return_value ret_val; - - /** - * Start the thread and - * let it put its return value into - * the ret_val object. - */ - void start (const std_cxx1x::function &function) - { - call (function, ret_val); - } + /** + * An object that will hold the value + * returned by the function called on + * the thread. + */ + return_value ret_val; + + /** + * Start the thread and + * let it put its return value into + * the ret_val object. + */ + void start (const std_cxx1x::function &function) + { + call (function, ret_val); + } - /** - * Wait for the thread to end. - */ - void join () - {} + /** + * Wait for the thread to end. + */ + void join () + {} }; #endif } - /** - * An object that represents a - * spawned thread. This object can - * be freely copied around in user - * space, and all instances will - * represent the same thread and - * can require to wait for its - * termination and access its - * return value. - * - * Threads can be abandoned, - * i.e. if you just call - * Threads::new_thread but don't - * care about the returned object, - * or if you assign the return - * Threads::Thread object to an - * object that subsequently goes - * out of scope, then the thread - * previously created will still - * continue to do work. You will - * simply not be able to access its - * return value any more, and it - * may also happen that your - * program terminates before the - * thread has finished its work. - * - * The default value of the - * template argument is void, - * so if the function you are - * calling on a new thread has no - * return value, you can omit the - * template argument. - * - * @author Wolfgang Bangerth, 2003, 2009 - * @ingroup threads - * @ingroup threads - */ + /** + * An object that represents a + * spawned thread. This object can + * be freely copied around in user + * space, and all instances will + * represent the same thread and + * can require to wait for its + * termination and access its + * return value. + * + * Threads can be abandoned, + * i.e. if you just call + * Threads::new_thread but don't + * care about the returned object, + * or if you assign the return + * Threads::Thread object to an + * object that subsequently goes + * out of scope, then the thread + * previously created will still + * continue to do work. You will + * simply not be able to access its + * return value any more, and it + * may also happen that your + * program terminates before the + * thread has finished its work. + * + * The default value of the + * template argument is void, + * so if the function you are + * calling on a new thread has no + * return value, you can omit the + * template argument. + * + * @author Wolfgang Bangerth, 2003, 2009 + * @ingroup threads + * @ingroup threads + */ template class Thread { - public: - /** - * Construct a thread object - * with a function object. - */ - Thread (const std_cxx1x::function &function) - : - thread_descriptor (new internal::ThreadDescriptor()) - { - // in a second step, start - // the thread. this needs - // to be done in two steps - // because inside the start - // function we call - // enable_shared_from_this::shared_from_this - // which requires that - // there is already a - // shared_ptr that owns the - // object - thread_descriptor->start (function); - } - - /** - * Default constructor. You - * can't do much with a thread - * object constructed this way, - * except for assigning it a - * thread object that holds - * data created by the - * new_thread functions. - */ - Thread () {} - - /** - * Join the thread represented by this - * object, i.e. wait for it to - * finish. If you have used the default - * constructor of this class and have - * not assigned a thread object to it, - * then this function is a no-op. - */ - void join () const - { - if (thread_descriptor) - thread_descriptor->join (); - } - - /** - * Get the return value of the - * function of the - * thread. Since this is only - * available once the thread - * finishes, this implicitly - * also calls join(). - */ - RT return_value () - { - join (); - return thread_descriptor->ret_val.get(); - } - - /** - * Return true if this object - * has had a thread associated - * with it, either by using the - * non-default constructor or - * by assignment. - */ - bool valid () const - { - return static_cast(thread_descriptor); - } - - - /** - * Check for equality of thread - * objects. Since objects of - * this class store an implicit - * pointer to an object that - * exists exactly once for each - * thread, the check is simply - * to compare these pointers. - */ - bool operator == (const Thread &t) - { - return thread_descriptor == t.thread_descriptor; - } + public: + /** + * Construct a thread object + * with a function object. + */ + Thread (const std_cxx1x::function &function) + : + thread_descriptor (new internal::ThreadDescriptor()) + { + // in a second step, start + // the thread. this needs + // to be done in two steps + // because inside the start + // function we call + // enable_shared_from_this::shared_from_this + // which requires that + // there is already a + // shared_ptr that owns the + // object + thread_descriptor->start (function); + } - private: - /** - * Shared pointer to the object - * representing the thread, and - * abstracting operating system - * functions to work on - * it. Boost's shared pointer - * implementation will make - * sure that that object lives - * as long as there is at least - * one subscriber to it. - * - * Note that since the - * descriptor holds the - * location where the return - * value is stored, even if all - * Threads::Thread objects that - * point to the descriptor go - * out of scope, we must make - * sure that the descriptor is - * not yet destroyed. Rather, - * the descriptor must be - * destroyed when the last - * Threads::Thread object that - * points to it goes out of - * scope or whenever the thread - * is finished, whatever - * happens later. We do this by - * having the descriptor keep a - * pointer to itself and reset - * it to zero once it is done - * -- effectly keeping the use - * pointer above zero as long - * as work is going on. - */ - std_cxx1x::shared_ptr > thread_descriptor; + /** + * Default constructor. You + * can't do much with a thread + * object constructed this way, + * except for assigning it a + * thread object that holds + * data created by the + * new_thread functions. + */ + Thread () {} + + /** + * Join the thread represented by this + * object, i.e. wait for it to + * finish. If you have used the default + * constructor of this class and have + * not assigned a thread object to it, + * then this function is a no-op. + */ + void join () const + { + if (thread_descriptor) + thread_descriptor->join (); + } + + /** + * Get the return value of the + * function of the + * thread. Since this is only + * available once the thread + * finishes, this implicitly + * also calls join(). + */ + RT return_value () + { + join (); + return thread_descriptor->ret_val.get(); + } + + /** + * Return true if this object + * has had a thread associated + * with it, either by using the + * non-default constructor or + * by assignment. + */ + bool valid () const + { + return static_cast(thread_descriptor); + } + + + /** + * Check for equality of thread + * objects. Since objects of + * this class store an implicit + * pointer to an object that + * exists exactly once for each + * thread, the check is simply + * to compare these pointers. + */ + bool operator == (const Thread &t) + { + return thread_descriptor == t.thread_descriptor; + } + + private: + /** + * Shared pointer to the object + * representing the thread, and + * abstracting operating system + * functions to work on + * it. Boost's shared pointer + * implementation will make + * sure that that object lives + * as long as there is at least + * one subscriber to it. + * + * Note that since the + * descriptor holds the + * location where the return + * value is stored, even if all + * Threads::Thread objects that + * point to the descriptor go + * out of scope, we must make + * sure that the descriptor is + * not yet destroyed. Rather, + * the descriptor must be + * destroyed when the last + * Threads::Thread object that + * points to it goes out of + * scope or whenever the thread + * is finished, whatever + * happens later. We do this by + * having the descriptor keep a + * pointer to itself and reset + * it to zero once it is done + * -- effectly keeping the use + * pointer above zero as long + * as work is going on. + */ + std_cxx1x::shared_ptr > thread_descriptor; }; namespace internal { - /** - * A general template that returns - * std_cxx1x::ref(t) if t is of reference - * type, and t otherwise. - * - * The case that t is of reference type - * is handled in a partial specialization - * declared below. - */ + /** + * A general template that returns + * std_cxx1x::ref(t) if t is of reference + * type, and t otherwise. + * + * The case that t is of reference type + * is handled in a partial specialization + * declared below. + */ template struct maybe_make_ref { - static T act (T &t) - { - return t; - } + static T act (T &t) + { + return t; + } }; - /** - * A general template that returns - * std_cxx1x::ref(t) if t is of reference - * type, and t otherwise. - * - * The case that t is of reference type - * is handled in this partial - * specialization. - */ + /** + * A general template that returns + * std_cxx1x::ref(t) if t is of reference + * type, and t otherwise. + * + * The case that t is of reference type + * is handled in this partial + * specialization. + */ template struct maybe_make_ref { - static std_cxx1x::reference_wrapper act (T &t) - { - return std_cxx1x::ref(t); - } + static std_cxx1x::reference_wrapper act (T &t) + { + return std_cxx1x::ref(t); + } }; } @@ -1721,310 +1754,310 @@ namespace Threads namespace internal { - /** - * @internal - * General template declaration - * of a class that is used to - * encapsulate arguments to - * global and static member - * functions, make sure a new - * thread is created and that - * function being run on that - * thread. - * - * Although this general template - * is not implemented at all, the - * default template argument - * makes sure that whenever using - * the name of this class, the - * last template argument will be - * computed correctly from the - * previous arguments, and the - * correct specialization for - * this last template argument be - * used, even though we need to - * specify it. - */ + /** + * @internal + * General template declaration + * of a class that is used to + * encapsulate arguments to + * global and static member + * functions, make sure a new + * thread is created and that + * function being run on that + * thread. + * + * Although this general template + * is not implemented at all, the + * default template argument + * makes sure that whenever using + * the name of this class, the + * last template argument will be + * computed correctly from the + * previous arguments, and the + * correct specialization for + * this last template argument be + * used, even though we need to + * specify it. + */ template class fun_encapsulator; // ----------- encapsulators for function objects - /** - * @internal - * Encapsulator class for - * functions with no arguments. - */ + /** + * @internal + * Encapsulator class for + * functions with no arguments. + */ template class fun_encapsulator { - public: - fun_encapsulator (typename internal::fun_ptr::type *function) - : function (*function) - {} - - fun_encapsulator (const std_cxx1x::function::type> &function) - : function (function) - {} - - inline - Thread - operator() () - { - return Thread (function); - } + public: + fun_encapsulator (typename internal::fun_ptr::type *function) + : function (*function) + {} + + fun_encapsulator (const std_cxx1x::function::type> &function) + : function (function) + {} + + inline + Thread + operator() () + { + return Thread (function); + } - private: - std_cxx1x::function::type> function; + private: + std_cxx1x::function::type> function; }; - /** - * @internal - * Encapsulator class for - * functions with 1 argument. - */ + /** + * @internal + * Encapsulator class for + * functions with 1 argument. + */ template class fun_encapsulator { - public: - fun_encapsulator (typename internal::fun_ptr::type *function) - : function (*function) - {} - - fun_encapsulator (const std_cxx1x::function::type> &function) - : function (function) - {} - - inline - Thread - operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1) - { - return - Thread - (std_cxx1x::bind (function, + public: + fun_encapsulator (typename internal::fun_ptr::type *function) + : function (*function) + {} + + fun_encapsulator (const std_cxx1x::function::type> &function) + : function (function) + {} + + inline + Thread + operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1) + { + return + Thread + (std_cxx1x::bind (function, internal::maybe_make_ref::type>::act(arg1))); - } + } - private: - std_cxx1x::function::type> function; + private: + std_cxx1x::function::type> function; }; - /** - * @internal - * Encapsulator class for - * functions with 2 arguments. - */ + /** + * @internal + * Encapsulator class for + * functions with 2 arguments. + */ template class fun_encapsulator { - public: - fun_encapsulator (typename internal::fun_ptr::type *function) - : function (*function) - {} - - fun_encapsulator (const std_cxx1x::function::type> &function) - : function (function) - {} - - inline - Thread - operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, - typename std_cxx1x::tuple_element<1,ArgList>::type arg2) - { - return - Thread - (std_cxx1x::bind (function, + public: + fun_encapsulator (typename internal::fun_ptr::type *function) + : function (*function) + {} + + fun_encapsulator (const std_cxx1x::function::type> &function) + : function (function) + {} + + inline + Thread + operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, + typename std_cxx1x::tuple_element<1,ArgList>::type arg2) + { + return + Thread + (std_cxx1x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2))); - } + } - private: - std_cxx1x::function::type> function; + private: + std_cxx1x::function::type> function; }; - /** - * @internal - * Encapsulator class for - * functions with 3 arguments. - */ + /** + * @internal + * Encapsulator class for + * functions with 3 arguments. + */ template class fun_encapsulator { - public: - fun_encapsulator (typename internal::fun_ptr::type *function) - : function (*function) - {} - - fun_encapsulator (const std_cxx1x::function::type> &function) - : function (function) - {} - - inline - Thread - operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, - typename std_cxx1x::tuple_element<1,ArgList>::type arg2, - typename std_cxx1x::tuple_element<2,ArgList>::type arg3) - { - return - Thread - (std_cxx1x::bind (function, + public: + fun_encapsulator (typename internal::fun_ptr::type *function) + : function (*function) + {} + + fun_encapsulator (const std_cxx1x::function::type> &function) + : function (function) + {} + + inline + Thread + operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, + typename std_cxx1x::tuple_element<1,ArgList>::type arg2, + typename std_cxx1x::tuple_element<2,ArgList>::type arg3) + { + return + Thread + (std_cxx1x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), internal::maybe_make_ref::type>::act(arg3))); - } + } - private: - std_cxx1x::function::type> function; + private: + std_cxx1x::function::type> function; }; - /** - * @internal - * Encapsulator class for - * functions with 4 arguments. - */ + /** + * @internal + * Encapsulator class for + * functions with 4 arguments. + */ template class fun_encapsulator { - public: - fun_encapsulator (typename internal::fun_ptr::type *function) - : function (*function) - {} - - fun_encapsulator (const std_cxx1x::function::type> &function) - : function (function) - {} - - inline - Thread - operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, - typename std_cxx1x::tuple_element<1,ArgList>::type arg2, - typename std_cxx1x::tuple_element<2,ArgList>::type arg3, - typename std_cxx1x::tuple_element<3,ArgList>::type arg4) - { - return - Thread - (std_cxx1x::bind (function, + public: + fun_encapsulator (typename internal::fun_ptr::type *function) + : function (*function) + {} + + fun_encapsulator (const std_cxx1x::function::type> &function) + : function (function) + {} + + inline + Thread + operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, + typename std_cxx1x::tuple_element<1,ArgList>::type arg2, + typename std_cxx1x::tuple_element<2,ArgList>::type arg3, + typename std_cxx1x::tuple_element<3,ArgList>::type arg4) + { + return + Thread + (std_cxx1x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), internal::maybe_make_ref::type>::act(arg3), internal::maybe_make_ref::type>::act(arg4))); - } + } - private: - std_cxx1x::function::type> function; + private: + std_cxx1x::function::type> function; }; - /** - * @internal - * Encapsulator class for - * functions with 5 arguments. - */ + /** + * @internal + * Encapsulator class for + * functions with 5 arguments. + */ template class fun_encapsulator { - public: - fun_encapsulator (typename internal::fun_ptr::type *function) - : function (*function) - {} - - fun_encapsulator (const std_cxx1x::function::type> &function) - : function (function) - {} - - inline - Thread - operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, - typename std_cxx1x::tuple_element<1,ArgList>::type arg2, - typename std_cxx1x::tuple_element<2,ArgList>::type arg3, - typename std_cxx1x::tuple_element<3,ArgList>::type arg4, - typename std_cxx1x::tuple_element<4,ArgList>::type arg5) - { - return - Thread - (std_cxx1x::bind (function, + public: + fun_encapsulator (typename internal::fun_ptr::type *function) + : function (*function) + {} + + fun_encapsulator (const std_cxx1x::function::type> &function) + : function (function) + {} + + inline + Thread + operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, + typename std_cxx1x::tuple_element<1,ArgList>::type arg2, + typename std_cxx1x::tuple_element<2,ArgList>::type arg3, + typename std_cxx1x::tuple_element<3,ArgList>::type arg4, + typename std_cxx1x::tuple_element<4,ArgList>::type arg5) + { + return + Thread + (std_cxx1x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), internal::maybe_make_ref::type>::act(arg3), internal::maybe_make_ref::type>::act(arg4), internal::maybe_make_ref::type>::act(arg5))); - } + } - private: - std_cxx1x::function::type> function; + private: + std_cxx1x::function::type> function; }; - /** - * @internal - * Encapsulator class for - * functions with 6 arguments. - */ + /** + * @internal + * Encapsulator class for + * functions with 6 arguments. + */ template class fun_encapsulator { - public: - fun_encapsulator (typename internal::fun_ptr::type *function) - : function (*function) - {} - - fun_encapsulator (const std_cxx1x::function::type> &function) - : function (function) - {} - - inline - Thread - operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, - typename std_cxx1x::tuple_element<1,ArgList>::type arg2, - typename std_cxx1x::tuple_element<2,ArgList>::type arg3, - typename std_cxx1x::tuple_element<3,ArgList>::type arg4, - typename std_cxx1x::tuple_element<4,ArgList>::type arg5, - typename std_cxx1x::tuple_element<5,ArgList>::type arg6) - { - return - Thread - (std_cxx1x::bind (function, + public: + fun_encapsulator (typename internal::fun_ptr::type *function) + : function (*function) + {} + + fun_encapsulator (const std_cxx1x::function::type> &function) + : function (function) + {} + + inline + Thread + operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, + typename std_cxx1x::tuple_element<1,ArgList>::type arg2, + typename std_cxx1x::tuple_element<2,ArgList>::type arg3, + typename std_cxx1x::tuple_element<3,ArgList>::type arg4, + typename std_cxx1x::tuple_element<4,ArgList>::type arg5, + typename std_cxx1x::tuple_element<5,ArgList>::type arg6) + { + return + Thread + (std_cxx1x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), internal::maybe_make_ref::type>::act(arg3), internal::maybe_make_ref::type>::act(arg4), internal::maybe_make_ref::type>::act(arg5), internal::maybe_make_ref::type>::act(arg6))); - } + } - private: - std_cxx1x::function::type> function; + private: + std_cxx1x::function::type> function; }; - /** - * @internal - * Encapsulator class for - * functions with 7 arguments. - */ + /** + * @internal + * Encapsulator class for + * functions with 7 arguments. + */ template class fun_encapsulator { - public: - fun_encapsulator (typename internal::fun_ptr::type *function) - : function (*function) - {} - - fun_encapsulator (const std_cxx1x::function::type> &function) - : function (function) - {} - - inline - Thread - operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, - typename std_cxx1x::tuple_element<1,ArgList>::type arg2, - typename std_cxx1x::tuple_element<2,ArgList>::type arg3, - typename std_cxx1x::tuple_element<3,ArgList>::type arg4, - typename std_cxx1x::tuple_element<4,ArgList>::type arg5, - typename std_cxx1x::tuple_element<5,ArgList>::type arg6, - typename std_cxx1x::tuple_element<6,ArgList>::type arg7) - { - return - Thread - (std_cxx1x::bind (function, + public: + fun_encapsulator (typename internal::fun_ptr::type *function) + : function (*function) + {} + + fun_encapsulator (const std_cxx1x::function::type> &function) + : function (function) + {} + + inline + Thread + operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, + typename std_cxx1x::tuple_element<1,ArgList>::type arg2, + typename std_cxx1x::tuple_element<2,ArgList>::type arg3, + typename std_cxx1x::tuple_element<3,ArgList>::type arg4, + typename std_cxx1x::tuple_element<4,ArgList>::type arg5, + typename std_cxx1x::tuple_element<5,ArgList>::type arg6, + typename std_cxx1x::tuple_element<6,ArgList>::type arg7) + { + return + Thread + (std_cxx1x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), internal::maybe_make_ref::type>::act(arg3), @@ -2032,43 +2065,43 @@ namespace Threads internal::maybe_make_ref::type>::act(arg5), internal::maybe_make_ref::type>::act(arg6), internal::maybe_make_ref::type>::act(arg7))); - } + } - private: - std_cxx1x::function::type> function; + private: + std_cxx1x::function::type> function; }; - /** - * @internal - * Encapsulator class for - * functions with 8 arguments. - */ + /** + * @internal + * Encapsulator class for + * functions with 8 arguments. + */ template class fun_encapsulator { - public: - fun_encapsulator (typename internal::fun_ptr::type *function) - : function (*function) - {} - - fun_encapsulator (const std_cxx1x::function::type> &function) - : function (function) - {} - - inline - Thread - operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, - typename std_cxx1x::tuple_element<1,ArgList>::type arg2, - typename std_cxx1x::tuple_element<2,ArgList>::type arg3, - typename std_cxx1x::tuple_element<3,ArgList>::type arg4, - typename std_cxx1x::tuple_element<4,ArgList>::type arg5, - typename std_cxx1x::tuple_element<5,ArgList>::type arg6, - typename std_cxx1x::tuple_element<6,ArgList>::type arg7, - typename std_cxx1x::tuple_element<7,ArgList>::type arg8) - { - return - Thread - (std_cxx1x::bind (function, + public: + fun_encapsulator (typename internal::fun_ptr::type *function) + : function (*function) + {} + + fun_encapsulator (const std_cxx1x::function::type> &function) + : function (function) + {} + + inline + Thread + operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, + typename std_cxx1x::tuple_element<1,ArgList>::type arg2, + typename std_cxx1x::tuple_element<2,ArgList>::type arg3, + typename std_cxx1x::tuple_element<3,ArgList>::type arg4, + typename std_cxx1x::tuple_element<4,ArgList>::type arg5, + typename std_cxx1x::tuple_element<5,ArgList>::type arg6, + typename std_cxx1x::tuple_element<6,ArgList>::type arg7, + typename std_cxx1x::tuple_element<7,ArgList>::type arg8) + { + return + Thread + (std_cxx1x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), internal::maybe_make_ref::type>::act(arg3), @@ -2077,44 +2110,44 @@ namespace Threads internal::maybe_make_ref::type>::act(arg6), internal::maybe_make_ref::type>::act(arg7), internal::maybe_make_ref::type>::act(arg8))); - } + } - private: - std_cxx1x::function::type> function; + private: + std_cxx1x::function::type> function; }; - /** - * @internal - * Encapsulator class for - * functions with 9 arguments. - */ + /** + * @internal + * Encapsulator class for + * functions with 9 arguments. + */ template class fun_encapsulator { - public: - fun_encapsulator (typename internal::fun_ptr::type *function) - : function (*function) - {} - - fun_encapsulator (const std_cxx1x::function::type> &function) - : function (function) - {} - - inline - Thread - operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, - typename std_cxx1x::tuple_element<1,ArgList>::type arg2, - typename std_cxx1x::tuple_element<2,ArgList>::type arg3, - typename std_cxx1x::tuple_element<3,ArgList>::type arg4, - typename std_cxx1x::tuple_element<4,ArgList>::type arg5, - typename std_cxx1x::tuple_element<5,ArgList>::type arg6, - typename std_cxx1x::tuple_element<6,ArgList>::type arg7, - typename std_cxx1x::tuple_element<7,ArgList>::type arg8, - typename std_cxx1x::tuple_element<8,ArgList>::type arg9) - { - return - Thread - (std_cxx1x::bind (function, + public: + fun_encapsulator (typename internal::fun_ptr::type *function) + : function (*function) + {} + + fun_encapsulator (const std_cxx1x::function::type> &function) + : function (function) + {} + + inline + Thread + operator() (typename std_cxx1x::tuple_element<0,ArgList>::type arg1, + typename std_cxx1x::tuple_element<1,ArgList>::type arg2, + typename std_cxx1x::tuple_element<2,ArgList>::type arg3, + typename std_cxx1x::tuple_element<3,ArgList>::type arg4, + typename std_cxx1x::tuple_element<4,ArgList>::type arg5, + typename std_cxx1x::tuple_element<5,ArgList>::type arg6, + typename std_cxx1x::tuple_element<6,ArgList>::type arg7, + typename std_cxx1x::tuple_element<7,ArgList>::type arg8, + typename std_cxx1x::tuple_element<8,ArgList>::type arg9) + { + return + Thread + (std_cxx1x::bind (function, internal::maybe_make_ref::type>::act(arg1), internal::maybe_make_ref::type>::act(arg2), internal::maybe_make_ref::type>::act(arg3), @@ -2124,10 +2157,10 @@ namespace Threads internal::maybe_make_ref::type>::act(arg7), internal::maybe_make_ref::type>::act(arg8), internal::maybe_make_ref::type>::act(arg9))); - } + } - private: - std_cxx1x::function::type> function; + private: + std_cxx1x::function::type> function; }; } @@ -2136,13 +2169,13 @@ namespace Threads // ----------- encapsulators for functions not taking any parameters - /** - * Overload of the spawn function for - * non-member or static member - * functions with no arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * non-member or static member + * functions with no arguments. + * + * @deprecated + */ template inline internal::fun_encapsulator,0> @@ -2152,13 +2185,13 @@ namespace Threads } - /** - * Overload of the non-const spawn - * function for member functions with - * no arguments. - * - * @deprecated - */ + /** + * Overload of the non-const spawn + * function for member functions with + * no arguments. + * + * @deprecated + */ template inline internal::fun_encapsulator,0> @@ -2169,13 +2202,13 @@ namespace Threads (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c))); } - /** - * Overload of the spawn function for - * const member functions with no - * arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * const member functions with no + * arguments. + * + * @deprecated + */ template inline internal::fun_encapsulator,0> @@ -2191,13 +2224,13 @@ namespace Threads // ----------- encapsulators for unary functions - /** - * Overload of the spawn function for - * non-member or static member - * functions with 1 argument. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * non-member or static member + * functions with 1 argument. + * + * @deprecated + */ template inline internal::fun_encapsulator,1> @@ -2208,13 +2241,13 @@ namespace Threads - /** - * Overload of the non-const spawn - * function for member functions with - * 1 argument. - * - * @deprecated - */ + /** + * Overload of the non-const spawn + * function for member functions with + * 1 argument. + * + * @deprecated + */ template inline internal::fun_encapsulator,1> @@ -2225,13 +2258,13 @@ namespace Threads (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), std_cxx1x::_1)); } - /** - * Overload of the spawn function for - * const member functions with 1 - * argument. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * const member functions with 1 + * argument. + * + * @deprecated + */ template inline internal::fun_encapsulator,1> @@ -2245,13 +2278,13 @@ namespace Threads // ----------- encapsulators for binary functions - /** - * Overload of the spawn function for - * non-member or static member - * functions with 2 arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * non-member or static member + * functions with 2 arguments. + * + * @deprecated + */ template inline internal::fun_encapsulator,2> @@ -2262,13 +2295,13 @@ namespace Threads - /** - * Overload of the non-const spawn - * function for member functions with - * 2 arguments. - * - * @deprecated - */ + /** + * Overload of the non-const spawn + * function for member functions with + * 2 arguments. + * + * @deprecated + */ template inline internal::fun_encapsulator,2> @@ -2279,13 +2312,13 @@ namespace Threads (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), std_cxx1x::_1, std_cxx1x::_2)); } - /** - * Overload of the spawn function for - * const member functions with 2 - * arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * const member functions with 2 + * arguments. + * + * @deprecated + */ template inline internal::fun_encapsulator,2> @@ -2299,15 +2332,15 @@ namespace Threads // ----------- encapsulators for ternary functions - /** - * Overload of the spawn function for - * non-member or static member - * functions with 3 arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * non-member or static member + * functions with 3 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3> inline internal::fun_encapsulator,3> spawn (RT (*fun_ptr)(Arg1,Arg2,Arg3)) @@ -2317,15 +2350,15 @@ namespace Threads - /** - * Overload of the non-const spawn - * function for member functions with - * 3 arguments. - * - * @deprecated - */ + /** + * Overload of the non-const spawn + * function for member functions with + * 3 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3> inline internal::fun_encapsulator,3> spawn (C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3)) @@ -2335,15 +2368,15 @@ namespace Threads (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), std_cxx1x::_1, std_cxx1x::_2, std_cxx1x::_3)); } - /** - * Overload of the spawn function for - * const member functions with 3 - * arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * const member functions with 3 + * arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3> inline internal::fun_encapsulator,3> spawn (const C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3) const) @@ -2357,15 +2390,15 @@ namespace Threads // ----------- encapsulators for functions with 4 arguments - /** - * Overload of the spawn function for - * non-member or static member - * functions with 4 arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * non-member or static member + * functions with 4 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, typename Arg4> inline internal::fun_encapsulator,4> spawn (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4)) @@ -2375,15 +2408,15 @@ namespace Threads - /** - * Overload of the non-const spawn - * function for member functions with - * 4 arguments. - * - * @deprecated - */ + /** + * Overload of the non-const spawn + * function for member functions with + * 4 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, typename Arg4> inline internal::fun_encapsulator,4> spawn (C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4)) @@ -2393,15 +2426,15 @@ namespace Threads (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), std_cxx1x::_1, std_cxx1x::_2, std_cxx1x::_3, std_cxx1x::_4)); } - /** - * Overload of the spawn function for - * const member functions with 4 - * arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * const member functions with 4 + * arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, typename Arg4> inline internal::fun_encapsulator,4> spawn (const C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4) const) @@ -2414,16 +2447,16 @@ namespace Threads // ----------- encapsulators for functions with 5 arguments - /** - * Overload of the spawn function for - * non-member or static member - * functions with 5 arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * non-member or static member + * functions with 5 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> inline internal::fun_encapsulator,5> spawn (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5)) @@ -2433,16 +2466,16 @@ namespace Threads - /** - * Overload of the non-const spawn - * function for member functions with - * 5 arguments. - * - * @deprecated - */ + /** + * Overload of the non-const spawn + * function for member functions with + * 5 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> inline internal::fun_encapsulator,5> spawn (C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5)) @@ -2452,16 +2485,16 @@ namespace Threads (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), std_cxx1x::_1, std_cxx1x::_2, std_cxx1x::_3, std_cxx1x::_4, std_cxx1x::_5)); } - /** - * Overload of the spawn function for - * const member functions with 5 - * arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * const member functions with 5 + * arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> inline internal::fun_encapsulator,5> spawn (const C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5) const) @@ -2474,16 +2507,16 @@ namespace Threads // ----------- encapsulators for functions with 6 arguments - /** - * Overload of the spawn function for - * non-member or static member - * functions with 6 arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * non-member or static member + * functions with 6 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6> inline internal::fun_encapsulator,6> spawn (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6)) @@ -2493,16 +2526,16 @@ namespace Threads - /** - * Overload of the non-const spawn - * function for member functions with - * 6 arguments. - * - * @deprecated - */ + /** + * Overload of the non-const spawn + * function for member functions with + * 6 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6> inline internal::fun_encapsulator,6> spawn (C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6)) @@ -2512,20 +2545,20 @@ namespace Threads (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), std_cxx1x::_1, std_cxx1x::_2, std_cxx1x::_3, std_cxx1x::_4, std_cxx1x::_5, std_cxx1x::_6)); } - /** - * Overload of the spawn function for - * const member functions with 6 - * arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * const member functions with 6 + * arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6> inline internal::fun_encapsulator,6> - spawn (const C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6) const) + std_cxx1x::tuple,6> + spawn (const C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6) const) { return std_cxx1x::function >::type> @@ -2535,64 +2568,64 @@ namespace Threads // ----------- encapsulators for functions with 7 arguments - /** - * Overload of the spawn function for - * non-member or static member - * functions with 7 arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * non-member or static member + * functions with 7 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7> inline internal::fun_encapsulator,7> - spawn (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7)) + Arg4, Arg5, Arg6, Arg7>,7> + spawn (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7)) { return fun_ptr; } - /** - * Overload of the non-const spawn - * function for member functions with - * 7 arguments. - * - * @deprecated - */ + /** + * Overload of the non-const spawn + * function for member functions with + * 7 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7> inline internal::fun_encapsulator,7> - spawn (C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7)) + Arg4, Arg5, Arg6, Arg7>,7> + spawn (C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7)) { return std_cxx1x::function >::type> (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), std_cxx1x::_1, std_cxx1x::_2, std_cxx1x::_3, std_cxx1x::_4, std_cxx1x::_5, std_cxx1x::_6, std_cxx1x::_7)); } - /** - * Overload of the spawn function for - * const member functions with 7 - * arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * const member functions with 7 + * arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7> inline internal::fun_encapsulator,7> - spawn (const C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7) const) + std_cxx1x::tuple,7> + spawn (const C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7) const) { return std_cxx1x::function >::type> @@ -2602,70 +2635,70 @@ namespace Threads // ----------- encapsulators for functions with 8 arguments - /** - * Overload of the spawn function for - * non-member or static member - * functions with 8 arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * non-member or static member + * functions with 8 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8> inline internal::fun_encapsulator,8> - spawn (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, - Arg6,Arg7,Arg8)) + Arg4, Arg5, Arg6, + Arg7, Arg8>,8> + spawn (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, + Arg6,Arg7,Arg8)) { return fun_ptr; } - /** - * Overload of the non-const spawn - * function for member functions with - * 8 arguments. - * - * @deprecated - */ + /** + * Overload of the non-const spawn + * function for member functions with + * 8 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8> inline internal::fun_encapsulator,8> - spawn (C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, - Arg6,Arg7,Arg8)) + Arg4, Arg5, Arg6, + Arg7, Arg8>,8> + spawn (C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, + Arg6,Arg7,Arg8)) { return std_cxx1x::function >::type> (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), std_cxx1x::_1, std_cxx1x::_2, std_cxx1x::_3, std_cxx1x::_4, std_cxx1x::_5, std_cxx1x::_6, std_cxx1x::_7, std_cxx1x::_8)); } - /** - * Overload of the spawn function for - * const member functions with 8 - * arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * const member functions with 8 + * arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8> inline internal::fun_encapsulator,8> - spawn (const C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, - Arg6,Arg7,Arg8) const) + std_cxx1x::tuple,8> + spawn (const C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, + Arg6,Arg7,Arg8) const) { return std_cxx1x::function >::type> @@ -2675,70 +2708,70 @@ namespace Threads // ----------- encapsulators for functions with 9 arguments - /** - * Overload of the spawn function for - * non-member or static member - * functions with 9 arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * non-member or static member + * functions with 9 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8, typename Arg9> inline internal::fun_encapsulator,9> - spawn (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, - Arg6,Arg7,Arg8,Arg9)) + Arg4, Arg5, Arg6, + Arg7, Arg8, Arg9>,9> + spawn (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, + Arg6,Arg7,Arg8,Arg9)) { return fun_ptr; } - /** - * Overload of the non-const spawn - * function for member functions with - * 9 arguments. - * - * @deprecated - */ + /** + * Overload of the non-const spawn + * function for member functions with + * 9 arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8, typename Arg9> inline internal::fun_encapsulator,9> - spawn (C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, - Arg6,Arg7,Arg8,Arg9)) + Arg4, Arg5, Arg6, + Arg7, Arg8, Arg9>,9> + spawn (C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, + Arg6,Arg7,Arg8,Arg9)) { return std_cxx1x::function >::type> (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), std_cxx1x::_1, std_cxx1x::_2, std_cxx1x::_3, std_cxx1x::_4, std_cxx1x::_5, std_cxx1x::_6, std_cxx1x::_7, std_cxx1x::_8, std_cxx1x::_9)); } - /** - * Overload of the spawn function for - * const member functions with 9 - * arguments. - * - * @deprecated - */ + /** + * Overload of the spawn function for + * const member functions with 9 + * arguments. + * + * @deprecated + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8, typename Arg9> inline internal::fun_encapsulator,9> - spawn (const C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, - Arg6,Arg7,Arg8,Arg9) const) + std_cxx1x::tuple,9> + spawn (const C &c, RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, + Arg6,Arg7,Arg8,Arg9) const) { return std_cxx1x::function >::type> @@ -2749,16 +2782,16 @@ namespace Threads // ----------- thread starters for functions not taking any parameters - /** - * Overload of the new_thread function for - * objects that can be converted to - * std_cxx1x::function, i.e. anything - * that can be called like a function - * object without arguments and returning - * an object of type RT (or void). - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * objects that can be converted to + * std_cxx1x::function, i.e. anything + * that can be called like a function + * object without arguments and returning + * an object of type RT (or void). + * + * @ingroup threads + */ template inline Thread @@ -2767,13 +2800,13 @@ namespace Threads return Thread(function); } - /** - * Overload of the new_thread function for - * non-member or static member - * functions with no arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * non-member or static member + * functions with no arguments. + * + * @ingroup threads + */ template inline Thread @@ -2783,13 +2816,13 @@ namespace Threads } - /** - * Overload of the non-const new_thread - * function for member functions with - * no arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_thread + * function for member functions with + * no arguments. + * + * @ingroup threads + */ template inline Thread @@ -2803,13 +2836,13 @@ namespace Threads #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_thread function for - * const member functions with no - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * const member functions with no + * arguments. + * + * @ingroup threads + */ template inline Thread @@ -2826,13 +2859,13 @@ namespace Threads // ----------- thread starters for unary functions - /** - * Overload of the new_thread function for - * non-member or static member - * functions with 1 argument. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * non-member or static member + * functions with 1 argument. + * + * @ingroup threads + */ template inline Thread @@ -2842,18 +2875,18 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, - internal::maybe_make_ref::act(arg1))); + internal::maybe_make_ref::act(arg1))); } - /** - * Overload of the non-const new_thread - * function for member functions with - * 1 argument. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_thread + * function for member functions with + * 1 argument. + * + * @ingroup threads + */ template inline Thread @@ -2864,17 +2897,17 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), - internal::maybe_make_ref::act(arg1))); + internal::maybe_make_ref::act(arg1))); } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_thread function for - * const member functions with 1 - * argument. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * const member functions with 1 + * argument. + * + * @ingroup threads + */ template inline Thread @@ -2885,19 +2918,19 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::cref(c), - internal::maybe_make_ref::act(arg1))); + internal::maybe_make_ref::act(arg1))); } #endif // ----------- thread starters for binary functions - /** - * Overload of the new_thread function for - * non-member or static member - * functions with 2 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * non-member or static member + * functions with 2 arguments. + * + * @ingroup threads + */ template inline Thread @@ -2908,19 +2941,19 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2))); } - /** - * Overload of the non-const new_thread - * function for member functions with - * 2 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_thread + * function for member functions with + * 2 arguments. + * + * @ingroup threads + */ template inline Thread @@ -2932,18 +2965,18 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2))); } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_thread function for - * const member functions with 2 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * const member functions with 2 + * arguments. + * + * @ingroup threads + */ template inline Thread @@ -2955,22 +2988,22 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::cref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2))); } #endif // ----------- thread starters for ternary functions - /** - * Overload of the new_thread function for - * non-member or static member - * functions with 3 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * non-member or static member + * functions with 3 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3> inline Thread new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3), @@ -2981,22 +3014,22 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3))); } - /** - * Overload of the non-const new_thread - * function for member functions with - * 3 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_thread + * function for member functions with + * 3 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3), @@ -3008,21 +3041,21 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3))); } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_thread function for - * const member functions with 3 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * const member functions with 3 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3) const, @@ -3034,24 +3067,24 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::cref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3))); } #endif // ----------- thread starters for functions with 4 arguments - /** - * Overload of the new_thread function for - * non-member or static member - * functions with 4 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * non-member or static member + * functions with 4 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, typename Arg4> inline Thread new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4), @@ -3063,23 +3096,23 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4))); } - /** - * Overload of the non-const new_thread - * function for member functions with - * 4 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_thread + * function for member functions with + * 4 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, typename Arg4> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4), @@ -3092,22 +3125,22 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4))); } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_thread function for - * const member functions with 4 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * const member functions with 4 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, typename Arg4> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4) const, @@ -3120,25 +3153,25 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::cref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4))); } #endif // ----------- thread starters for functions with 5 arguments - /** - * Overload of the new_thread function for - * non-member or static member - * functions with 5 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * non-member or static member + * functions with 5 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> inline Thread new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5), @@ -3151,25 +3184,25 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5))); } - /** - * Overload of the non-const new_thread - * function for member functions with - * 5 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_thread + * function for member functions with + * 5 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5), @@ -3183,24 +3216,24 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5))); } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_thread function for - * const member functions with 5 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * const member functions with 5 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5) const, @@ -3214,26 +3247,26 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::cref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5))); } #endif // ----------- thread starters for functions with 6 arguments - /** - * Overload of the new_thread function for - * non-member or static member - * functions with 6 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * non-member or static member + * functions with 6 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6> inline Thread new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6), @@ -3247,26 +3280,26 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5), - internal::maybe_make_ref::act(arg6))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5), + internal::maybe_make_ref::act(arg6))); } - /** - * Overload of the non-const new_thread - * function for member functions with - * 6 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_thread + * function for member functions with + * 6 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6), @@ -3281,25 +3314,25 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5), - internal::maybe_make_ref::act(arg6))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5), + internal::maybe_make_ref::act(arg6))); } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_thread function for - * const member functions with 6 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * const member functions with 6 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6) const, @@ -3314,28 +3347,28 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::cref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5), - internal::maybe_make_ref::act(arg6))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5), + internal::maybe_make_ref::act(arg6))); } #endif // ----------- thread starters for functions with 7 arguments - /** - * Overload of the new_thread function for - * non-member or static member - * functions with 7 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * non-member or static member + * functions with 7 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7> inline Thread new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7), @@ -3350,28 +3383,28 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5), - internal::maybe_make_ref::act(arg6), - internal::maybe_make_ref::act(arg7))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5), + internal::maybe_make_ref::act(arg6), + internal::maybe_make_ref::act(arg7))); } - /** - * Overload of the non-const new_thread - * function for member functions with - * 7 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_thread + * function for member functions with + * 7 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7), @@ -3387,27 +3420,27 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5), - internal::maybe_make_ref::act(arg6), - internal::maybe_make_ref::act(arg7))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5), + internal::maybe_make_ref::act(arg6), + internal::maybe_make_ref::act(arg7))); } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_thread function for - * const member functions with 7 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * const member functions with 7 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7) const, @@ -3423,29 +3456,29 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::cref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5), - internal::maybe_make_ref::act(arg6), - internal::maybe_make_ref::act(arg7))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5), + internal::maybe_make_ref::act(arg6), + internal::maybe_make_ref::act(arg7))); } #endif // ----------- thread starters for functions with 8 arguments - /** - * Overload of the new_thread function for - * non-member or static member - * functions with 8 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * non-member or static member + * functions with 8 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8> inline Thread new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, @@ -3462,29 +3495,29 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5), - internal::maybe_make_ref::act(arg6), - internal::maybe_make_ref::act(arg7), - internal::maybe_make_ref::act(arg8))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5), + internal::maybe_make_ref::act(arg6), + internal::maybe_make_ref::act(arg7), + internal::maybe_make_ref::act(arg8))); } - /** - * Overload of the non-const new_thread - * function for member functions with - * 8 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_thread + * function for member functions with + * 8 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, @@ -3502,28 +3535,28 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5), - internal::maybe_make_ref::act(arg6), - internal::maybe_make_ref::act(arg7), - internal::maybe_make_ref::act(arg8))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5), + internal::maybe_make_ref::act(arg6), + internal::maybe_make_ref::act(arg7), + internal::maybe_make_ref::act(arg8))); } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_thread function for - * const member functions with 8 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * const member functions with 8 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, @@ -3541,30 +3574,30 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::cref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5), - internal::maybe_make_ref::act(arg6), - internal::maybe_make_ref::act(arg7), - internal::maybe_make_ref::act(arg8))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5), + internal::maybe_make_ref::act(arg6), + internal::maybe_make_ref::act(arg7), + internal::maybe_make_ref::act(arg8))); } #endif // ----------- thread starters for functions with 9 arguments - /** - * Overload of the new_thread function for - * non-member or static member - * functions with 9 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * non-member or static member + * functions with 9 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8, typename Arg9> inline Thread new_thread (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, @@ -3582,30 +3615,30 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5), - internal::maybe_make_ref::act(arg6), - internal::maybe_make_ref::act(arg7), - internal::maybe_make_ref::act(arg8), - internal::maybe_make_ref::act(arg9))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5), + internal::maybe_make_ref::act(arg6), + internal::maybe_make_ref::act(arg7), + internal::maybe_make_ref::act(arg8), + internal::maybe_make_ref::act(arg9))); } - /** - * Overload of the non-const new_thread - * function for member functions with - * 9 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_thread + * function for member functions with + * 9 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8, typename Arg9> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, @@ -3624,29 +3657,29 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::ref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5), - internal::maybe_make_ref::act(arg6), - internal::maybe_make_ref::act(arg7), - internal::maybe_make_ref::act(arg8), - internal::maybe_make_ref::act(arg9))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5), + internal::maybe_make_ref::act(arg6), + internal::maybe_make_ref::act(arg7), + internal::maybe_make_ref::act(arg8), + internal::maybe_make_ref::act(arg9))); } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_thread function for - * const member functions with 9 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_thread function for + * const member functions with 9 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8, typename Arg9> inline Thread new_thread (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, @@ -3665,68 +3698,69 @@ namespace Threads return Thread (std_cxx1x::bind(fun_ptr, std_cxx1x::cref(c), - internal::maybe_make_ref::act(arg1), - internal::maybe_make_ref::act(arg2), - internal::maybe_make_ref::act(arg3), - internal::maybe_make_ref::act(arg4), - internal::maybe_make_ref::act(arg5), - internal::maybe_make_ref::act(arg6), - internal::maybe_make_ref::act(arg7), - internal::maybe_make_ref::act(arg8), - internal::maybe_make_ref::act(arg9))); + internal::maybe_make_ref::act(arg1), + internal::maybe_make_ref::act(arg2), + internal::maybe_make_ref::act(arg3), + internal::maybe_make_ref::act(arg4), + internal::maybe_make_ref::act(arg5), + internal::maybe_make_ref::act(arg6), + internal::maybe_make_ref::act(arg7), + internal::maybe_make_ref::act(arg8), + internal::maybe_make_ref::act(arg9))); } #endif // ------------------------ ThreadGroup ------------------------------------- - /** - * A container for thread - * objects. Allows to add new - * thread objects and wait for them - * all together. The thread objects - * need to have the same return - * value for the called function. - * - * @author Wolfgang Bangerth, 2003 - * @ingroup threads - */ + /** + * A container for thread + * objects. Allows to add new + * thread objects and wait for them + * all together. The thread objects + * need to have the same return + * value for the called function. + * + * @author Wolfgang Bangerth, 2003 + * @ingroup threads + */ template class ThreadGroup { - public: - /** - * Add another thread object to - * the collection. - */ - ThreadGroup & operator += (const Thread &t) - { - threads.push_back (t); - return *this; - } - - /** - * Wait for all threads in the - * collection to finish. It is - * not a problem if some of - * them have already been - * waited for, i.e. you may - * call this function more than - * once, and you can also add - * new thread objects between - * subsequent calls to this - * function if you want. - */ - void join_all () const { - for (typename std::list >::const_iterator - t=threads.begin(); t!=threads.end(); ++t) - t->join (); - } + public: + /** + * Add another thread object to + * the collection. + */ + ThreadGroup &operator += (const Thread &t) + { + threads.push_back (t); + return *this; + } - private: - /** - * List of thread objects. - */ - std::list > threads; + /** + * Wait for all threads in the + * collection to finish. It is + * not a problem if some of + * them have already been + * waited for, i.e. you may + * call this function more than + * once, and you can also add + * new thread objects between + * subsequent calls to this + * function if you want. + */ + void join_all () const + { + for (typename std::list >::const_iterator + t=threads.begin(); t!=threads.end(); ++t) + t->join (); + } + + private: + /** + * List of thread objects. + */ + std::list > threads; }; @@ -3739,177 +3773,177 @@ namespace Threads template struct TaskDescriptor; - /** - * The task class for TBB that is - * used by the TaskDescriptor - * class. - */ + /** + * The task class for TBB that is + * used by the TaskDescriptor + * class. + */ template struct TaskEntryPoint : public tbb::task { - TaskEntryPoint (TaskDescriptor &task_descriptor) - : - task_descriptor (task_descriptor) - {} + TaskEntryPoint (TaskDescriptor &task_descriptor) + : + task_descriptor (task_descriptor) + {} - virtual tbb::task * execute () - { - // call the function object - // and put the return value - // into the proper place - call (task_descriptor.function, task_descriptor.ret_val); - return 0; - } + virtual tbb::task *execute () + { + // call the function object + // and put the return value + // into the proper place + call (task_descriptor.function, task_descriptor.ret_val); + return 0; + } - /** - * A reference to the descriptor - * object of this task. - */ - TaskDescriptor &task_descriptor; + /** + * A reference to the descriptor + * object of this task. + */ + TaskDescriptor &task_descriptor; }; - /** - * @internal - * Base class describing a - * task. This is the basic - * class abstracting the - * Threading Building Blocks - * implementation of tasks. - * It provides a mechanism - * to start a new task, as well - * as for joining it. - * - * Internally, the way things are - * implemented is that all Task<> - * objects keep a shared pointer - * to the task descriptor. When - * the last Task<> object goes - * out of scope, the destructor - * of the descriptor is - * called. Since tasks can not be - * abandoned, the destructor - * makes sure that the task is - * finished before it can - * continue to destroy the - * object. - * - * Note that unlike threads, - * tasks are not always started - * right away, and so the - * starting thread can't rely on - * the fact that the started task - * can copy things off the - * spawning thread's stack - * frame. As a consequence, the - * task description needs to - * include a way to store the - * function and its arguments - * that shall be run on the task. - * - * @author Wolfgang Bangerth, 2009 - */ + /** + * @internal + * Base class describing a + * task. This is the basic + * class abstracting the + * Threading Building Blocks + * implementation of tasks. + * It provides a mechanism + * to start a new task, as well + * as for joining it. + * + * Internally, the way things are + * implemented is that all Task<> + * objects keep a shared pointer + * to the task descriptor. When + * the last Task<> object goes + * out of scope, the destructor + * of the descriptor is + * called. Since tasks can not be + * abandoned, the destructor + * makes sure that the task is + * finished before it can + * continue to destroy the + * object. + * + * Note that unlike threads, + * tasks are not always started + * right away, and so the + * starting thread can't rely on + * the fact that the started task + * can copy things off the + * spawning thread's stack + * frame. As a consequence, the + * task description needs to + * include a way to store the + * function and its arguments + * that shall be run on the task. + * + * @author Wolfgang Bangerth, 2009 + */ template struct TaskDescriptor { - private: - /** - * The function and its arguments - * that are to be run on the task. - */ - std_cxx1x::function function; - - /** - * Variable holding the data the TBB - * needs to work with a task. Set by - * the queue_up_task() function. Note - * that the object behind this - * pointer will be deleted upon - * termination of the task, so we do - * not have to do so ourselves. In - * particular, if all objects with - * pointers to this task_description - * object go out of scope then no - * action is needed on our behalf. - */ - tbb::task *task; - - /** - * A place where the task will - * deposit its return value. - */ - return_value ret_val; - - /** - * A flag indicating whether the task - * has terminated. - */ - bool task_is_done; - - public: - - /** - * Constructor. Take the function to - * be run on this task as argument. - */ - TaskDescriptor (const std_cxx1x::function &function); - - /** - * Default - * constructor. Throws an - * exception since we want to - * queue a task immediately - * upon construction of these - * objects to make sure that - * each TaskDescriptor object - * corresponds to exactly one - * task. - */ - TaskDescriptor (); - - /** - * Copy constructor. Throws - * an exception since we want - * to make sure that each - * TaskDescriptor object - * corresponds to exactly one - * task. - */ - TaskDescriptor (const TaskDescriptor &); - - /** - * Destructor. - */ - ~TaskDescriptor (); - - /** - * Queue up the task to the - * scheduler. We need to do - * this in a separate - * function since the new - * tasks needs to access - * objects from the current - * object and that can only - * reliably happen if the - * current object is - * completely constructed - * already. - */ - void queue_task (); - - /** - * Join a task, i.e. wait - * for it to finish. This - * function can safely be - * called from different - * threads at the same time, - * and can also be called - * more than once. - */ - void join (); - - - template friend struct TaskEntryPoint; - friend class dealii::Threads::Task; + private: + /** + * The function and its arguments + * that are to be run on the task. + */ + std_cxx1x::function function; + + /** + * Variable holding the data the TBB + * needs to work with a task. Set by + * the queue_up_task() function. Note + * that the object behind this + * pointer will be deleted upon + * termination of the task, so we do + * not have to do so ourselves. In + * particular, if all objects with + * pointers to this task_description + * object go out of scope then no + * action is needed on our behalf. + */ + tbb::task *task; + + /** + * A place where the task will + * deposit its return value. + */ + return_value ret_val; + + /** + * A flag indicating whether the task + * has terminated. + */ + bool task_is_done; + + public: + + /** + * Constructor. Take the function to + * be run on this task as argument. + */ + TaskDescriptor (const std_cxx1x::function &function); + + /** + * Default + * constructor. Throws an + * exception since we want to + * queue a task immediately + * upon construction of these + * objects to make sure that + * each TaskDescriptor object + * corresponds to exactly one + * task. + */ + TaskDescriptor (); + + /** + * Copy constructor. Throws + * an exception since we want + * to make sure that each + * TaskDescriptor object + * corresponds to exactly one + * task. + */ + TaskDescriptor (const TaskDescriptor &); + + /** + * Destructor. + */ + ~TaskDescriptor (); + + /** + * Queue up the task to the + * scheduler. We need to do + * this in a separate + * function since the new + * tasks needs to access + * objects from the current + * object and that can only + * reliably happen if the + * current object is + * completely constructed + * already. + */ + void queue_task (); + + /** + * Join a task, i.e. wait + * for it to finish. This + * function can safely be + * called from different + * threads at the same time, + * and can also be called + * more than once. + */ + void join (); + + + template friend struct TaskEntryPoint; + friend class dealii::Threads::Task; }; @@ -3917,9 +3951,9 @@ namespace Threads template inline TaskDescriptor::TaskDescriptor (const std_cxx1x::function &function) - : - function (function), - task_is_done (false) + : + function (function), + task_is_done (false) {} @@ -3928,11 +3962,11 @@ namespace Threads void TaskDescriptor::queue_task () { - // use the pattern described in - // the TBB book on pages - // 230/231 ("Start a large task - // in parallel with the main - // program) + // use the pattern described in + // the TBB book on pages + // 230/231 ("Start a large task + // in parallel with the main + // program) task = new (tbb::task::allocate_root()) tbb::empty_task; task->set_ref_count (2); @@ -3962,28 +3996,28 @@ namespace Threads inline TaskDescriptor::~TaskDescriptor () { - // wait for the task to - // complete for sure + // wait for the task to + // complete for sure join (); - // now destroy the empty task - // structure. the book recommends to - // spawn it as well and let the - // scheduler destroy the object when - // done, but this has the disadvantage - // that the scheduler may not get to - // actually finishing the task before - // it goes out of scope (at the end of - // the program, or if a thread is done - // on which it was run) and then we - // would get a hard-to-decipher warning - // about unfinished tasks when the - // scheduler "goes out of the - // arena". rather, let's explicitly - // destroy the empty task - // object. before that, make sure that - // the task has been shut down, - // expressed by a zero reference count + // now destroy the empty task + // structure. the book recommends to + // spawn it as well and let the + // scheduler destroy the object when + // done, but this has the disadvantage + // that the scheduler may not get to + // actually finishing the task before + // it goes out of scope (at the end of + // the program, or if a thread is done + // on which it was run) and then we + // would get a hard-to-decipher warning + // about unfinished tasks when the + // scheduler "goes out of the + // arena". rather, let's explicitly + // destroy the empty task + // object. before that, make sure that + // the task has been shut down, + // expressed by a zero reference count Assert (task != 0, ExcInternalError()); Assert (task->ref_count()==0, ExcInternalError()); task->destroy (*task); @@ -3995,24 +4029,24 @@ namespace Threads void TaskDescriptor::join () { - // if the task is already done, just - // return. this makes sure we call - // tbb::Task::wait_for_all() exactly - // once, as required by TBB. we could - // also get the reference count of task - // for doing this, but that is usually - // slower. note that this does not work - // when the thread calling this - // function is not the same as the one - // that initialized the task. - // - // TODO: can we assert that no other - // thread tries to end the task? + // if the task is already done, just + // return. this makes sure we call + // tbb::Task::wait_for_all() exactly + // once, as required by TBB. we could + // also get the reference count of task + // for doing this, but that is usually + // slower. note that this does not work + // when the thread calling this + // function is not the same as the one + // that initialized the task. + // + // TODO: can we assert that no other + // thread tries to end the task? if (task_is_done == true) return; - // let TBB wait for the task to - // complete. + // let TBB wait for the task to + // complete. task_is_done = true; task->wait_for_all(); } @@ -4021,48 +4055,48 @@ namespace Threads #else // no threading enabled - /** - * A way to describe tasks. Since - * we are in non-MT mode at this - * place, things are a lot - * simpler than in MT mode. - */ + /** + * A way to describe tasks. Since + * we are in non-MT mode at this + * place, things are a lot + * simpler than in MT mode. + */ template struct TaskDescriptor { - /** - * A place where the task will - * deposit its return value. - */ - return_value ret_val; - - /** - * Constructor. Call the - * given function and emplace - * the return value into the - * slot reserved for this - * purpose. - */ - TaskDescriptor (const std_cxx1x::function &function) - { - call (function, ret_val); - } - /** - * Wait for the task to - * return. Since we are in - * non-MT mode here, there is - * nothing to do. - */ - static void join () {} - - /** - * Run the task. Since we are - * here in non-MT mode, there - * is nothing to do that the - * constructor hasn't already - * done. - */ - static void queue_task () {} + /** + * A place where the task will + * deposit its return value. + */ + return_value ret_val; + + /** + * Constructor. Call the + * given function and emplace + * the return value into the + * slot reserved for this + * purpose. + */ + TaskDescriptor (const std_cxx1x::function &function) + { + call (function, ret_val); + } + /** + * Wait for the task to + * return. Since we are in + * non-MT mode here, there is + * nothing to do. + */ + static void join () {} + + /** + * Run the task. Since we are + * here in non-MT mode, there + * is nothing to do that the + * constructor hasn't already + * done. + */ + static void queue_task () {} }; #endif @@ -4071,132 +4105,132 @@ namespace Threads - /** - * Describes one task object based on the - * Threading Building Blocks' Task. Note - * that the call to join() must be executed - * on the same thread as the call to the - * constructor. Otherwise, there might be a - * deadlock. In other words, a Task - * object should never passed on to another - * task for calling the join() method. - * - * @author Wolfgang Bangerth, 2009 - * @ingroup threads - */ + /** + * Describes one task object based on the + * Threading Building Blocks' Task. Note + * that the call to join() must be executed + * on the same thread as the call to the + * constructor. Otherwise, there might be a + * deadlock. In other words, a Task + * object should never passed on to another + * task for calling the join() method. + * + * @author Wolfgang Bangerth, 2009 + * @ingroup threads + */ template class Task { - /** - * Construct a task object - * given a function object to - * execute on the task. - */ - public: - Task (const std_cxx1x::function &function_object) - { - // create a task descriptor and tell it - // to queue itself up with the scheduling - // system - task_descriptor = - std_cxx1x::shared_ptr > - (new internal::TaskDescriptor(function_object)); - task_descriptor->queue_task (); - } - - /** - * Default constructor. You - * can't do much with a task - * object constructed this way, - * except for assigning it a - * task object that holds - * data created by the - * spawn functions. - */ - Task () {} - - /** - * Join the task represented - * by this object, i.e. wait - * for it to finish. You can't - * call this function if you - * have used the default - * constructor of this class - * and have not assigned a - * task object to it. - */ - void join () const - { - AssertThrow (task_descriptor != - std_cxx1x::shared_ptr >(), - ExcNoTask()); - task_descriptor->join (); - } - - /** - * Get the return value of the - * function of the - * task. Since this is only - * available once the task - * finishes, this implicitly - * also calls join(). - */ - RT return_value () - { - join (); - return task_descriptor->ret_val.get(); - } - - - /** - * Check for equality of task - * objects. Since objects of - * this class store an implicit - * pointer to an object that - * exists exactly once for each - * task, the check is simply - * to compare these pointers. - */ - bool operator == (const Task &t) - { - AssertThrow (task_descriptor != - std_cxx1x::shared_ptr >(), - ExcNoTask()); - return task_descriptor == t.task_descriptor; - } - - /** @addtogroup Exceptions - * @{ */ - - /** - * Exception - */ - DeclException0 (ExcNoTask); - //@} - private: - /** - * Shared pointer to the object - * representing the task. Boost's - * shared pointer implementation will - * make sure that that object lives as - * long as there is at least one - * subscriber to it. - */ - std_cxx1x::shared_ptr > task_descriptor; + /** + * Construct a task object + * given a function object to + * execute on the task. + */ + public: + Task (const std_cxx1x::function &function_object) + { + // create a task descriptor and tell it + // to queue itself up with the scheduling + // system + task_descriptor = + std_cxx1x::shared_ptr > + (new internal::TaskDescriptor(function_object)); + task_descriptor->queue_task (); + } + + /** + * Default constructor. You + * can't do much with a task + * object constructed this way, + * except for assigning it a + * task object that holds + * data created by the + * spawn functions. + */ + Task () {} + + /** + * Join the task represented + * by this object, i.e. wait + * for it to finish. You can't + * call this function if you + * have used the default + * constructor of this class + * and have not assigned a + * task object to it. + */ + void join () const + { + AssertThrow (task_descriptor != + std_cxx1x::shared_ptr >(), + ExcNoTask()); + task_descriptor->join (); + } + + /** + * Get the return value of the + * function of the + * task. Since this is only + * available once the task + * finishes, this implicitly + * also calls join(). + */ + RT return_value () + { + join (); + return task_descriptor->ret_val.get(); + } + + + /** + * Check for equality of task + * objects. Since objects of + * this class store an implicit + * pointer to an object that + * exists exactly once for each + * task, the check is simply + * to compare these pointers. + */ + bool operator == (const Task &t) + { + AssertThrow (task_descriptor != + std_cxx1x::shared_ptr >(), + ExcNoTask()); + return task_descriptor == t.task_descriptor; + } + + /** @addtogroup Exceptions + * @{ */ + + /** + * Exception + */ + DeclException0 (ExcNoTask); + //@} + private: + /** + * Shared pointer to the object + * representing the task. Boost's + * shared pointer implementation will + * make sure that that object lives as + * long as there is at least one + * subscriber to it. + */ + std_cxx1x::shared_ptr > task_descriptor; }; - /** - * Overload of the new_task function for - * objects that can be converted to - * std_cxx1x::function, i.e. anything - * that can be called like a function - * object without arguments and returning - * an object of type RT (or void). - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * objects that can be converted to + * std_cxx1x::function, i.e. anything + * that can be called like a function + * object without arguments and returning + * an object of type RT (or void). + * + * @ingroup threads + */ template inline Task @@ -4205,13 +4239,13 @@ namespace Threads return Task(function); } - /** - * Overload of the new_task function for - * non-member or static member - * functions with no arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * non-member or static member + * functions with no arguments. + * + * @ingroup threads + */ template inline Task @@ -4221,13 +4255,13 @@ namespace Threads } - /** - * Overload of the non-const new_task - * function for member functions with - * no arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_task + * function for member functions with + * no arguments. + * + * @ingroup threads + */ template inline Task @@ -4240,13 +4274,13 @@ namespace Threads } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_task function for - * const member functions with no - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * const member functions with no + * arguments. + * + * @ingroup threads + */ template inline Task @@ -4263,13 +4297,13 @@ namespace Threads // ----------- thread starters for unary functions - /** - * Overload of the new_task function for - * non-member or static member - * functions with 1 argument. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * non-member or static member + * functions with 1 argument. + * + * @ingroup threads + */ template inline Task @@ -4284,13 +4318,13 @@ namespace Threads - /** - * Overload of the non-const new_task - * function for member functions with - * 1 argument. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_task + * function for member functions with + * 1 argument. + * + * @ingroup threads + */ template inline Task @@ -4305,13 +4339,13 @@ namespace Threads } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_task function for - * const member functions with 1 - * argument. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * const member functions with 1 + * argument. + * + * @ingroup threads + */ template inline Task @@ -4328,13 +4362,13 @@ namespace Threads // ----------- thread starters for binary functions - /** - * Overload of the new_task function for - * non-member or static member - * functions with 2 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * non-member or static member + * functions with 2 arguments. + * + * @ingroup threads + */ template inline Task @@ -4351,13 +4385,13 @@ namespace Threads - /** - * Overload of the non-const new_task - * function for member functions with - * 2 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_task + * function for member functions with + * 2 arguments. + * + * @ingroup threads + */ template inline Task @@ -4374,13 +4408,13 @@ namespace Threads } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_task function for - * const member functions with 2 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * const member functions with 2 + * arguments. + * + * @ingroup threads + */ template inline Task @@ -4399,15 +4433,15 @@ namespace Threads // ----------- thread starters for ternary functions - /** - * Overload of the new_task function for - * non-member or static member - * functions with 3 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * non-member or static member + * functions with 3 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3> inline Task new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3), @@ -4425,15 +4459,15 @@ namespace Threads - /** - * Overload of the non-const new_task - * function for member functions with - * 3 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_task + * function for member functions with + * 3 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3), @@ -4451,15 +4485,15 @@ namespace Threads } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_task function for - * const member functions with 3 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * const member functions with 3 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3) const, @@ -4480,15 +4514,15 @@ namespace Threads // ----------- thread starters for functions with 4 arguments - /** - * Overload of the new_task function for - * non-member or static member - * functions with 4 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * non-member or static member + * functions with 4 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, typename Arg4> inline Task new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4), @@ -4508,15 +4542,15 @@ namespace Threads - /** - * Overload of the non-const new_task - * function for member functions with - * 4 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_task + * function for member functions with + * 4 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, typename Arg4> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4), @@ -4536,15 +4570,15 @@ namespace Threads } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_task function for - * const member functions with 4 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * const member functions with 4 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, typename Arg4> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4) const, @@ -4566,16 +4600,16 @@ namespace Threads // ----------- thread starters for functions with 5 arguments - /** - * Overload of the new_task function for - * non-member or static member - * functions with 5 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * non-member or static member + * functions with 5 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> inline Task new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5), @@ -4597,16 +4631,16 @@ namespace Threads - /** - * Overload of the non-const new_task - * function for member functions with - * 5 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_task + * function for member functions with + * 5 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5), @@ -4628,16 +4662,16 @@ namespace Threads } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_task function for - * const member functions with 5 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * const member functions with 5 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5) const, @@ -4661,16 +4695,16 @@ namespace Threads // ----------- thread starters for functions with 6 arguments - /** - * Overload of the new_task function for - * non-member or static member - * functions with 6 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * non-member or static member + * functions with 6 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6> inline Task new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6), @@ -4694,16 +4728,16 @@ namespace Threads - /** - * Overload of the non-const new_task - * function for member functions with - * 6 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_task + * function for member functions with + * 6 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6), @@ -4727,16 +4761,16 @@ namespace Threads } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_task function for - * const member functions with 6 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * const member functions with 6 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6) const, @@ -4762,17 +4796,17 @@ namespace Threads // ----------- thread starters for functions with 7 arguments - /** - * Overload of the new_task function for - * non-member or static member - * functions with 7 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * non-member or static member + * functions with 7 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7> inline Task new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7), @@ -4798,17 +4832,17 @@ namespace Threads - /** - * Overload of the non-const new_task - * function for member functions with - * 7 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_task + * function for member functions with + * 7 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7), @@ -4834,17 +4868,17 @@ namespace Threads } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_task function for - * const member functions with 7 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * const member functions with 7 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7) const, @@ -4872,17 +4906,17 @@ namespace Threads // ----------- thread starters for functions with 8 arguments - /** - * Overload of the new_task function for - * non-member or static member - * functions with 8 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * non-member or static member + * functions with 8 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8> inline Task new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, @@ -4911,17 +4945,17 @@ namespace Threads - /** - * Overload of the non-const new_task - * function for member functions with - * 8 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_task + * function for member functions with + * 8 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, @@ -4950,17 +4984,17 @@ namespace Threads } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_task function for - * const member functions with 8 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * const member functions with 8 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, @@ -4991,17 +5025,17 @@ namespace Threads // ----------- thread starters for functions with 9 arguments - /** - * Overload of the new_task function for - * non-member or static member - * functions with 9 arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * non-member or static member + * functions with 9 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8, typename Arg9> inline Task new_task (RT (*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, @@ -5032,17 +5066,17 @@ namespace Threads - /** - * Overload of the non-const new_task - * function for member functions with - * 9 arguments. - * - * @ingroup threads - */ + /** + * Overload of the non-const new_task + * function for member functions with + * 9 arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8, typename Arg9> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, @@ -5073,17 +5107,17 @@ namespace Threads } #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG - /** - * Overload of the new_task function for - * const member functions with 9 - * arguments. - * - * @ingroup threads - */ + /** + * Overload of the new_task function for + * const member functions with 9 + * arguments. + * + * @ingroup threads + */ template + typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5, typename Arg6, + typename Arg7, typename Arg8, typename Arg9> inline Task new_task (RT (C::*fun_ptr)(Arg1,Arg2,Arg3,Arg4,Arg5, @@ -5117,62 +5151,63 @@ namespace Threads // ------------------------ TaskGroup ------------------------------------- - /** - * A container for task - * objects. Allows to add new - * task objects and wait for them - * all together. The task objects - * need to have the same return - * value for the called function. - * - * Note that the call to join_all() must be - * executed on the same thread as the calls - * that add subtasks. Otherwise, there - * might be a deadlock. In other words, a - * Task object should never passed on to - * another task for calling the join() - * method. - * - * @author Wolfgang Bangerth, 2003 - * @ingroup tasks - */ + /** + * A container for task + * objects. Allows to add new + * task objects and wait for them + * all together. The task objects + * need to have the same return + * value for the called function. + * + * Note that the call to join_all() must be + * executed on the same thread as the calls + * that add subtasks. Otherwise, there + * might be a deadlock. In other words, a + * Task object should never passed on to + * another task for calling the join() + * method. + * + * @author Wolfgang Bangerth, 2003 + * @ingroup tasks + */ template class TaskGroup { - public: - /** - * Add another task object to - * the collection. - */ - TaskGroup & operator += (const Task &t) - { - tasks.push_back (t); - return *this; - } - - /** - * Wait for all tasks in the - * collection to finish. It is - * not a problem if some of - * them have already been - * waited for, i.e. you may - * call this function more than - * once, and you can also add - * new task objects between - * subsequent calls to this - * function if you want. - */ - void join_all () const { - for (typename std::list >::const_iterator - t=tasks.begin(); t!=tasks.end(); ++t) - t->join (); - } + public: + /** + * Add another task object to + * the collection. + */ + TaskGroup &operator += (const Task &t) + { + tasks.push_back (t); + return *this; + } - private: - /** - * List of task objects. - */ - std::list > tasks; + /** + * Wait for all tasks in the + * collection to finish. It is + * not a problem if some of + * them have already been + * waited for, i.e. you may + * call this function more than + * once, and you can also add + * new task objects between + * subsequent calls to this + * function if you want. + */ + void join_all () const + { + for (typename std::list >::const_iterator + t=tasks.begin(); t!=tasks.end(); ++t) + t->join (); + } + + private: + /** + * List of task objects. + */ + std::list > tasks; }; } // end of implementation of namespace Threads diff --git a/deal.II/include/deal.II/base/timer.h b/deal.II/include/deal.II/base/timer.h index 1153a9b50e..553445e70f 100644 --- a/deal.II/include/deal.II/base/timer.h +++ b/deal.II/include/deal.II/base/timer.h @@ -70,186 +70,186 @@ DEAL_II_NAMESPACE_OPEN */ class Timer { - public: - /** - * Constructor. Starts the timer at 0 sec. - */ - Timer (); +public: + /** + * Constructor. Starts the timer at 0 sec. + */ + Timer (); #ifdef DEAL_II_COMPILER_SUPPORTS_MPI - /** - * Constructor that takes an MPI - * communicator as input. A timer - * constructed this way will sum up the - * CPU times over all processors in the - * MPI network when requested by the - * operator (). - * - * Starts the timer at 0 sec. - * - * If @p sync_wall_time is true, the wall - * time is synchronized between all CPUs - * using a MPI_Barrier() and a collective - * operation. Note that this only works - * if you stop() the timer before - * querying for the wall time. The time - * for the MPI operations are not - * included in the timing but may slow - * down your program. - * - * This constructor is only available - * if the deal.II compiler is an MPI - * compiler. - */ - Timer (MPI_Comm mpi_communicator, - bool sync_wall_time = false); - - /** - * Returns a reference to the data - * structure with global timing - * information. Filled after calling - * stop(). - */ - const Utilities::MPI::MinMaxAvg & get_data() const; - - /** - * Prints the data to the given stream. - */ - template - void print_data(STREAM & stream) const; + /** + * Constructor that takes an MPI + * communicator as input. A timer + * constructed this way will sum up the + * CPU times over all processors in the + * MPI network when requested by the + * operator (). + * + * Starts the timer at 0 sec. + * + * If @p sync_wall_time is true, the wall + * time is synchronized between all CPUs + * using a MPI_Barrier() and a collective + * operation. Note that this only works + * if you stop() the timer before + * querying for the wall time. The time + * for the MPI operations are not + * included in the timing but may slow + * down your program. + * + * This constructor is only available + * if the deal.II compiler is an MPI + * compiler. + */ + Timer (MPI_Comm mpi_communicator, + bool sync_wall_time = false); + + /** + * Returns a reference to the data + * structure with global timing + * information. Filled after calling + * stop(). + */ + const Utilities::MPI::MinMaxAvg &get_data() const; + + /** + * Prints the data to the given stream. + */ + template + void print_data(STREAM &stream) const; #endif - /** - * Re-start the timer at the point where - * it was stopped. This way a cumulative - * measurement of time is possible. - */ - void start (); - - /** - * Sets the current time as next - * starting time and return the - * elapsed time in seconds. - */ - double stop (); - - /** - * Stop the timer if necessary and reset - * the elapsed time to zero. - */ - void reset (); - - /** - * Resets the elapsed time to zero and - * starts the timer. This corresponds to - * calling @p reset() and @p start() on - * the Timer object. - */ - void restart(); - - /** - * Access to the current CPU time - * without disturbing time - * measurement. The elapsed time is - * returned in units of seconds. - */ - double operator() () const; - - /** - * Access to the current wall time - * without disturbing time - * measurement. The elapsed time is - * returned in units of seconds. - */ - double wall_time () const; - - private: - - /** - * Value of the user time when start() - * was called the last time or when the - * object was created and no stop() was - * issued in between. - */ - double start_time; - - - /** - * Similar to #start_time, but - * needed for children threads - * in multithread mode. Value of - * the user time when start() - * was called the last time or - * when the object was created - * and no stop() was issued in - * between. - * - * For some reason (error in - * operating system?) the - * function call - * getrusage(RUSAGE_CHILDREN,.) - * gives always 0 (at least - * on Solaris7). Hence the - * Timer class still does not - * yet work for multithreading - * mode. - */ - double start_time_children; - - /** - * Value of the wall time when start() - * was called the last time or when the - * object was created and no stop() was - * issued in between. - */ - double start_wall_time; - - /** - * Accumulated time for all previous - * start()/stop() cycles. The time for - * the present cycle is not included. - */ - double cumulative_time; - - /** - * Accumulated wall time for all - * previous start()/stop() cycles. The - * wall time for the present cycle is - * not included. - */ - double cumulative_wall_time; - - /** - * Store whether the timer is presently - * running. - */ - bool running; - - /** - * Store whether the timer is presently - * running. - */ - MPI_Comm mpi_communicator; + /** + * Re-start the timer at the point where + * it was stopped. This way a cumulative + * measurement of time is possible. + */ + void start (); + + /** + * Sets the current time as next + * starting time and return the + * elapsed time in seconds. + */ + double stop (); + + /** + * Stop the timer if necessary and reset + * the elapsed time to zero. + */ + void reset (); + + /** + * Resets the elapsed time to zero and + * starts the timer. This corresponds to + * calling @p reset() and @p start() on + * the Timer object. + */ + void restart(); + + /** + * Access to the current CPU time + * without disturbing time + * measurement. The elapsed time is + * returned in units of seconds. + */ + double operator() () const; + + /** + * Access to the current wall time + * without disturbing time + * measurement. The elapsed time is + * returned in units of seconds. + */ + double wall_time () const; + +private: + + /** + * Value of the user time when start() + * was called the last time or when the + * object was created and no stop() was + * issued in between. + */ + double start_time; + + + /** + * Similar to #start_time, but + * needed for children threads + * in multithread mode. Value of + * the user time when start() + * was called the last time or + * when the object was created + * and no stop() was issued in + * between. + * + * For some reason (error in + * operating system?) the + * function call + * getrusage(RUSAGE_CHILDREN,.) + * gives always 0 (at least + * on Solaris7). Hence the + * Timer class still does not + * yet work for multithreading + * mode. + */ + double start_time_children; + + /** + * Value of the wall time when start() + * was called the last time or when the + * object was created and no stop() was + * issued in between. + */ + double start_wall_time; + + /** + * Accumulated time for all previous + * start()/stop() cycles. The time for + * the present cycle is not included. + */ + double cumulative_time; + + /** + * Accumulated wall time for all + * previous start()/stop() cycles. The + * wall time for the present cycle is + * not included. + */ + double cumulative_wall_time; + + /** + * Store whether the timer is presently + * running. + */ + bool running; + + /** + * Store whether the timer is presently + * running. + */ + MPI_Comm mpi_communicator; #ifdef DEAL_II_COMPILER_SUPPORTS_MPI - /** - * Store whether the wall time is - * synchronized between machines. - */ - bool sync_wall_time; - - /** - * A structure for parallel wall time - * measurement that includes the minimum - * time recorded among all processes, the - * maximum time as well as the average - * time defined as the sum of all - * individual times divided by the number - * of MPI processes in the MPI_Comm. - */ - Utilities::System::MinMaxAvg mpi_data; + /** + * Store whether the wall time is + * synchronized between machines. + */ + bool sync_wall_time; + + /** + * A structure for parallel wall time + * measurement that includes the minimum + * time recorded among all processes, the + * maximum time as well as the average + * time defined as the sum of all + * individual times divided by the number + * of MPI processes in the MPI_Comm. + */ + Utilities::System::MinMaxAvg mpi_data; #endif }; @@ -319,204 +319,204 @@ class Timer */ class TimerOutput { - public: - /** - * Sets whether to generate output every - * time we exit a section, just in the - * end, or both. - */ - enum OutputFrequency {every_call, summary, every_call_and_summary} - output_frequency; - - /** - * Sets whether to show CPU times, wall - * times, or both CPU and wall times. - */ - enum OutputType {cpu_times, wall_times, cpu_and_wall_times} - output_type; - - /** - * Constructor that takes std::cout as - * output stream. - */ - TimerOutput (std::ostream &stream, - const enum OutputFrequency output_frequency, - const enum OutputType output_type); - - /** - * Constructor that takes a - * ConditionalOStream to write output to. - */ - TimerOutput (ConditionalOStream &stream, - const enum OutputFrequency output_frequency, - const enum OutputType output_type); +public: + /** + * Sets whether to generate output every + * time we exit a section, just in the + * end, or both. + */ + enum OutputFrequency {every_call, summary, every_call_and_summary} + output_frequency; + + /** + * Sets whether to show CPU times, wall + * times, or both CPU and wall times. + */ + enum OutputType {cpu_times, wall_times, cpu_and_wall_times} + output_type; + + /** + * Constructor that takes std::cout as + * output stream. + */ + TimerOutput (std::ostream &stream, + const enum OutputFrequency output_frequency, + const enum OutputType output_type); + + /** + * Constructor that takes a + * ConditionalOStream to write output to. + */ + TimerOutput (ConditionalOStream &stream, + const enum OutputFrequency output_frequency, + const enum OutputType output_type); #ifdef DEAL_II_COMPILER_SUPPORTS_MPI - /** - * Constructor that takes an MPI - * communicator as input. A timer - * constructed this way will sum up the - * CPU times over all processors in the - * MPI network for calculating the CPU - * time. - * - * Meant for using std::cout as output - * stream. - */ - TimerOutput (MPI_Comm mpi_comm, - std::ostream &stream, - const enum OutputFrequency output_frequency, - const enum OutputType output_type); - - /** - * Constructor that takes an MPI - * communicator as input. A timer - * constructed this way will sum up the - * CPU times over all processors in the - * MPI network for calculating the CPU - * time. - * - * Constructor that takes a - * ConditionalOStream to write output to. - */ - TimerOutput (MPI_Comm mpi_comm, - ConditionalOStream &stream, - const enum OutputFrequency output_frequency, - const enum OutputType output_type); + /** + * Constructor that takes an MPI + * communicator as input. A timer + * constructed this way will sum up the + * CPU times over all processors in the + * MPI network for calculating the CPU + * time. + * + * Meant for using std::cout as output + * stream. + */ + TimerOutput (MPI_Comm mpi_comm, + std::ostream &stream, + const enum OutputFrequency output_frequency, + const enum OutputType output_type); + + /** + * Constructor that takes an MPI + * communicator as input. A timer + * constructed this way will sum up the + * CPU times over all processors in the + * MPI network for calculating the CPU + * time. + * + * Constructor that takes a + * ConditionalOStream to write output to. + */ + TimerOutput (MPI_Comm mpi_comm, + ConditionalOStream &stream, + const enum OutputFrequency output_frequency, + const enum OutputType output_type); #endif - /** - * Destructor. Calls print_summary() in - * case the option for writing the - * summary output is set. - */ - ~TimerOutput(); - - /** - * Open a section by given a string name - * of it. In case the name already - * exists, that section is done once - * again. - */ - void enter_subsection (const std::string §ion_name); - - /** - * Same as @p enter_subsection. - */ - void enter_section (const std::string §ion_name); - - /** - * Leave a section. If no name is given, - * the last section that was entered is - * left. - */ - void leave_subsection (const std::string §ion_name = std::string()); - - /** - * Same as @p leave_subsection. - */ - void exit_section (const std::string §ion_name = std::string()); - - /** - * Print a formatted table that - * summarizes the time consumed in the - * various sections. - */ - void print_summary () const; - - /** - * By calling this function, all output - * can be disabled. This function - * together with enable_output() can be - * useful if one wants to control the - * output in a flexible way without - * putting a lot of if clauses - * in the program. - */ - void disable_output (); - - /** - * This function re-enables output of - * this class if it was previously - * disabled with disable_output(). This - * function together with - * disable_output() can be useful if - * one wants to control the output in a - * flexible way without putting a lot - * of if clauses in the - * program. - */ - void enable_output (); - - private: - /** - * A timer object for the overall - * run time. If we are using MPI, - * this timer also accumulates - * over all MPI processes. - */ - Timer timer_all; - - /** - * A structure that groups all - * information that we collect - * about each of the sections. - */ - struct Section - { - Timer timer; - double total_cpu_time; - double total_wall_time; - unsigned int n_calls; - }; - - /** - * A list of all the sections and - * their information. - */ - std::map sections; - - /** - * The stream object to which we - * are to output. - */ - ConditionalOStream out_stream; - - /** - * A boolean variable that sets whether - * output of this class is currently on - * or off. - */ - bool output_is_enabled; - - /** - * A list of the sections that - * have been entered and not - * exited. The list is kept in - * the order in which sections - * have been entered, but - * elements may be removed in the - * middle if an argument is given - * to the exit_section() - * function. - */ - std::list active_sections; - - /** - * mpi communicator - */ - MPI_Comm mpi_communicator; - - /** - * A lock that makes sure that this - * class gives reasonable results even - * when used with several threads. - */ - Threads::ThreadMutex mutex; + /** + * Destructor. Calls print_summary() in + * case the option for writing the + * summary output is set. + */ + ~TimerOutput(); + + /** + * Open a section by given a string name + * of it. In case the name already + * exists, that section is done once + * again. + */ + void enter_subsection (const std::string §ion_name); + + /** + * Same as @p enter_subsection. + */ + void enter_section (const std::string §ion_name); + + /** + * Leave a section. If no name is given, + * the last section that was entered is + * left. + */ + void leave_subsection (const std::string §ion_name = std::string()); + + /** + * Same as @p leave_subsection. + */ + void exit_section (const std::string §ion_name = std::string()); + + /** + * Print a formatted table that + * summarizes the time consumed in the + * various sections. + */ + void print_summary () const; + + /** + * By calling this function, all output + * can be disabled. This function + * together with enable_output() can be + * useful if one wants to control the + * output in a flexible way without + * putting a lot of if clauses + * in the program. + */ + void disable_output (); + + /** + * This function re-enables output of + * this class if it was previously + * disabled with disable_output(). This + * function together with + * disable_output() can be useful if + * one wants to control the output in a + * flexible way without putting a lot + * of if clauses in the + * program. + */ + void enable_output (); + +private: + /** + * A timer object for the overall + * run time. If we are using MPI, + * this timer also accumulates + * over all MPI processes. + */ + Timer timer_all; + + /** + * A structure that groups all + * information that we collect + * about each of the sections. + */ + struct Section + { + Timer timer; + double total_cpu_time; + double total_wall_time; + unsigned int n_calls; + }; + + /** + * A list of all the sections and + * their information. + */ + std::map sections; + + /** + * The stream object to which we + * are to output. + */ + ConditionalOStream out_stream; + + /** + * A boolean variable that sets whether + * output of this class is currently on + * or off. + */ + bool output_is_enabled; + + /** + * A list of the sections that + * have been entered and not + * exited. The list is kept in + * the order in which sections + * have been entered, but + * elements may be removed in the + * middle if an argument is given + * to the exit_section() + * function. + */ + std::list active_sections; + + /** + * mpi communicator + */ + MPI_Comm mpi_communicator; + + /** + * A lock that makes sure that this + * class gives reasonable results even + * when used with several threads. + */ + Threads::ThreadMutex mutex; }; @@ -547,7 +547,7 @@ Timer::get_data() const template inline void -Timer::print_data(STREAM & stream) const +Timer::print_data(STREAM &stream) const { unsigned int my_id = dealii::Utilities::MPI::this_mpi_process(mpi_communicator); if (my_id==0) diff --git a/deal.II/include/deal.II/base/types.h b/deal.II/include/deal.II/base/types.h index 51ac444b5f..39ff6bf5c8 100644 --- a/deal.II/include/deal.II/base/types.h +++ b/deal.II/include/deal.II/base/types.h @@ -22,118 +22,118 @@ DEAL_II_NAMESPACE_OPEN */ namespace types { - /** - * The type used to denote - * subdomain_ids of cells. - * - * See the @ref GlossSubdomainId - * "glossary" for more information. - * - * There is a special value, - * numbers::invalid_subdomain_id - * that is used to indicate an - * invalid value of this type. - */ + /** + * The type used to denote + * subdomain_ids of cells. + * + * See the @ref GlossSubdomainId + * "glossary" for more information. + * + * There is a special value, + * numbers::invalid_subdomain_id + * that is used to indicate an + * invalid value of this type. + */ typedef unsigned int subdomain_id; - /** - * @deprecated Old name for the typedef above. - */ + /** + * @deprecated Old name for the typedef above. + */ typedef subdomain_id subdomain_id_t; - /** - * @deprecated Use numbers::invalid_subdomain_id - */ + /** + * @deprecated Use numbers::invalid_subdomain_id + */ const unsigned int invalid_subdomain_id = static_cast(-1); - /** - * @deprecated Use numbers::artificial_subdomain_id - */ + /** + * @deprecated Use numbers::artificial_subdomain_id + */ const unsigned int artificial_subdomain_id = static_cast(-2); //#define DEAL_II_USE_LARGE_INDEX_TYPE #ifdef DEAL_II_USE_LARGE_INDEX_TYPE - /** - * The type used for global indices of - * degrees of freedom. While in sequential - * computations the 4 billion indices of - * 32-bit unsigned integers is plenty, - * parallel computations using the - * parallel::distributed::Triangulation - * class can overflow this number and we - * need a bigger index space. - * - * The data type always indicates an - * unsigned integer type. - */ - // TODO: we should check that unsigned long long int - // has the same size as uint64_t + /** + * The type used for global indices of + * degrees of freedom. While in sequential + * computations the 4 billion indices of + * 32-bit unsigned integers is plenty, + * parallel computations using the + * parallel::distributed::Triangulation + * class can overflow this number and we + * need a bigger index space. + * + * The data type always indicates an + * unsigned integer type. + */ + // TODO: we should check that unsigned long long int + // has the same size as uint64_t typedef unsigned long long int global_dof_index; - /** - * An identifier that denotes the MPI type - * associated with types::global_dof_index. - */ + /** + * An identifier that denotes the MPI type + * associated with types::global_dof_index. + */ # define DEAL_II_DOF_INDEX_MPI_TYPE MPI_UNSIGNED_LONG_LONG #else - /** - * The type used for global indices of - * degrees of freedom. While in sequential - * computations the 4 billion indices of - * 32-bit unsigned integers is plenty, - * parallel computations using the - * parallel::distributed::Triangulation - * class can overflow this number and we - * need a bigger index space. - * - * The data type always indicates an - * unsigned integer type. - */ + /** + * The type used for global indices of + * degrees of freedom. While in sequential + * computations the 4 billion indices of + * 32-bit unsigned integers is plenty, + * parallel computations using the + * parallel::distributed::Triangulation + * class can overflow this number and we + * need a bigger index space. + * + * The data type always indicates an + * unsigned integer type. + */ typedef unsigned int global_dof_index; - /** - * An identifier that denotes the MPI type - * associated with types::global_dof_index. - */ + /** + * An identifier that denotes the MPI type + * associated with types::global_dof_index. + */ # define DEAL_II_DOF_INDEX_MPI_TYPE MPI_UNSIGNED #endif - /** - * @deprecated Use numbers::invalid_dof_index - */ + /** + * @deprecated Use numbers::invalid_dof_index + */ const global_dof_index invalid_dof_index = static_cast(-1); - /** - * The type used to denote boundary indicators associated with every - * piece of the boundary and, in the case of meshes that describe - * manifolds in higher dimensions, associated with every cell. - * - * There is a special value, numbers::internal_face_boundary_id - * that is used to indicate an invalid value of this type and that - * is used as the boundary indicator for faces that are in the interior - * of the domain and therefore not part of any addressable boundary - * component. - */ + /** + * The type used to denote boundary indicators associated with every + * piece of the boundary and, in the case of meshes that describe + * manifolds in higher dimensions, associated with every cell. + * + * There is a special value, numbers::internal_face_boundary_id + * that is used to indicate an invalid value of this type and that + * is used as the boundary indicator for faces that are in the interior + * of the domain and therefore not part of any addressable boundary + * component. + */ typedef unsigned char boundary_id; - /** - * @deprecated Old name for the typedef above. - */ + /** + * @deprecated Old name for the typedef above. + */ typedef boundary_id boundary_id_t; - /** - * The type used to denote material indicators associated with every - * cell. - * - * There is a special value, numbers::invalid_material_id - * that is used to indicate an invalid value of this type. - */ + /** + * The type used to denote material indicators associated with every + * cell. + * + * There is a special value, numbers::invalid_material_id + * that is used to indicate an invalid value of this type. + */ typedef unsigned char material_id; - /** - * @deprecated Old name for the typedef above. - */ + /** + * @deprecated Old name for the typedef above. + */ typedef material_id material_id_t; } @@ -144,79 +144,79 @@ namespace types // numbers.h namespace numbers { - /** - * Representation of the - * largest number that - * can be put into an - * unsigned integer. This - * value is widely used - * throughout the library - * as a marker for an - * invalid unsigned - * integer value, such as - * an invalid array - * index, an invalid - * array size, and the - * like. - */ + /** + * Representation of the + * largest number that + * can be put into an + * unsigned integer. This + * value is widely used + * throughout the library + * as a marker for an + * invalid unsigned + * integer value, such as + * an invalid array + * index, an invalid + * array size, and the + * like. + */ static const unsigned int invalid_unsigned_int = static_cast (-1); - /** - * An invalid value for indices of degrees - * of freedom. - */ + /** + * An invalid value for indices of degrees + * of freedom. + */ const types::global_dof_index invalid_dof_index = static_cast(-1); - /** - * Invalid material_id which we - * need in several places as a - * default value. We assume that - * all material_ids lie in the - * range [0, invalid_material_id). - */ + /** + * Invalid material_id which we + * need in several places as a + * default value. We assume that + * all material_ids lie in the + * range [0, invalid_material_id). + */ const types::material_id invalid_material_id = static_cast(-1); - /** - * The number which we reserve for - * internal faces. We assume that - * all boundary_ids lie in the - * range [0, - * internal_face_boundary_id). - */ + /** + * The number which we reserve for + * internal faces. We assume that + * all boundary_ids lie in the + * range [0, + * internal_face_boundary_id). + */ const types::boundary_id internal_face_boundary_id = static_cast(-1); - /** - * A special id for an invalid - * subdomain id. This value may not - * be used as a valid id but is - * used, for example, for default - * arguments to indicate a - * subdomain id that is not to be - * used. - * - * See the @ref GlossSubdomainId - * "glossary" for more information. - */ + /** + * A special id for an invalid + * subdomain id. This value may not + * be used as a valid id but is + * used, for example, for default + * arguments to indicate a + * subdomain id that is not to be + * used. + * + * See the @ref GlossSubdomainId + * "glossary" for more information. + */ const types::subdomain_id invalid_subdomain_id = static_cast(-1); - /** - * The subdomain id assigned to a - * cell whose true subdomain id we - * don't know, for example because - * it resides on a different - * processor on a mesh that is kept - * distributed on many - * processors. Such cells are - * called "artificial". - * - * See the glossary entries on @ref - * GlossSubdomainId "subdomain ids" - * and @ref GlossArtificialCell - * "artificial cells" as well as - * the @ref distributed module for - * more information. - */ + /** + * The subdomain id assigned to a + * cell whose true subdomain id we + * don't know, for example because + * it resides on a different + * processor on a mesh that is kept + * distributed on many + * processors. Such cells are + * called "artificial". + * + * See the glossary entries on @ref + * GlossSubdomainId "subdomain ids" + * and @ref GlossArtificialCell + * "artificial cells" as well as + * the @ref distributed module for + * more information. + */ const types::subdomain_id artificial_subdomain_id = static_cast(-2); } diff --git a/deal.II/include/deal.II/base/utilities.h b/deal.II/include/deal.II/base/utilities.h index 994630cb33..4ded7f1412 100644 --- a/deal.II/include/deal.II/base/utilities.h +++ b/deal.II/include/deal.II/base/utilities.h @@ -45,222 +45,222 @@ DEAL_II_NAMESPACE_OPEN namespace Utilities { - /** - * Convert a number @p i to a string, with - * as many digits as given to fill with - * leading zeros. - * - * If the second parameter is left at its - * default value, the number is not padded - * with leading zeros. The result is then - * the same as of the standard C function - * itoa() had been called. - */ + /** + * Convert a number @p i to a string, with + * as many digits as given to fill with + * leading zeros. + * + * If the second parameter is left at its + * default value, the number is not padded + * with leading zeros. The result is then + * the same as of the standard C function + * itoa() had been called. + */ std::string int_to_string (const unsigned int i, const unsigned int digits = numbers::invalid_unsigned_int); - /** - * Determine how many digits are needed to - * represent numbers at most as large as - * the given number. - */ + /** + * Determine how many digits are needed to + * represent numbers at most as large as + * the given number. + */ unsigned int needed_digits (const unsigned int max_number); - /** - * Given a string, convert it to an - * integer. Throw an assertion if that is - * not possible. - */ + /** + * Given a string, convert it to an + * integer. Throw an assertion if that is + * not possible. + */ int string_to_int (const std::string &s); - /** - * Given a list of strings, convert it to a - * list of integers. Throw an assertion if - * that is not possible. - */ + /** + * Given a list of strings, convert it to a + * list of integers. Throw an assertion if + * that is not possible. + */ std::vector string_to_int (const std::vector &s); - /** - * Given a string, convert it to an - * double. Throw an assertion if that is - * not possible. - */ + /** + * Given a string, convert it to an + * double. Throw an assertion if that is + * not possible. + */ double string_to_double (const std::string &s); - /** - * Given a list of strings, convert it to a - * list of doubles. Throw an assertion if - * that is not possible. - */ + /** + * Given a list of strings, convert it to a + * list of doubles. Throw an assertion if + * that is not possible. + */ std::vector string_to_double (const std::vector &s); - /** - * Given a string that contains text - * separated by a @p delimiter, split it into - * its components; for each component, - * remove leading and trailing spaces. - * - * The default value of the delimiter is a - * comma, so that the function splits comma - * separated lists of strings. - */ + /** + * Given a string that contains text + * separated by a @p delimiter, split it into + * its components; for each component, + * remove leading and trailing spaces. + * + * The default value of the delimiter is a + * comma, so that the function splits comma + * separated lists of strings. + */ std::vector split_string_list (const std::string &s, const char delimiter = ','); - /** - * Take a text, usually a documentation or - * something, and try to break it into - * individual lines of text at most @p - * width characters wide, by breaking at - * positions marked by @p delimiter in the - * text. If this is not possible, return - * the shortest lines that are longer than - * @p width. The default value of the - * delimiter is a space character. If - * original_text contains newline - * characters (\n), the string is split at - * these locations, too. - */ + /** + * Take a text, usually a documentation or + * something, and try to break it into + * individual lines of text at most @p + * width characters wide, by breaking at + * positions marked by @p delimiter in the + * text. If this is not possible, return + * the shortest lines that are longer than + * @p width. The default value of the + * delimiter is a space character. If + * original_text contains newline + * characters (\n), the string is split at + * these locations, too. + */ std::vector break_text_into_lines (const std::string &original_text, const unsigned int width, const char delimiter = ' '); - /** - * Return true if the given pattern - * string appears in the first - * position of the string. - */ + /** + * Return true if the given pattern + * string appears in the first + * position of the string. + */ bool match_at_string_start (const std::string &name, const std::string &pattern); - /** - * Read a (signed) integer starting - * at the position in @p name - * indicated by the second - * argument, and retun this integer - * as a pair together with how many - * characters it takes up in the - * string. - * - * If no integer can be read at the - * indicated position, return - * (-1,numbers::invalid_unsigned_int) - */ + /** + * Read a (signed) integer starting + * at the position in @p name + * indicated by the second + * argument, and retun this integer + * as a pair together with how many + * characters it takes up in the + * string. + * + * If no integer can be read at the + * indicated position, return + * (-1,numbers::invalid_unsigned_int) + */ std::pair get_integer_at_position (const std::string &name, const unsigned int position); - /** - * Generate a random number from a - * normalized Gaussian probability - * distribution centered around @p a and - * with standard deviation @p sigma. - */ + /** + * Generate a random number from a + * normalized Gaussian probability + * distribution centered around @p a and + * with standard deviation @p sigma. + */ double generate_normal_random_number (const double a, const double sigma); - /** - * Calculate a fixed power, provided as a - * template argument, of a number. - * - * This function provides an efficient way - * to calculate things like - * t^N where N is - * a known number at compile time. - * - * Use this function as in - * fixed_power@ (n). - */ + /** + * Calculate a fixed power, provided as a + * template argument, of a number. + * + * This function provides an efficient way + * to calculate things like + * t^N where N is + * a known number at compile time. + * + * Use this function as in + * fixed_power@ (n). + */ template T fixed_power (const T t); - /** - * Calculate a fixed power of an integer - * number by a template expression where - * both the number a and the - * power N are compile-time - * constants. This computes the result of - * the power operation at compile time, - * enables its use e.g. in other - * templates. - * - * Use this function with the arguments - * fixed_int_power@<5,2@>::value - * to compute 52. - */ + /** + * Calculate a fixed power of an integer + * number by a template expression where + * both the number a and the + * power N are compile-time + * constants. This computes the result of + * the power operation at compile time, + * enables its use e.g. in other + * templates. + * + * Use this function with the arguments + * fixed_int_power@<5,2@>::value + * to compute 52. + */ template struct fixed_int_power { - static const int value = a * fixed_int_power::value; + static const int value = a *fixed_int_power::value; }; - /** - * Base case for the power operation with - * N=0, which gives the result - * 1. - */ + /** + * Base case for the power operation with + * N=0, which gives the result + * 1. + */ template struct fixed_int_power { static const int value = 1; }; - /** - * Optimized replacement for - * std::lower_bound for - * searching within the range of - * column indices. Slashes - * execution time by - * approximately one half for the - * present application, partly - * because because the - * binary search is replaced by a - * linear search for small loop - * lengths. - * - * Another reason for this function is - * rather obscure: - * when using the GCC libstdc++ - * function std::lower_bound, - * complexity is O(log(N)) as - * required. However, when using the - * debug version of the GCC libstdc++ - * as we do when running the testsuite, - * then std::lower_bound tests whether - * the sequence is in fact partitioned - * with respect to the pivot 'value' - * (i.e. in essence that the sequence - * is sorted as required for binary - * search to work). However, verifying - * this means that the complexity of - * std::lower_bound jumps to O(N); we - * call this function O(N) times below, - * making the overall complexity - * O(N**2). The consequence is that a - * few tests with big meshes completely - * run off the wall time limit for - * tests and fail with the libstdc++ - * debug mode - * - * This function simply makes the - * assumption that the sequence is - * sorted, and we simply don't do the - * additional check. - */ + /** + * Optimized replacement for + * std::lower_bound for + * searching within the range of + * column indices. Slashes + * execution time by + * approximately one half for the + * present application, partly + * because because the + * binary search is replaced by a + * linear search for small loop + * lengths. + * + * Another reason for this function is + * rather obscure: + * when using the GCC libstdc++ + * function std::lower_bound, + * complexity is O(log(N)) as + * required. However, when using the + * debug version of the GCC libstdc++ + * as we do when running the testsuite, + * then std::lower_bound tests whether + * the sequence is in fact partitioned + * with respect to the pivot 'value' + * (i.e. in essence that the sequence + * is sorted as required for binary + * search to work). However, verifying + * this means that the complexity of + * std::lower_bound jumps to O(N); we + * call this function O(N) times below, + * making the overall complexity + * O(N**2). The consequence is that a + * few tests with big meshes completely + * run off the wall time limit for + * tests and fail with the libstdc++ + * debug mode + * + * This function simply makes the + * assumption that the sequence is + * sorted, and we simply don't do the + * additional check. + */ template Iterator lower_bound (Iterator first, @@ -268,12 +268,12 @@ namespace Utilities const T &val); - /** - * The same function as above, but taking - * an argument that is used to compare - * individual elements of the sequence of - * objects pointed to by the iterators. - */ + /** + * The same function as above, but taking + * an argument that is used to compare + * individual elements of the sequence of + * objects pointed to by the iterators. + */ template Iterator lower_bound (Iterator first, @@ -281,185 +281,185 @@ namespace Utilities const T &val, const Comp comp); - /** - * Given a permutation vector (i.e. a - * vector $p_0\ldots p_{N-1}$ where each - * $p_i\in [0,N)$ and $p_i\neq p_j$ for - * $i\neq j$), produce the reverse - * permutation $q_i=N-1-p_i$. - */ + /** + * Given a permutation vector (i.e. a + * vector $p_0\ldots p_{N-1}$ where each + * $p_i\in [0,N)$ and $p_i\neq p_j$ for + * $i\neq j$), produce the reverse + * permutation $q_i=N-1-p_i$. + */ std::vector reverse_permutation (const std::vector &permutation); - /** - * Given a permutation vector (i.e. a - * vector $p_0\ldots p_{N-1}$ where each - * $p_i\in [0,N)$ and $p_i\neq p_j$ for - * $i\neq j$), produce the inverse - * permutation $q_0\ldots q_{N-1}$ so that - * $q_{p_i}=p_{q_i}=i$. - */ + /** + * Given a permutation vector (i.e. a + * vector $p_0\ldots p_{N-1}$ where each + * $p_i\in [0,N)$ and $p_i\neq p_j$ for + * $i\neq j$), produce the inverse + * permutation $q_0\ldots q_{N-1}$ so that + * $q_{p_i}=p_{q_i}=i$. + */ std::vector invert_permutation (const std::vector &permutation); - /** - * A namespace for utility functions that - * probe system properties. - * - * @ingroup utilities - */ + /** + * A namespace for utility functions that + * probe system properties. + * + * @ingroup utilities + */ namespace System { - /** - * Return the CPU load as returned by - * "uptime". Note that the interpretation - * of this number depends on the actual - * number of processors in the - * machine. This is presently only - * implemented on Linux, using the - * /proc/loadavg pseudo-file, on other - * systems we simply return zero. - */ + /** + * Return the CPU load as returned by + * "uptime". Note that the interpretation + * of this number depends on the actual + * number of processors in the + * machine. This is presently only + * implemented on Linux, using the + * /proc/loadavg pseudo-file, on other + * systems we simply return zero. + */ double get_cpu_load (); - /** - * Structure that hold information about - * memory usage in kB. Used by - * get_memory_stats(). See man 5 proc - * entry /status for details. - */ + /** + * Structure that hold information about + * memory usage in kB. Used by + * get_memory_stats(). See man 5 proc + * entry /status for details. + */ struct MemoryStats { - unsigned long int VmPeak; /** peak virtual memory size in kB */ - unsigned long int VmSize; /** current virtual memory size in kB */ - unsigned long int VmHWM; /** peak resident memory size in kB */ - unsigned long int VmRSS; /** current resident memory size in kB */ + unsigned long int VmPeak; /** peak virtual memory size in kB */ + unsigned long int VmSize; /** current virtual memory size in kB */ + unsigned long int VmHWM; /** peak resident memory size in kB */ + unsigned long int VmRSS; /** current resident memory size in kB */ }; - /** - * Fills the @param stats structure with - * information about the memory - * consumption of this process. This is - * only implemented on Linux. - */ - void get_memory_stats (MemoryStats & stats); + /** + * Fills the @param stats structure with + * information about the memory + * consumption of this process. This is + * only implemented on Linux. + */ + void get_memory_stats (MemoryStats &stats); - /** - * Return the name of the host this - * process runs on. - */ + /** + * Return the name of the host this + * process runs on. + */ std::string get_hostname (); - /** - * Return the present time as HH:MM:SS. - */ + /** + * Return the present time as HH:MM:SS. + */ std::string get_time (); - /** - * Return whether (i) deal.II has - * been compiled to support MPI - * (for example by compiling with - * CXX=mpiCC) and if - * so whether (ii) - * MPI_Init() has - * been called (for example using - * the - * Utilities::System::MPI_InitFinalize - * class). In other words, the - * result indicates whether the - * current job is running under - * MPI. - * - * @note The function does not - * take into account whether an - * MPI job actually runs on more - * than one processor or is, in - * fact, a single-node job that - * happens to run under MPI. - */ + /** + * Return whether (i) deal.II has + * been compiled to support MPI + * (for example by compiling with + * CXX=mpiCC) and if + * so whether (ii) + * MPI_Init() has + * been called (for example using + * the + * Utilities::System::MPI_InitFinalize + * class). In other words, the + * result indicates whether the + * current job is running under + * MPI. + * + * @note The function does not + * take into account whether an + * MPI job actually runs on more + * than one processor or is, in + * fact, a single-node job that + * happens to run under MPI. + */ bool job_supports_mpi (); - /** - * Alias for job_supports_mpi(). - * - * @deprecated - */ + /** + * Alias for job_supports_mpi(). + * + * @deprecated + */ bool program_uses_mpi (); - /** - * @name Functions that work - * in parallel via MPI. The - * functions following here - * are all deprecated and have - * been moved to namespace - * Utilities::MPI. - */ - /** @{ */ - - /** - * This function is an alias for - * Utilities::MPI::n_mpi_processes. - * - * @deprecated - */ + /** + * @name Functions that work + * in parallel via MPI. The + * functions following here + * are all deprecated and have + * been moved to namespace + * Utilities::MPI. + */ + /** @{ */ + + /** + * This function is an alias for + * Utilities::MPI::n_mpi_processes. + * + * @deprecated + */ unsigned int get_n_mpi_processes (const MPI_Comm &mpi_communicator); - /** - * This function is an alias for - * Utilities::MPI::this_mpi_process. - * - * @deprecated - */ + /** + * This function is an alias for + * Utilities::MPI::this_mpi_process. + * + * @deprecated + */ unsigned int get_this_mpi_process (const MPI_Comm &mpi_communicator); - /** - * This function is an alias for - * Utilities::MPI::compute_point_to_point_communication_pattern. - * - * @deprecated - */ + /** + * This function is an alias for + * Utilities::MPI::compute_point_to_point_communication_pattern. + * + * @deprecated + */ using Utilities::MPI::compute_point_to_point_communication_pattern; - /** - * This function is an alias for - * Utilities::MPI::duplicate_communicator. - * - * @deprecated - */ + /** + * This function is an alias for + * Utilities::MPI::duplicate_communicator. + * + * @deprecated + */ using Utilities::MPI::duplicate_communicator; - /** - * An alias for Utilities::MPI::MinMaxAvg. - * - * @deprecated - */ + /** + * An alias for Utilities::MPI::MinMaxAvg. + * + * @deprecated + */ using Utilities::MPI::MinMaxAvg; - /** - * An alias for Utilities::MPI::min_max_avg. - * - * @deprecated - */ + /** + * An alias for Utilities::MPI::min_max_avg. + * + * @deprecated + */ void calculate_collective_mpi_min_max_avg (const MPI_Comm &mpi_communicator, const double my_value, - MinMaxAvg & result); + MinMaxAvg &result); - /** - * An alias for Utilities::MPI::MPI_InitFinalize. - * - * @deprecated - */ + /** + * An alias for Utilities::MPI::MPI_InitFinalize. + * + * @deprecated + */ using Utilities::MPI::MPI_InitFinalize; - /** @} */ + /** @} */ } @@ -471,172 +471,172 @@ namespace Utilities */ namespace Trilinos { - /** - * Returns a Trilinos Epetra_Comm - * object needed for creation of - * Epetra_Maps. - * - * If deal.II has been configured to use - * a compiler that does not support MPI - * then the resulting communicator will - * be a serial one. Otherwise, the - * communicator will correspond to - * MPI_COMM_WORLD, i.e. a communicator - * that encompasses all processes within - * this MPI universe. - */ - const Epetra_Comm& comm_world(); - - /** - * Returns a Trilinos Epetra_Comm - * object needed for creation of - * Epetra_Maps. - * - * If deal.II has been configured to use - * a compiler that does not support MPI - * then the resulting communicator will - * be a serial one. Otherwise, the - * communicator will correspond to - * MPI_COMM_SELF, i.e. a communicator - * that comprises only this one - * processor. - */ - const Epetra_Comm& comm_self(); - - /** - * Given a communicator, duplicate it. If - * the given communicator is serial, that - * means to just return a copy of - * itself. On the other hand, if it is - * %parallel, we duplicate the underlying - * MPI_Comm object: we create a separate - * MPI communicator that contains the - * same processors and in the same order - * but has a separate identifier distinct - * from the given communicator. The - * function returns a pointer to a new - * object of a class derived from - * Epetra_Comm. The caller of this - * function needs to assume ownership of - * this function. The returned object - * should be destroyed using the - * destroy_communicator() function. - * - * This facility is used to separate - * streams of communication. For example, - * a program could simply use - * MPI_Comm_World for everything. But it - * is easy to come up with scenarios - * where sometimes not all processors - * participate in a communication that is - * intended to be global -- for example - * if we assemble a matrix on a coarse - * mesh with fewer cells than there are - * processors, some processors may not - * sync their matrices with the rest - * because they haven't written into it - * because they own no cells. That's - * clearly a bug. However, if these - * processors just continue their work, - * and the next %parallel operation - * happens to be a sync on a different - * matrix, then the sync could succeed -- - * by accident, since different - * processors are talking about different - * matrices. - * - * This kind of situation can be avoided - * if we use different communicators for - * different matrices which reduces the - * likelihood that communications meant - * to be separate aren't recognized as - * such just because they happen on the - * same communicator. In addition, it is - * conceivable that some MPI operations - * can be parallelized using multiple - * threads because their communicators - * identifies the communication in - * question, not their relative timing as - * is the case in a sequential program - * that just uses a single communicator. - */ + /** + * Returns a Trilinos Epetra_Comm + * object needed for creation of + * Epetra_Maps. + * + * If deal.II has been configured to use + * a compiler that does not support MPI + * then the resulting communicator will + * be a serial one. Otherwise, the + * communicator will correspond to + * MPI_COMM_WORLD, i.e. a communicator + * that encompasses all processes within + * this MPI universe. + */ + const Epetra_Comm &comm_world(); + + /** + * Returns a Trilinos Epetra_Comm + * object needed for creation of + * Epetra_Maps. + * + * If deal.II has been configured to use + * a compiler that does not support MPI + * then the resulting communicator will + * be a serial one. Otherwise, the + * communicator will correspond to + * MPI_COMM_SELF, i.e. a communicator + * that comprises only this one + * processor. + */ + const Epetra_Comm &comm_self(); + + /** + * Given a communicator, duplicate it. If + * the given communicator is serial, that + * means to just return a copy of + * itself. On the other hand, if it is + * %parallel, we duplicate the underlying + * MPI_Comm object: we create a separate + * MPI communicator that contains the + * same processors and in the same order + * but has a separate identifier distinct + * from the given communicator. The + * function returns a pointer to a new + * object of a class derived from + * Epetra_Comm. The caller of this + * function needs to assume ownership of + * this function. The returned object + * should be destroyed using the + * destroy_communicator() function. + * + * This facility is used to separate + * streams of communication. For example, + * a program could simply use + * MPI_Comm_World for everything. But it + * is easy to come up with scenarios + * where sometimes not all processors + * participate in a communication that is + * intended to be global -- for example + * if we assemble a matrix on a coarse + * mesh with fewer cells than there are + * processors, some processors may not + * sync their matrices with the rest + * because they haven't written into it + * because they own no cells. That's + * clearly a bug. However, if these + * processors just continue their work, + * and the next %parallel operation + * happens to be a sync on a different + * matrix, then the sync could succeed -- + * by accident, since different + * processors are talking about different + * matrices. + * + * This kind of situation can be avoided + * if we use different communicators for + * different matrices which reduces the + * likelihood that communications meant + * to be separate aren't recognized as + * such just because they happen on the + * same communicator. In addition, it is + * conceivable that some MPI operations + * can be parallelized using multiple + * threads because their communicators + * identifies the communication in + * question, not their relative timing as + * is the case in a sequential program + * that just uses a single communicator. + */ Epetra_Comm * duplicate_communicator (const Epetra_Comm &communicator); - /** - * Given an Epetra communicator that was - * created by the - * duplicate_communicator() function, - * destroy the underlying MPI - * communicator object and reset the - * Epetra_Comm object to a the result of - * comm_self(). - * - * It is necessary to call this function - * at the time when the result of - * duplicate_communicator() is no longer - * needed. The reason is that in that - * function, we first create a new - * MPI_Comm object and then create an - * Epetra_Comm around it. While we can - * take care of destroying the latter, it - * doesn't destroy the communicator since - * it can only assume that it may also be - * still used by other objects in the - * program. Consequently, we have to take - * care of destroying it ourselves, - * explicitly. - * - * This function does exactly - * that. Because this has to happen while - * the Epetra_Comm object is still - * around, it first resets the latter and - * then destroys the communicator object. - * - * @note If you call this function on an - * Epetra_Comm object that is not created - * by duplicate_communicator(), you are - * likely doing something quite - * wrong. Don't do this. - */ + /** + * Given an Epetra communicator that was + * created by the + * duplicate_communicator() function, + * destroy the underlying MPI + * communicator object and reset the + * Epetra_Comm object to a the result of + * comm_self(). + * + * It is necessary to call this function + * at the time when the result of + * duplicate_communicator() is no longer + * needed. The reason is that in that + * function, we first create a new + * MPI_Comm object and then create an + * Epetra_Comm around it. While we can + * take care of destroying the latter, it + * doesn't destroy the communicator since + * it can only assume that it may also be + * still used by other objects in the + * program. Consequently, we have to take + * care of destroying it ourselves, + * explicitly. + * + * This function does exactly + * that. Because this has to happen while + * the Epetra_Comm object is still + * around, it first resets the latter and + * then destroys the communicator object. + * + * @note If you call this function on an + * Epetra_Comm object that is not created + * by duplicate_communicator(), you are + * likely doing something quite + * wrong. Don't do this. + */ void destroy_communicator (Epetra_Comm &communicator); - /** - * Return the number of MPI processes - * there exist in the given communicator - * object. If this is a sequential job, - * it returns 1. - */ + /** + * Return the number of MPI processes + * there exist in the given communicator + * object. If this is a sequential job, + * it returns 1. + */ unsigned int get_n_mpi_processes (const Epetra_Comm &mpi_communicator); - /** - * Return the number of the present MPI - * process in the space of processes - * described by the given - * communicator. This will be a unique - * value for each process between zero - * and (less than) the number of all - * processes (given by - * get_n_mpi_processes()). - */ + /** + * Return the number of the present MPI + * process in the space of processes + * described by the given + * communicator. This will be a unique + * value for each process between zero + * and (less than) the number of all + * processes (given by + * get_n_mpi_processes()). + */ unsigned int get_this_mpi_process (const Epetra_Comm &mpi_communicator); - /** - * Given a Trilinos Epetra map, create a - * new map that has the same subdivision - * of elements to processors but uses the - * given communicator object instead of - * the one stored in the first - * argument. In essence, this means that - * we create a map that communicates - * among the same processors in the same - * way, but using a separate channel. - * - * This function is typically used with a - * communicator that has been obtained by - * the duplicate_communicator() function. - */ + /** + * Given a Trilinos Epetra map, create a + * new map that has the same subdivision + * of elements to processors but uses the + * given communicator object instead of + * the one stored in the first + * argument. In essence, this means that + * we create a map that communicates + * among the same processors in the same + * way, but using a separate channel. + * + * This function is typically used with a + * communicator that has been obtained by + * the duplicate_communicator() function. + */ Epetra_Map duplicate_map (const Epetra_BlockMap &map, const Epetra_Comm &comm); @@ -659,21 +659,21 @@ namespace Utilities Assert (N>0, ExcNotImplemented()); switch (N) { - case 0: - return 1; - case 1: - return n; - case 2: - return n*n; - case 3: - return n*n*n; - case 4: - return n*n*n*n; - default: - T result = 1; - for (int d=0;d> 1; const Iterator middle = first + half; - // if the value is larger than - // that pointed to by the - // middle pointer, then the - // insertion point must be - // right of it + // if the value is larger than + // that pointed to by the + // middle pointer, then the + // insertion point must be + // right of it if (comp(*middle, val)) { first = middle + 1; diff --git a/deal.II/include/deal.II/base/vector_slice.h b/deal.II/include/deal.II/base/vector_slice.h index cb79d90cc8..af436af31a 100644 --- a/deal.II/include/deal.II/base/vector_slice.h +++ b/deal.II/include/deal.II/base/vector_slice.h @@ -44,84 +44,84 @@ DEAL_II_NAMESPACE_OPEN template class VectorSlice { - public: - /** - * Construct a vector slice - * containing the whole - * vector. Comes handy, if you - * did not want to have a slice - * at all, but the function you - * call wants it: just put in the - * vector itself as argument and - * let this constructor make a - * slice for you. - */ - VectorSlice(VECTOR& v); - /** - * The real constructor for a - * vector slice, allowing you to - * specify the start index and - * the length of the slice. - */ - VectorSlice(VECTOR& v, - unsigned int start, - unsigned int length); - - /** - * Return the length of the slice - * using the same interface as - * std::vector. - */ - unsigned int size() const; - - /** - * Access an element of the slice - * using the same interface as - * std::vector. - */ - typename VECTOR::reference operator[] (unsigned int i); - - /** - * Access an element of a - * constant slice using the same - * interface as - * std::vector. - */ - typename VECTOR::const_reference operator[] (unsigned int i) const; - - /** - * STL conforming iterator function. - */ - typename VECTOR::iterator begin(); - - /** - * STL conforming iterator function. - */ - typename VECTOR::const_iterator begin() const; - - /** - * STL conforming iterator function. - */ - typename VECTOR::iterator end(); - - /** - * STL conforming iterator function. - */ - typename VECTOR::const_iterator end() const; - - private: - /** - * The vector we extract from. - */ - VECTOR& v; - /** - * The start index of the slice. - */ - const unsigned int start; - /** - * The length of the slice. - */ - const unsigned int length; +public: + /** + * Construct a vector slice + * containing the whole + * vector. Comes handy, if you + * did not want to have a slice + * at all, but the function you + * call wants it: just put in the + * vector itself as argument and + * let this constructor make a + * slice for you. + */ + VectorSlice(VECTOR &v); + /** + * The real constructor for a + * vector slice, allowing you to + * specify the start index and + * the length of the slice. + */ + VectorSlice(VECTOR &v, + unsigned int start, + unsigned int length); + + /** + * Return the length of the slice + * using the same interface as + * std::vector. + */ + unsigned int size() const; + + /** + * Access an element of the slice + * using the same interface as + * std::vector. + */ + typename VECTOR::reference operator[] (unsigned int i); + + /** + * Access an element of a + * constant slice using the same + * interface as + * std::vector. + */ + typename VECTOR::const_reference operator[] (unsigned int i) const; + + /** + * STL conforming iterator function. + */ + typename VECTOR::iterator begin(); + + /** + * STL conforming iterator function. + */ + typename VECTOR::const_iterator begin() const; + + /** + * STL conforming iterator function. + */ + typename VECTOR::iterator end(); + + /** + * STL conforming iterator function. + */ + typename VECTOR::const_iterator end() const; + +private: + /** + * The vector we extract from. + */ + VECTOR &v; + /** + * The start index of the slice. + */ + const unsigned int start; + /** + * The length of the slice. + */ + const unsigned int length; }; @@ -135,7 +135,7 @@ class VectorSlice template inline const VectorSlice -make_slice (VECTOR& v) +make_slice (VECTOR &v) { const VectorSlice r(v); return r; @@ -153,7 +153,7 @@ make_slice (VECTOR& v) template inline const VectorSlice -make_slice (VECTOR& v, +make_slice (VECTOR &v, const unsigned int start, const unsigned int length) { @@ -168,19 +168,19 @@ make_slice (VECTOR& v, template inline -VectorSlice::VectorSlice(VECTOR& v) - : - v(v), start(0), length(v.size()) +VectorSlice::VectorSlice(VECTOR &v) + : + v(v), start(0), length(v.size()) {} template inline -VectorSlice::VectorSlice(VECTOR& v, - unsigned int start, - unsigned int length) - : - v(v), start(start), length(length) +VectorSlice::VectorSlice(VECTOR &v, + unsigned int start, + unsigned int length) + : + v(v), start(start), length(length) { Assert((start+length<=v.size()), ExcIndexRange(length, 0, v.size()-start+1)); diff --git a/deal.II/include/deal.II/base/vectorization.h b/deal.II/include/deal.II/base/vectorization.h index 5308189f54..09441fe875 100644 --- a/deal.II/include/deal.II/base/vectorization.h +++ b/deal.II/include/deal.II/base/vectorization.h @@ -25,8 +25,8 @@ // set flag DEAL_II_COMPILER_VECTORIZATION_LEVEL depending on whether we are // in debug or optimized mode -// Note: -// The flag DEAL_II_COMPILER_VECTORIZATION_LEVEL is essentially constructed +// Note: +// The flag DEAL_II_COMPILER_VECTORIZATION_LEVEL is essentially constructed // according to the following scheme // #ifdef __AVX__ // #define DEAL_II_COMPILER_VECTORIZATION_LEVEL 2 @@ -39,11 +39,11 @@ // ensures that these feature are not only present but also working properly. #if DEAL_II_COMPILER_VECTORIZATION_LEVEL == 2 // AVX - #include - #include +#include +#include #elif DEAL_II_COMPILER_VECTORIZATION_LEVEL == 1 // SSE2 - #include - #include +#include +#include #endif @@ -51,18 +51,18 @@ // forward declarations namespace dealii { -template class VectorizedArray; + template class VectorizedArray; } namespace std { template ::dealii::VectorizedArray - sqrt(const ::dealii::VectorizedArray&); + sqrt(const ::dealii::VectorizedArray &); template ::dealii::VectorizedArray - abs(const ::dealii::VectorizedArray&); + abs(const ::dealii::VectorizedArray &); template ::dealii::VectorizedArray - max(const ::dealii::VectorizedArray&, const ::dealii::VectorizedArray&); + max(const ::dealii::VectorizedArray &, const ::dealii::VectorizedArray &); template ::dealii::VectorizedArray - min (const ::dealii::VectorizedArray&, const ::dealii::VectorizedArray&); + min (const ::dealii::VectorizedArray &, const ::dealii::VectorizedArray &); } @@ -70,65 +70,65 @@ DEAL_II_NAMESPACE_OPEN // for safety, also check that __AVX__ is defined in case the user manually -// set some conflicting compile flags which prevent compilation +// set some conflicting compile flags which prevent compilation #if DEAL_II_COMPILER_VECTORIZATION_LEVEL == 2 && defined(__AVX__) - /** - * Specialization of VectorizedArray class for double and AVX. - */ +/** + * Specialization of VectorizedArray class for double and AVX. + */ template <> class VectorizedArray { public: - /** - * This gives the number of vectors collected - * in this class. - */ + /** + * This gives the number of vectors collected + * in this class. + */ static const unsigned int n_array_elements = 4; - /** - * This function can be used to set all data - * fields to a given scalar. - */ - VectorizedArray& + /** + * This function can be used to set all data + * fields to a given scalar. + */ + VectorizedArray & operator = (const double x) { data = _mm256_set1_pd(x); return *this; } - /** - * Access operator. - */ + /** + * Access operator. + */ double & operator [] (const unsigned int comp) { AssertIndexRange (comp, 4); - return *(reinterpret_cast(&data)+comp); + return *(reinterpret_cast(&data)+comp); } - /** - * Constant access operator. - */ + /** + * Constant access operator. + */ const double & operator [] (const unsigned int comp) const { AssertIndexRange (comp, 4); - return *(reinterpret_cast(&data)+comp); + return *(reinterpret_cast(&data)+comp); } - /** - * Addition. - */ + /** + * Addition. + */ VectorizedArray & operator += (const VectorizedArray &vec) { - // if the compiler supports vector - // arithmetics, we can simply use += operator - // on the given data type. Otherwise, we need - // to use the built-in intrinsic command for - // __m256d + // if the compiler supports vector + // arithmetics, we can simply use += operator + // on the given data type. Otherwise, we need + // to use the built-in intrinsic command for + // __m256d #ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS data += vec.data; #else @@ -137,9 +137,9 @@ public: return *this; } - /** - * Subtraction. - */ + /** + * Subtraction. + */ VectorizedArray & operator -= (const VectorizedArray &vec) { @@ -150,9 +150,9 @@ public: #endif return *this; } - /** - * Multiplication. - */ + /** + * Multiplication. + */ VectorizedArray & operator *= (const VectorizedArray &vec) { @@ -164,9 +164,9 @@ public: return *this; } - /** - * Division. - */ + /** + * Division. + */ VectorizedArray & operator /= (const VectorizedArray &vec) { @@ -178,18 +178,18 @@ public: return *this; } - /** - * Actual data field. Since this class - * represents a POD data type, it remains - * public. - */ + /** + * Actual data field. Since this class + * represents a POD data type, it remains + * public. + */ __m256d data; private: - /** - * Returns the square root of this field. Not - * for use in user code. Use sqrt(x) instead. - */ + /** + * Returns the square root of this field. Not + * for use in user code. Use sqrt(x) instead. + */ VectorizedArray get_sqrt () const { @@ -198,29 +198,29 @@ private: return res; } - /** - * Returns the absolute value of this - * field. Not for use in user code. Use - * abs(x) instead. - */ + /** + * Returns the absolute value of this + * field. Not for use in user code. Use + * abs(x) instead. + */ VectorizedArray get_abs () const { - // to compute the absolute value, perform - // bitwise andnot with -0. This will leave all - // value and exponent bits unchanged but force - // the sign value to +. + // to compute the absolute value, perform + // bitwise andnot with -0. This will leave all + // value and exponent bits unchanged but force + // the sign value to +. __m256d mask = _mm256_set1_pd (-0.); VectorizedArray res; res.data = _mm256_andnot_pd(mask, data); return res; } - /** - * Returns the component-wise maximum of this - * field and another one. Not for use in user - * code. Use max(x,y) instead. - */ + /** + * Returns the component-wise maximum of this + * field and another one. Not for use in user + * code. Use max(x,y) instead. + */ VectorizedArray get_max (const VectorizedArray &other) const { @@ -229,11 +229,11 @@ private: return res; } - /** - * Returns the component-wise minimum of this - * field and another one. Not for use in user - * code. Use min(x,y) instead. - */ + /** + * Returns the component-wise minimum of this + * field and another one. Not for use in user + * code. Use min(x,y) instead. + */ VectorizedArray get_min (const VectorizedArray &other) const { @@ -242,68 +242,68 @@ private: return res; } - /** - * Make a few functions friends. - */ + /** + * Make a few functions friends. + */ template friend VectorizedArray - std::sqrt (const VectorizedArray&); + std::sqrt (const VectorizedArray &); template friend VectorizedArray - std::abs (const VectorizedArray&); + std::abs (const VectorizedArray &); template friend VectorizedArray - std::max (const VectorizedArray&, const VectorizedArray&); + std::max (const VectorizedArray &, const VectorizedArray &); template friend VectorizedArray - std::min (const VectorizedArray&, const VectorizedArray&); + std::min (const VectorizedArray &, const VectorizedArray &); }; - /** - * Specialization for float and AVX. - */ +/** + * Specialization for float and AVX. + */ template<> class VectorizedArray { public: - /** - * This gives the number of vectors collected - * in this class. - */ + /** + * This gives the number of vectors collected + * in this class. + */ static const unsigned int n_array_elements = 8; - /** - * This function can be used to set all data - * fields to a given scalar. - */ - VectorizedArray& + /** + * This function can be used to set all data + * fields to a given scalar. + */ + VectorizedArray & operator = (const float x) { data = _mm256_set1_ps(x); return *this; } - /** - * Access operator. - */ + /** + * Access operator. + */ float & operator [] (const unsigned int comp) { AssertIndexRange (comp, 8); - return *(reinterpret_cast(&data)+comp); + return *(reinterpret_cast(&data)+comp); } - /** - * Constant access operator. - */ + /** + * Constant access operator. + */ const float & operator [] (const unsigned int comp) const { AssertIndexRange (comp, 8); - return *(reinterpret_cast(&data)+comp); + return *(reinterpret_cast(&data)+comp); } - /** - * Addition. - */ + /** + * Addition. + */ VectorizedArray & operator += (const VectorizedArray &vec) { @@ -315,9 +315,9 @@ public: return *this; } - /** - * Subtraction. - */ + /** + * Subtraction. + */ VectorizedArray & operator -= (const VectorizedArray &vec) { @@ -328,9 +328,9 @@ public: #endif return *this; } - /** - * Multiplication. - */ + /** + * Multiplication. + */ VectorizedArray & operator *= (const VectorizedArray &vec) { @@ -342,9 +342,9 @@ public: return *this; } - /** - * Division. - */ + /** + * Division. + */ VectorizedArray & operator /= (const VectorizedArray &vec) { @@ -356,19 +356,19 @@ public: return *this; } - /** - * Actual data field. Since this class - * represents a POD data type, it remains - * public. - */ + /** + * Actual data field. Since this class + * represents a POD data type, it remains + * public. + */ __m256 data; private: - /** - * Returns the square root of this field. Not - * for use in user code. Use sqrt(x) instead. - */ + /** + * Returns the square root of this field. Not + * for use in user code. Use sqrt(x) instead. + */ VectorizedArray get_sqrt () const { @@ -376,29 +376,29 @@ private: res.data = _mm256_sqrt_ps(data); return res; } - /** - * Returns the absolute value of this - * field. Not for use in user code. Use - * abs(x) instead. - */ + /** + * Returns the absolute value of this + * field. Not for use in user code. Use + * abs(x) instead. + */ VectorizedArray get_abs () const { - // to compute the absolute value, perform - // bitwise andnot with -0. This will leave all - // value and exponent bits unchanged but force - // the sign value to +. + // to compute the absolute value, perform + // bitwise andnot with -0. This will leave all + // value and exponent bits unchanged but force + // the sign value to +. __m256 mask = _mm256_set1_ps (-0.f); VectorizedArray res; res.data = _mm256_andnot_ps(mask, data); return res; } - /** - * Returns the component-wise maximum of this - * field and another one. Not for use in user - * code. Use max(x,y) instead. - */ + /** + * Returns the component-wise maximum of this + * field and another one. Not for use in user + * code. Use max(x,y) instead. + */ VectorizedArray get_max (const VectorizedArray &other) const { @@ -407,11 +407,11 @@ private: return res; } - /** - * Returns the component-wise minimum of this - * field and another one. Not for use in user - * code. Use min(x,y) instead. - */ + /** + * Returns the component-wise minimum of this + * field and another one. Not for use in user + * code. Use min(x,y) instead. + */ VectorizedArray get_min (const VectorizedArray &other) const { @@ -420,76 +420,76 @@ private: return res; } - /** - * Make a few functions friends. - */ + /** + * Make a few functions friends. + */ template friend VectorizedArray std::sqrt (const VectorizedArray &); template friend VectorizedArray std::abs (const VectorizedArray &); template friend VectorizedArray - std::max (const VectorizedArray&, const VectorizedArray&); + std::max (const VectorizedArray &, const VectorizedArray &); template friend VectorizedArray - std::min (const VectorizedArray&, const VectorizedArray&); + std::min (const VectorizedArray &, const VectorizedArray &); }; // for safety, also check that __SSE2__ is defined in case the user manually -// set some conflicting compile flags which prevent compilation +// set some conflicting compile flags which prevent compilation #elif DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__) - /** - * Specialization for double and SSE2. - */ +/** + * Specialization for double and SSE2. + */ template <> class VectorizedArray { public: - /** - * This gives the number of vectors collected - * in this class. - */ + /** + * This gives the number of vectors collected + * in this class. + */ static const unsigned int n_array_elements = 2; - /** - * This function can be used to set all data - * fields to a given scalar. - */ + /** + * This function can be used to set all data + * fields to a given scalar. + */ - VectorizedArray& + VectorizedArray & operator = (const double x) { data = _mm_set1_pd(x); return *this; } - /** - * Access operator. - */ + /** + * Access operator. + */ double & operator [] (const unsigned int comp) { AssertIndexRange (comp, 2); - return *(reinterpret_cast(&data)+comp); + return *(reinterpret_cast(&data)+comp); } - /** - * Constant access operator. - */ + /** + * Constant access operator. + */ const double & operator [] (const unsigned int comp) const { AssertIndexRange (comp, 2); - return *(reinterpret_cast(&data)+comp); + return *(reinterpret_cast(&data)+comp); } - /** - * Addition. - */ + /** + * Addition. + */ VectorizedArray & operator += (const VectorizedArray &vec) { @@ -501,9 +501,9 @@ public: return *this; } - /** - * Subtraction. - */ + /** + * Subtraction. + */ VectorizedArray & operator -= (const VectorizedArray &vec) { @@ -514,9 +514,9 @@ public: #endif return *this; } - /** - * Multiplication. - */ + /** + * Multiplication. + */ VectorizedArray & operator *= (const VectorizedArray &vec) { @@ -528,9 +528,9 @@ public: return *this; } - /** - * Division. - */ + /** + * Division. + */ VectorizedArray & operator /= (const VectorizedArray &vec) { @@ -542,18 +542,18 @@ public: return *this; } - /** - * Actual data field. Since this class - * represents a POD data type, it remains - * public. - */ + /** + * Actual data field. Since this class + * represents a POD data type, it remains + * public. + */ __m128d data; private: - /** - * Returns the square root of this field. Not - * for use in user code. Use sqrt(x) instead. - */ + /** + * Returns the square root of this field. Not + * for use in user code. Use sqrt(x) instead. + */ VectorizedArray get_sqrt () const { @@ -562,29 +562,29 @@ private: return res; } - /** - * Returns the absolute value of this - * field. Not for use in user code. Use abs(x) - * instead. - */ + /** + * Returns the absolute value of this + * field. Not for use in user code. Use abs(x) + * instead. + */ VectorizedArray get_abs () const { - // to compute the absolute value, perform - // bitwise andnot with -0. This will leave all - // value and exponent bits unchanged but force - // the sign value to +. + // to compute the absolute value, perform + // bitwise andnot with -0. This will leave all + // value and exponent bits unchanged but force + // the sign value to +. __m128d mask = _mm_set1_pd (-0.); VectorizedArray res; res.data = _mm_andnot_pd(mask, data); return res; } - /** - * Returns the component-wise maximum of this - * field and another one. Not for use in user - * code. Use max(x,y) instead. - */ + /** + * Returns the component-wise maximum of this + * field and another one. Not for use in user + * code. Use max(x,y) instead. + */ VectorizedArray get_max (const VectorizedArray &other) const { @@ -593,11 +593,11 @@ private: return res; } - /** - * Returns the component-wise minimum of this - * field and another one. Not for use in user - * code. Use min(x,y) instead. - */ + /** + * Returns the component-wise minimum of this + * field and another one. Not for use in user + * code. Use min(x,y) instead. + */ VectorizedArray get_min (const VectorizedArray &other) const { @@ -606,69 +606,69 @@ private: return res; } - /** - * Make a few functions friends. - */ + /** + * Make a few functions friends. + */ template friend VectorizedArray std::sqrt (const VectorizedArray &); template friend VectorizedArray std::abs (const VectorizedArray &); template friend VectorizedArray - std::max (const VectorizedArray&, const VectorizedArray&); + std::max (const VectorizedArray &, const VectorizedArray &); template friend VectorizedArray - std::min (const VectorizedArray&, const VectorizedArray&); + std::min (const VectorizedArray &, const VectorizedArray &); }; - /** - * Specialization for float and SSE2. - */ +/** + * Specialization for float and SSE2. + */ template <> class VectorizedArray { public: - /** - * This gives the number of vectors collected - * in this class. - */ + /** + * This gives the number of vectors collected + * in this class. + */ static const unsigned int n_array_elements = 4; - /** - * This function can be used to set all data - * fields to a given scalar. - */ + /** + * This function can be used to set all data + * fields to a given scalar. + */ - VectorizedArray& + VectorizedArray & operator = (const float x) { data = _mm_set1_ps(x); return *this; } - /** - * Access operator. - */ + /** + * Access operator. + */ float & operator [] (const unsigned int comp) { AssertIndexRange (comp, 4); - return *(reinterpret_cast(&data)+comp); + return *(reinterpret_cast(&data)+comp); } - /** - * Constant access operator. - */ + /** + * Constant access operator. + */ const float & operator [] (const unsigned int comp) const { AssertIndexRange (comp, 4); - return *(reinterpret_cast(&data)+comp);; + return *(reinterpret_cast(&data)+comp);; } - /** - * Addition. - */ + /** + * Addition. + */ VectorizedArray & operator += (const VectorizedArray &vec) { @@ -680,9 +680,9 @@ public: return *this; } - /** - * Subtraction. - */ + /** + * Subtraction. + */ VectorizedArray & operator -= (const VectorizedArray &vec) { @@ -693,9 +693,9 @@ public: #endif return *this; } - /** - * Multiplication. - */ + /** + * Multiplication. + */ VectorizedArray & operator *= (const VectorizedArray &vec) { @@ -707,9 +707,9 @@ public: return *this; } - /** - * Division. - */ + /** + * Division. + */ VectorizedArray & operator /= (const VectorizedArray &vec) { @@ -721,18 +721,18 @@ public: return *this; } - /** - * Actual data field. Since this class - * represents a POD data type, it remains - * public. - */ + /** + * Actual data field. Since this class + * represents a POD data type, it remains + * public. + */ __m128 data; - + private: - /** - * Returns the square root of this field. Not - * for use in user code. Use sqrt(x) instead. - */ + /** + * Returns the square root of this field. Not + * for use in user code. Use sqrt(x) instead. + */ VectorizedArray get_sqrt () const { @@ -741,29 +741,29 @@ private: return res; } - /** - * Returns the absolute value of this - * field. Not for use in user code. Use - * abs(x) instead. - */ + /** + * Returns the absolute value of this + * field. Not for use in user code. Use + * abs(x) instead. + */ VectorizedArray get_abs () const { - // to compute the absolute value, perform - // bitwise andnot with -0. This will leave all - // value and exponent bits unchanged but force - // the sign value to +. + // to compute the absolute value, perform + // bitwise andnot with -0. This will leave all + // value and exponent bits unchanged but force + // the sign value to +. __m128 mask = _mm_set1_ps (-0.f); VectorizedArray res; res.data = _mm_andnot_ps(mask, data); return res; } - /** - * Returns the component-wise maximum of this - * field and another one. Not for use in user - * code. Use max(x,y) instead. - */ + /** + * Returns the component-wise maximum of this + * field and another one. Not for use in user + * code. Use max(x,y) instead. + */ VectorizedArray get_max (const VectorizedArray &other) const { @@ -772,11 +772,11 @@ private: return res; } - /** - * Returns the component-wise minimum of this - * field and another one. Not for use in user - * code. Use min(x,y) instead. - */ + /** + * Returns the component-wise minimum of this + * field and another one. Not for use in user + * code. Use min(x,y) instead. + */ VectorizedArray get_min (const VectorizedArray &other) const { @@ -785,103 +785,103 @@ private: return res; } - /** - * Make a few functions friends. - */ + /** + * Make a few functions friends. + */ template friend VectorizedArray - std::sqrt (const VectorizedArray&); + std::sqrt (const VectorizedArray &); template friend VectorizedArray - std::abs (const VectorizedArray&); + std::abs (const VectorizedArray &); template friend VectorizedArray - std::max (const VectorizedArray&, const VectorizedArray&); + std::max (const VectorizedArray &, const VectorizedArray &); template friend VectorizedArray - std::min (const VectorizedArray&, const VectorizedArray&); + std::min (const VectorizedArray &, const VectorizedArray &); }; #endif // if DEAL_II_COMPILER_VECTORIZATION_LEVEL > 0 - /** - * This generic class defines a unified interface to a vectorized data - * type. For general template arguments, this class simply corresponds to - * the template argument. For example, VectorizedArray is - * nothing else but a wrapper around long double with exactly one - * data field of type long double and overloaded arithmetic - * operations. This means that VectorizedArray has - * a similar layout as ComplicatedType, provided that ComplicatedType - * defines basic arithmetic operations. For floats and doubles, an array of - * numbers are packed together, though. The number of elements packed - * together depend on the computer system and compiler flags that are used - * for compilation of deal.II. The fundamental idea of these packed data - * types is to use one single CPU instruction to perform arithmetic - * operations on the whole array using the processor's vector units. Most - * computer systems by 2010 standards will use an array of two doubles and - * four floats, respectively (this corresponds to the SSE/SSE2 data sets) - * when compiling deal.II on 64-bit operating systems. On Intel Sandy Bridge - * processors and newer or AMD Bulldozer processors and newer, four doubles - * and eight floats are used when deal.II is configured e.g. using gcc with - * --with-cpu=native or --with-cpu=corei7-avx. - * - * This behavior of this class is made similar to the basic data types - * double and float. The definition of a vectorized array does not - * initialize the data field but rather leaves it undefined, as is the case - * for double and float. However, when calling something like - * VectorType a = VectorType(), it sets all numbers in this - * field to zero. In other words, this class is a plain old data (POD) type - * which has an equivalent C representation and can e.g. be safely copied - * with std::memcpy. This POD layout is also necessary for ensuring correct - * alignment of data with address boundaries when collected in a vector - * (i.e., when the first element in a vector is properly aligned, all - * subsequent elements will be correctly aligned, too). - * - * Note that for proper functioning of this class, certain data alignment - * rules must be respected. This is because the computer expects the - * starting address of a VectorizedArray field at specific addresses - * in memory (usually, the address of the vectorized array should be a - * multiple of the length of the array in bytes). Otherwise, a segmentation - * fault or a severe loss of performance might occur. When creating a single - * data field on the stack like VectorizedArray a = - * VectorizedArray(), the compiler will take care of data - * alignment automatically. However, when allocating a long vector of - * VectorizedArray data, one needs to respect these rules. Use the - * class AlignedVector for this purpose. It is a class very similar to - * std::vector otherwise but always makes sure that data is correctly - * aligned. - * - * @author Katharina Kormann, Martin Kronbichler, 2010, 2011 - */ +/** + * This generic class defines a unified interface to a vectorized data + * type. For general template arguments, this class simply corresponds to + * the template argument. For example, VectorizedArray is + * nothing else but a wrapper around long double with exactly one + * data field of type long double and overloaded arithmetic + * operations. This means that VectorizedArray has + * a similar layout as ComplicatedType, provided that ComplicatedType + * defines basic arithmetic operations. For floats and doubles, an array of + * numbers are packed together, though. The number of elements packed + * together depend on the computer system and compiler flags that are used + * for compilation of deal.II. The fundamental idea of these packed data + * types is to use one single CPU instruction to perform arithmetic + * operations on the whole array using the processor's vector units. Most + * computer systems by 2010 standards will use an array of two doubles and + * four floats, respectively (this corresponds to the SSE/SSE2 data sets) + * when compiling deal.II on 64-bit operating systems. On Intel Sandy Bridge + * processors and newer or AMD Bulldozer processors and newer, four doubles + * and eight floats are used when deal.II is configured e.g. using gcc with + * --with-cpu=native or --with-cpu=corei7-avx. + * + * This behavior of this class is made similar to the basic data types + * double and float. The definition of a vectorized array does not + * initialize the data field but rather leaves it undefined, as is the case + * for double and float. However, when calling something like + * VectorType a = VectorType(), it sets all numbers in this + * field to zero. In other words, this class is a plain old data (POD) type + * which has an equivalent C representation and can e.g. be safely copied + * with std::memcpy. This POD layout is also necessary for ensuring correct + * alignment of data with address boundaries when collected in a vector + * (i.e., when the first element in a vector is properly aligned, all + * subsequent elements will be correctly aligned, too). + * + * Note that for proper functioning of this class, certain data alignment + * rules must be respected. This is because the computer expects the + * starting address of a VectorizedArray field at specific addresses + * in memory (usually, the address of the vectorized array should be a + * multiple of the length of the array in bytes). Otherwise, a segmentation + * fault or a severe loss of performance might occur. When creating a single + * data field on the stack like VectorizedArray a = + * VectorizedArray(), the compiler will take care of data + * alignment automatically. However, when allocating a long vector of + * VectorizedArray data, one needs to respect these rules. Use the + * class AlignedVector for this purpose. It is a class very similar to + * std::vector otherwise but always makes sure that data is correctly + * aligned. + * + * @author Katharina Kormann, Martin Kronbichler, 2010, 2011 + */ template class VectorizedArray { public: - /** - * This gives the number of vectors collected - * in this class. - */ + /** + * This gives the number of vectors collected + * in this class. + */ static const unsigned int n_array_elements = 1; - // POD means that there should be no - // user-defined constructors, destructors and - // copy functions (the standard is somewhat - // relaxed in C++2011, though). + // POD means that there should be no + // user-defined constructors, destructors and + // copy functions (the standard is somewhat + // relaxed in C++2011, though). - /** - * This function assigns a scalar to this - * class. - */ + /** + * This function assigns a scalar to this + * class. + */ - VectorizedArray& + VectorizedArray & operator = (const Number scalar) { data = scalar; return *this; } - /** - * Access operator (only valid with component - * 0) - */ + /** + * Access operator (only valid with component + * 0) + */ Number & operator [] (const unsigned int comp) { @@ -889,10 +889,10 @@ public: return data; } - /** - * Constant access operator (only valid with - * component 0) - */ + /** + * Constant access operator (only valid with + * component 0) + */ const Number & operator [] (const unsigned int comp) const { @@ -900,9 +900,9 @@ public: return data; } - /** - * Addition - */ + /** + * Addition + */ VectorizedArray & operator += (const VectorizedArray &vec) { @@ -910,9 +910,9 @@ public: return *this; } - /** - * Subtraction - */ + /** + * Subtraction + */ VectorizedArray & operator -= (const VectorizedArray &vec) { @@ -920,9 +920,9 @@ public: return *this; } - /** - * Multiplication - */ + /** + * Multiplication + */ VectorizedArray & operator *= (const VectorizedArray &vec) { @@ -930,9 +930,9 @@ public: return *this; } - /** - * Division - */ + /** + * Division + */ VectorizedArray & operator /= (const VectorizedArray &vec) { @@ -940,18 +940,18 @@ public: return *this; } - /** - * Actual data field. Since this class - * represents a POD data type, it is declared - * public. - */ + /** + * Actual data field. Since this class + * represents a POD data type, it is declared + * public. + */ Number data; private: - /** - * Returns the square root of this field. Not - * for use in user code. Use sqrt(x) instead. - */ + /** + * Returns the square root of this field. Not + * for use in user code. Use sqrt(x) instead. + */ VectorizedArray get_sqrt () const { @@ -960,11 +960,11 @@ private: return res; } - /** - * Returns the absolute value of this - * field. Not for use in user code. Use - * abs(x) instead. - */ + /** + * Returns the absolute value of this + * field. Not for use in user code. Use + * abs(x) instead. + */ VectorizedArray get_abs () const { @@ -973,11 +973,11 @@ private: return res; } - /** - * Returns the component-wise maximum of this - * field and another one. Not for use in user - * code. Use max(x,y) instead. - */ + /** + * Returns the component-wise maximum of this + * field and another one. Not for use in user + * code. Use max(x,y) instead. + */ VectorizedArray get_max (const VectorizedArray &other) const { @@ -986,11 +986,11 @@ private: return res; } - /** - * Returns the component-wise minimum of this - * field and another one. Not for use in user - * code. Use min(x,y) instead. - */ + /** + * Returns the component-wise minimum of this + * field and another one. Not for use in user + * code. Use min(x,y) instead. + */ VectorizedArray get_min (const VectorizedArray &other) const { @@ -999,17 +999,17 @@ private: return res; } - /** - * Make a few functions friends. - */ + /** + * Make a few functions friends. + */ template friend VectorizedArray - std::sqrt (const VectorizedArray&); + std::sqrt (const VectorizedArray &); template friend VectorizedArray - std::abs (const VectorizedArray&); + std::abs (const VectorizedArray &); template friend VectorizedArray - std::max (const VectorizedArray&, const VectorizedArray&); + std::max (const VectorizedArray &, const VectorizedArray &); template friend VectorizedArray - std::min (const VectorizedArray&, const VectorizedArray&); + std::min (const VectorizedArray &, const VectorizedArray &); }; /** @@ -1417,18 +1417,18 @@ namespace internal { static const std::size_t minimum_parallel_grain_size = 160000/sizeof(T)+1; public: - /** - * Constructor. Issues a parallel call if - * there are sufficiently many elements, - * otherwise work in serial. Copies the data - * from source to destination and then calls - * destructor on the source. If the optional - * argument is set to true, the source is left - * untouched instead. - */ - AlignedVectorMove (T* source_begin, - T* source_end, - T* destination, + /** + * Constructor. Issues a parallel call if + * there are sufficiently many elements, + * otherwise work in serial. Copies the data + * from source to destination and then calls + * destructor on the source. If the optional + * argument is set to true, the source is left + * untouched instead. + */ + AlignedVectorMove (T *source_begin, + T *source_end, + T *destination, bool copy_only = false) : source_ (source_begin), @@ -1443,38 +1443,37 @@ namespace internal apply_parallel (0, size, minimum_parallel_grain_size); } - /** - * This method moves elements from the source - * to the destination given in the constructor - * on a subrange given by two integers. - */ + /** + * This method moves elements from the source + * to the destination given in the constructor + * on a subrange given by two integers. + */ virtual void apply_to_subrange (const std::size_t begin, const std::size_t end) const { - // for classes trivial assignment can use - // memcpy + // for classes trivial assignment can use + // memcpy if (std_cxx1x::is_trivial::value == true) std::memcpy (destination_+begin, source_+begin, (end-begin)*sizeof(T)); + else if (copy_only_ == false) + for (std::size_t i=begin; i::value == true && trivial_element) std::memset (destination_+begin, 0, (end-begin)*sizeof(T)); else for (std::size_t i=begin; i class AlignedVector { - public: - /** - * Declare standard types used in all - * containers. These types parallel those - * in the C++ standard libraries - * vector<...> class. - */ - typedef T value_type; - typedef value_type *pointer; - typedef const value_type *const_pointer; - typedef value_type *iterator; - typedef const value_type *const_iterator; - typedef value_type &reference; - typedef const value_type &const_reference; - typedef std::size_t size_type; - - /** - * Empty constructor. Sets the vector size to - * zero. - */ - AlignedVector () - : - _data (0), - _end_data (0), - _end_allocated (0) - {}; - - /** - * Sets the vector size to the given size and - * initializes all elements with T(). - */ - AlignedVector (const size_type size) - : - _data (0), - _end_data (0), - _end_allocated (0) - { - if (size > 0) - resize (size); - } +public: + /** + * Declare standard types used in all + * containers. These types parallel those + * in the C++ standard libraries + * vector<...> class. + */ + typedef T value_type; + typedef value_type *pointer; + typedef const value_type *const_pointer; + typedef value_type *iterator; + typedef const value_type *const_iterator; + typedef value_type &reference; + typedef const value_type &const_reference; + typedef std::size_t size_type; - /** - * Destructor. - */ - ~AlignedVector () - { - clear(); - } + /** + * Empty constructor. Sets the vector size to + * zero. + */ + AlignedVector () + : + _data (0), + _end_data (0), + _end_allocated (0) + {}; - /** - * Copy constructor. - */ - AlignedVector (const AlignedVector &vec) - : - _data (0), - _end_data (0), - _end_allocated (0) - { - // do not invalidate old data - resize_fast (vec._end_data - vec._data); - internal::AlignedVectorMove (vec._data, vec._end_data, _data, true); - } + /** + * Sets the vector size to the given size and + * initializes all elements with T(). + */ + AlignedVector (const size_type size) + : + _data (0), + _end_data (0), + _end_allocated (0) + { + if (size > 0) + resize (size); + } - /** - * Assignment to the input vector @p vec. - */ - AlignedVector & - operator = (const AlignedVector &vec) - { - clear(); - resize_fast (vec._end_data - vec._data); - internal::AlignedVectorMove (vec._data, vec._end_data, _data, true); - return *this; - } + /** + * Destructor. + */ + ~AlignedVector () + { + clear(); + } - /** - * Change the size of the vector. It keeps old - * elements previously available but does not - * initialize the newly allocated memory, - * leaving it in an undefined state. - */ - void resize_fast (const size_type size) - { - reserve (size); - _end_data = _data + size; - } + /** + * Copy constructor. + */ + AlignedVector (const AlignedVector &vec) + : + _data (0), + _end_data (0), + _end_allocated (0) + { + // do not invalidate old data + resize_fast (vec._end_data - vec._data); + internal::AlignedVectorMove (vec._data, vec._end_data, _data, true); + } - /** - * Change the size of the vector. It keeps old - * elements previously available, and - * initializes each element with the specified - * data. If the new vector size is shorter - * than the old one, the memory is not - * released unless the new size is zero. - */ - void resize (const size_type size_in, - const T &init = T()) - { - const size_type old_size = size(); - if (std_cxx1x::is_trivial::value == false && size_in < old_size) - { - // call destructor on fields that are released - while (_end_data != _data+size_in) - (--_end_data)->~T(); - } + /** + * Assignment to the input vector @p vec. + */ + AlignedVector & + operator = (const AlignedVector &vec) + { + clear(); + resize_fast (vec._end_data - vec._data); + internal::AlignedVectorMove (vec._data, vec._end_data, _data, true); + return *this; + } - resize_fast (size_in); - // now _size is set correctly, need to set the - // values - if (size_in > old_size) - internal::AlignedVectorSet (size_in-old_size, init, - _data+old_size); - } + /** + * Change the size of the vector. It keeps old + * elements previously available but does not + * initialize the newly allocated memory, + * leaving it in an undefined state. + */ + void resize_fast (const size_type size) + { + reserve (size); + _end_data = _data + size; + } - /** - * Reserve memory space for @p size - * elements. If the argument @p size is set to - * zero, all previously allocated memory is - * released. - * - * In order to avoid too frequent reallocation - * (which involves copy of the data), this - * function doubles the amount of memory - * occupied when the given size is larger than - * the previously allocated size. - */ - void reserve (const size_type size_alloc) - { - const size_type old_size = _end_data - _data; - const size_type allocated_size = _end_allocated - _data; - if (size_alloc > allocated_size) - { - // if we continuously increase the size of the - // vector, we might be reallocating a lot of - // times. therefore, try to increase the size - // more aggressively - size_type new_size = size_alloc; - if (size_alloc < (2 * allocated_size)) - new_size = 2 * allocated_size; + /** + * Change the size of the vector. It keeps old + * elements previously available, and + * initializes each element with the specified + * data. If the new vector size is shorter + * than the old one, the memory is not + * released unless the new size is zero. + */ + void resize (const size_type size_in, + const T &init = T()) + { + const size_type old_size = size(); + if (std_cxx1x::is_trivial::value == false && size_in < old_size) + { + // call destructor on fields that are released + while (_end_data != _data+size_in) + (--_end_data)->~T(); + } - const size_type size_actual_allocate = new_size * sizeof(T); + resize_fast (size_in); + // now _size is set correctly, need to set the + // values + if (size_in > old_size) + internal::AlignedVectorSet (size_in-old_size, init, + _data+old_size); + } + + /** + * Reserve memory space for @p size + * elements. If the argument @p size is set to + * zero, all previously allocated memory is + * released. + * + * In order to avoid too frequent reallocation + * (which involves copy of the data), this + * function doubles the amount of memory + * occupied when the given size is larger than + * the previously allocated size. + */ + void reserve (const size_type size_alloc) + { + const size_type old_size = _end_data - _data; + const size_type allocated_size = _end_allocated - _data; + if (size_alloc > allocated_size) + { + // if we continuously increase the size of the + // vector, we might be reallocating a lot of + // times. therefore, try to increase the size + // more aggressively + size_type new_size = size_alloc; + if (size_alloc < (2 * allocated_size)) + new_size = 2 * allocated_size; + + const size_type size_actual_allocate = new_size * sizeof(T); #if DEAL_II_COMPILER_VECTORIZATION_LEVEL > 0 - // allocate and align along boundaries of the - // size of VectorizedArray, which is - // 16 bytes for SSE and 32 bytes for AVX - T* new_data = static_cast(_mm_malloc (size_actual_allocate, - sizeof(VectorizedArray))); + // allocate and align along boundaries of the + // size of VectorizedArray, which is + // 16 bytes for SSE and 32 bytes for AVX + T *new_data = static_cast(_mm_malloc (size_actual_allocate, + sizeof(VectorizedArray))); #else - T * new_data = static_cast(malloc (size_actual_allocate)); + T *new_data = static_cast(malloc (size_actual_allocate)); #endif - if (new_data == 0) - throw std::bad_alloc(); - - // copy data in case there was some content - // before and release the old memory with the - // function corresponding to the one used for - // allocating - std::swap (_data, new_data); - _end_data = _data + old_size; - _end_allocated = _data + new_size; - if (_end_data != _data) - { - internal::AlignedVectorMove(new_data, new_data + old_size, - _data); + if (new_data == 0) + throw std::bad_alloc(); + + // copy data in case there was some content + // before and release the old memory with the + // function corresponding to the one used for + // allocating + std::swap (_data, new_data); + _end_data = _data + old_size; + _end_allocated = _data + new_size; + if (_end_data != _data) + { + internal::AlignedVectorMove(new_data, new_data + old_size, + _data); #if DEAL_II_COMPILER_VECTORIZATION_LEVEL > 0 - _mm_free(new_data); + _mm_free(new_data); #else - free(new_data); + free(new_data); #endif - } - } - else if (size_alloc == 0) - clear(); - } + } + } + else if (size_alloc == 0) + clear(); + } - /** - * Releases all previously allocated memory - * and leaves the vector in a state equivalent - * to the state after the default constructor - * has been called. - */ - void clear () - { - if (_data != 0) - { - if (std_cxx1x::is_trivial::value == false) - while (_end_data != _data) - (--_end_data)->~T(); + /** + * Releases all previously allocated memory + * and leaves the vector in a state equivalent + * to the state after the default constructor + * has been called. + */ + void clear () + { + if (_data != 0) + { + if (std_cxx1x::is_trivial::value == false) + while (_end_data != _data) + (--_end_data)->~T(); #if DEAL_II_COMPILER_VECTORIZATION_LEVEL > 0 - _mm_free(_data); + _mm_free(_data); #else - free(_data); + free(_data); #endif - } - _data = 0; - _end_data = 0; - _end_allocated = 0; - }; - - /** - * Inserts an element at the end of the - * vector, increasing the vector size by - * one. Note that the allocated size will - * double whenever the previous space is not - * enough to hold the new element. - */ - void push_back (const T in_data) - { - Assert (_end_data <= _end_allocated, ExcInternalError()); - if (_end_data == _end_allocated) - reserve (std::max(2*capacity(),static_cast(16))); - if (std_cxx1x::is_trivial::value == false) - new (_end_data) T; - *_end_data++ = in_data; - } + } + _data = 0; + _end_data = 0; + _end_allocated = 0; + }; - /** - * Returns the last element of the vector - * (read and write access). - */ - reference back () - { - AssertIndexRange (0, size()); - T* field = _end_data - 1; - return *field; - } + /** + * Inserts an element at the end of the + * vector, increasing the vector size by + * one. Note that the allocated size will + * double whenever the previous space is not + * enough to hold the new element. + */ + void push_back (const T in_data) + { + Assert (_end_data <= _end_allocated, ExcInternalError()); + if (_end_data == _end_allocated) + reserve (std::max(2*capacity(),static_cast(16))); + if (std_cxx1x::is_trivial::value == false) + new (_end_data) T; + *_end_data++ = in_data; + } - /** - * Returns the last element of the vector - * (read-only access). - */ - const_reference back () const - { - AssertIndexRange (0, size()); - const T* field = _end_data - 1; - return *field; - } + /** + * Returns the last element of the vector + * (read and write access). + */ + reference back () + { + AssertIndexRange (0, size()); + T *field = _end_data - 1; + return *field; + } - /** - * Inserts several elements at the end of the - * vector given by a range of elements. - */ - template - void insert_back (ForwardIterator begin, - ForwardIterator end) - { - const unsigned int old_size = size(); - reserve (old_size + (end-begin)); - for ( ; begin != end; ++begin, ++_end_data) - { - if (std_cxx1x::is_trivial::value == false) - new (_end_data) T; - *_end_data = *begin; - } - } + /** + * Returns the last element of the vector + * (read-only access). + */ + const_reference back () const + { + AssertIndexRange (0, size()); + const T *field = _end_data - 1; + return *field; + } - /** - * Swaps the given vector with the calling - * vector. - */ - void swap (AlignedVector &vec) - { - std::swap (_data, vec._data); - std::swap (_end_data, vec._end_data); - std::swap (_end_allocated, vec._end_allocated); - } + /** + * Inserts several elements at the end of the + * vector given by a range of elements. + */ + template + void insert_back (ForwardIterator begin, + ForwardIterator end) + { + const unsigned int old_size = size(); + reserve (old_size + (end-begin)); + for ( ; begin != end; ++begin, ++_end_data) + { + if (std_cxx1x::is_trivial::value == false) + new (_end_data) T; + *_end_data = *begin; + } + } - /** - * Returns the size of the vector. - */ - size_type size () const - { - return _end_data - _data; - } + /** + * Swaps the given vector with the calling + * vector. + */ + void swap (AlignedVector &vec) + { + std::swap (_data, vec._data); + std::swap (_end_data, vec._end_data); + std::swap (_end_allocated, vec._end_allocated); + } - /** - * Returns the capacity of the vector, i.e., - * the size this vector can hold without - * reallocation. Note that capacity() >= - * size(). - */ - size_type capacity () const - { - return _end_allocated - _data; - } + /** + * Returns the size of the vector. + */ + size_type size () const + { + return _end_data - _data; + } - /** - * Read-write access to entry @p index in the - * vector. - */ - reference - operator [] (const size_type index) - { - AssertIndexRange (index, size()); - return _data[index]; - }; - - /** - * Read-only access to entry @p index in the - * vector. - */ - const_reference operator [] (const size_type index) const - { - AssertIndexRange (index, size()); - return _data[index]; - }; - - /** - * Returns a read and write pointer to the - * beginning of the data array. - */ - iterator begin () - { - return _data; - } + /** + * Returns the capacity of the vector, i.e., + * the size this vector can hold without + * reallocation. Note that capacity() >= + * size(). + */ + size_type capacity () const + { + return _end_allocated - _data; + } - /** - * Returns a read and write pointer to the - * end of the data array. - */ - iterator end () - { - return _end_data; - } + /** + * Read-write access to entry @p index in the + * vector. + */ + reference + operator [] (const size_type index) + { + AssertIndexRange (index, size()); + return _data[index]; + }; - /** - * Returns a read-only pointer to the - * beginning of the data array. - */ - const_iterator begin () const - { - return _data; - } + /** + * Read-only access to entry @p index in the + * vector. + */ + const_reference operator [] (const size_type index) const + { + AssertIndexRange (index, size()); + return _data[index]; + }; - /** - * Returns a read-only pointer to the - * end of the data array. - */ - const_iterator end () const - { - return _end_data; - } + /** + * Returns a read and write pointer to the + * beginning of the data array. + */ + iterator begin () + { + return _data; + } - /** - * Returns the memory consumption of the - * allocated memory in this class. If the - * underlying type @p T allocates memory by - * itself, this memory is not counted. - */ - size_type memory_consumption () const - { - size_type memory = sizeof(this); - memory += sizeof(T) * capacity(); - return memory; - } + /** + * Returns a read and write pointer to the + * end of the data array. + */ + iterator end () + { + return _end_data; + } - private: + /** + * Returns a read-only pointer to the + * beginning of the data array. + */ + const_iterator begin () const + { + return _data; + } + + /** + * Returns a read-only pointer to the + * end of the data array. + */ + const_iterator end () const + { + return _end_data; + } + + /** + * Returns the memory consumption of the + * allocated memory in this class. If the + * underlying type @p T allocates memory by + * itself, this memory is not counted. + */ + size_type memory_consumption () const + { + size_type memory = sizeof(this); + memory += sizeof(T) * capacity(); + return memory; + } - /** - * Pointer to actual class data. - */ - T* _data; +private: + + /** + * Pointer to actual class data. + */ + T *_data; - /** - * Pointer to the end of valid data fields. - */ - T* _end_data; + /** + * Pointer to the end of valid data fields. + */ + T *_end_data; - /** - * Pointer to the end of the allocated memory. - */ - T* _end_allocated; + /** + * Pointer to the end of the allocated memory. + */ + T *_end_allocated; }; @@ -1950,15 +1949,15 @@ DEAL_II_NAMESPACE_CLOSE */ namespace std { - /** - * Computes the sine of a vectorized - * data field. The result is return as - * vectorized array in the form - * {sin(x[0]), sin(x[1]), ..., - * sin(x[n_array_elements-1])}. - * - * @relates VectorizedArray - */ + /** + * Computes the sine of a vectorized + * data field. The result is return as + * vectorized array in the form + * {sin(x[0]), sin(x[1]), ..., + * sin(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ template inline ::dealii::VectorizedArray @@ -1972,15 +1971,15 @@ namespace std - /** - * Computes the tangent of a vectorized - * data field. The result is return as - * vectorized array in the form - * {tan(x[0]), tan(x[1]), ..., - * tan(x[n_array_elements-1])}. - * - * @relates VectorizedArray - */ + /** + * Computes the tangent of a vectorized + * data field. The result is return as + * vectorized array in the form + * {tan(x[0]), tan(x[1]), ..., + * tan(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ template inline ::dealii::VectorizedArray @@ -1993,15 +1992,15 @@ namespace std } - /** - * Computes the cosine of a vectorized - * data field. The result is return as - * vectorized array in the form - * {cos(x[0]), cos(x[1]), ..., - * cos(x[n_array_elements-1])}. - * - * @relates VectorizedArray - */ + /** + * Computes the cosine of a vectorized + * data field. The result is return as + * vectorized array in the form + * {cos(x[0]), cos(x[1]), ..., + * cos(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ template inline ::dealii::VectorizedArray @@ -2014,15 +2013,15 @@ namespace std } - /** - * Computes the exponential of a vectorized - * data field. The result is return as - * vectorized array in the form - * {exp(x[0]), exp(x[1]), ..., - * exp(x[n_array_elements-1])}. - * - * @relates VectorizedArray - */ + /** + * Computes the exponential of a vectorized + * data field. The result is return as + * vectorized array in the form + * {exp(x[0]), exp(x[1]), ..., + * exp(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ template inline ::dealii::VectorizedArray @@ -2035,15 +2034,15 @@ namespace std } - /** - * Computes the natural logarithm of a - * vectorized data field. The result is return - * as vectorized array in the form - * {log(x[0]), log(x[1]), ..., - * log(x[n_array_elements-1])}. - * - * @relates VectorizedArray - */ + /** + * Computes the natural logarithm of a + * vectorized data field. The result is return + * as vectorized array in the form + * {log(x[0]), log(x[1]), ..., + * log(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ template inline ::dealii::VectorizedArray @@ -2057,15 +2056,15 @@ namespace std - /** - * Computes the square root of a vectorized - * data field. The result is return as - * vectorized array in the form - * {sqrt(x[0]), sqrt(x[1]), ..., - * sqrt(x[n_array_elements-1])}. - * - * @relates VectorizedArray - */ + /** + * Computes the square root of a vectorized + * data field. The result is return as + * vectorized array in the form + * {sqrt(x[0]), sqrt(x[1]), ..., + * sqrt(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ template inline ::dealii::VectorizedArray @@ -2076,15 +2075,15 @@ namespace std - /** - * Computes the absolute value (modulus) of a - * vectorized data field. The result is return - * as vectorized array in the form - * {abs(x[0]), abs(x[1]), ..., - * abs(x[n_array_elements-1])}. - * - * @relates VectorizedArray - */ + /** + * Computes the absolute value (modulus) of a + * vectorized data field. The result is return + * as vectorized array in the form + * {abs(x[0]), abs(x[1]), ..., + * abs(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ template inline ::dealii::VectorizedArray @@ -2095,15 +2094,15 @@ namespace std - /** - * Computes the componentwise maximum of two - * vectorized data fields. The result is - * return as vectorized array in the form - * {max(x[0],y[0]), max(x[1],y[1]), - * ...}. - * - * @relates VectorizedArray - */ + /** + * Computes the componentwise maximum of two + * vectorized data fields. The result is + * return as vectorized array in the form + * {max(x[0],y[0]), max(x[1],y[1]), + * ...}. + * + * @relates VectorizedArray + */ template inline ::dealii::VectorizedArray @@ -2115,15 +2114,15 @@ namespace std - /** - * Computes the componentwise minimum of two - * vectorized data fields. The result is - * return as vectorized array in the form - * {min(x[0],y[0]), min(x[1],y[1]), - * ...}. - * - * @relates VectorizedArray - */ + /** + * Computes the componentwise minimum of two + * vectorized data fields. The result is + * return as vectorized array in the form + * {min(x[0],y[0]), min(x[1],y[1]), + * ...}. + * + * @relates VectorizedArray + */ template inline ::dealii::VectorizedArray diff --git a/deal.II/include/deal.II/base/work_stream.h b/deal.II/include/deal.II/base/work_stream.h index dd6fd8a93c..e81dc7c09b 100644 --- a/deal.II/include/deal.II/base/work_stream.h +++ b/deal.II/include/deal.II/base/work_stream.h @@ -130,310 +130,310 @@ namespace WorkStream namespace internal { - /** - * A class that creates a sequence of - * items from a range of iterators. - */ + /** + * A class that creates a sequence of + * items from a range of iterators. + */ template + typename ScratchData, + typename CopyData> class IteratorRangeToItemStream : public tbb::filter { - public: - /** - * A data type that we use to identify - * items to be worked on. - * - * The first element indicates an array - * of iterators to work on; the second - * the scratch space; the third an - * array of copy data spaces; and the - * last the number of elements to work - * on. This last argument is an integer - * between one and chunk_size. The - * arrays have a length of chunk_size. - */ - typedef - std_cxx1x::tuple, - ScratchData*, - std::vector, - unsigned int> - ItemType; + public: + /** + * A data type that we use to identify + * items to be worked on. + * + * The first element indicates an array + * of iterators to work on; the second + * the scratch space; the third an + * array of copy data spaces; and the + * last the number of elements to work + * on. This last argument is an integer + * between one and chunk_size. The + * arrays have a length of chunk_size. + */ + typedef + std_cxx1x::tuple, + ScratchData *, + std::vector, + unsigned int> + ItemType; + + + /** + * Constructor. Take an iterator + * range, the size of a buffer that + * can hold items, and the sample + * additional data object that will + * be passed to each worker and + * copier function invokation. + */ + IteratorRangeToItemStream (const Iterator &begin, + const Iterator &end, + const unsigned int buffer_size, + const unsigned int chunk_size, + const ScratchData &sample_scratch_data, + const CopyData &sample_copy_data) + : + tbb::filter (/*is_serial=*/true), + remaining_iterator_range (begin, end), + ring_buffer (buffer_size), + n_emitted_items (0), + chunk_size (chunk_size) + { + // initialize copies of + // additional_data. since + // this is frequently + // expensive (creating + // FEValues objects etc) do + // that in parallel + Threads::TaskGroup<> tasks; + for (unsigned int i=0; i(ring_buffer[i]); + } - /** - * Constructor. Take an iterator - * range, the size of a buffer that - * can hold items, and the sample - * additional data object that will - * be passed to each worker and - * copier function invokation. - */ - IteratorRangeToItemStream (const Iterator &begin, - const Iterator &end, - const unsigned int buffer_size, - const unsigned int chunk_size, - const ScratchData &sample_scratch_data, - const CopyData &sample_copy_data) - : - tbb::filter (/*is_serial=*/true), - remaining_iterator_range (begin, end), - ring_buffer (buffer_size), - n_emitted_items (0), - chunk_size (chunk_size) + /** + * Create a item and return a + * pointer to it. + */ + virtual void *operator () (void *) + { + // store the current + // position of the pointer + ItemType *current_item + = &ring_buffer[n_emitted_items % ring_buffer.size()]; + + // initialize the next item. it may + // consist of at most chunk_size + // elements + std_cxx1x::get<3>(*current_item) = 0; + while ((remaining_iterator_range.first != + remaining_iterator_range.second) + && + (std_cxx1x::get<3>(*current_item) < chunk_size)) { - // initialize copies of - // additional_data. since - // this is frequently - // expensive (creating - // FEValues objects etc) do - // that in parallel - Threads::TaskGroup<> tasks; - for (unsigned int i=0; i(*current_item)[std_cxx1x::get<3>(*current_item)] + = remaining_iterator_range.first; - /** - * Destructor. - */ - ~IteratorRangeToItemStream () - { - for (unsigned int i=0; i(ring_buffer[i]); + ++remaining_iterator_range.first; + ++std_cxx1x::get<3>(*current_item); } - /** - * Create a item and return a - * pointer to it. - */ - virtual void * operator () (void *) + if (std_cxx1x::get<3>(*current_item) == 0) + // there were no items + // left. terminate the pipeline + return 0; + else { - // store the current - // position of the pointer - ItemType *current_item - = &ring_buffer[n_emitted_items % ring_buffer.size()]; - - // initialize the next item. it may - // consist of at most chunk_size - // elements - std_cxx1x::get<3>(*current_item) = 0; - while ((remaining_iterator_range.first != - remaining_iterator_range.second) - && - (std_cxx1x::get<3>(*current_item) < chunk_size)) - { - std_cxx1x::get<0>(*current_item)[std_cxx1x::get<3>(*current_item)] - = remaining_iterator_range.first; - - ++remaining_iterator_range.first; - ++std_cxx1x::get<3>(*current_item); - } - - if (std_cxx1x::get<3>(*current_item) == 0) - // there were no items - // left. terminate the pipeline - return 0; - else - { - ++n_emitted_items; - return current_item; - } + ++n_emitted_items; + return current_item; } + } - private: - /** - * The interval of iterators still to - * be worked on. This range will shrink - * over time. - */ - std::pair remaining_iterator_range; - - /** - * A ring buffer that will store items. - */ - std::vector ring_buffer; - - /** - * Counter for the number of emitted - * items. Each item may consist of up - * to chunk_size iterator elements. - */ - unsigned int n_emitted_items; - - /** - * Number of elements of the - * iterator range that each - * thread should work on - * sequentially; a large number - * makes sure that each thread - * gets a significant amount of - * work before the next task - * switch happens, whereas a - * small number is better for - * load balancing. - */ - const unsigned int chunk_size; - - /** - * Initialize the pointers and vector - * elements in the specified entry of - * the ring buffer. - */ - void init_buffer_elements (const unsigned int element, - const ScratchData &sample_scratch_data, - const CopyData &sample_copy_data) - { - Assert (std_cxx1x::get<1>(ring_buffer[element]) == 0, - ExcInternalError()); - - std_cxx1x::get<0>(ring_buffer[element]) - .resize (chunk_size, remaining_iterator_range.second); - std_cxx1x::get<1>(ring_buffer[element]) - = new ScratchData(sample_scratch_data); - std_cxx1x::get<2>(ring_buffer[element]) - .resize (chunk_size, sample_copy_data); - } + private: + /** + * The interval of iterators still to + * be worked on. This range will shrink + * over time. + */ + std::pair remaining_iterator_range; + + /** + * A ring buffer that will store items. + */ + std::vector ring_buffer; + + /** + * Counter for the number of emitted + * items. Each item may consist of up + * to chunk_size iterator elements. + */ + unsigned int n_emitted_items; + + /** + * Number of elements of the + * iterator range that each + * thread should work on + * sequentially; a large number + * makes sure that each thread + * gets a significant amount of + * work before the next task + * switch happens, whereas a + * small number is better for + * load balancing. + */ + const unsigned int chunk_size; + + /** + * Initialize the pointers and vector + * elements in the specified entry of + * the ring buffer. + */ + void init_buffer_elements (const unsigned int element, + const ScratchData &sample_scratch_data, + const CopyData &sample_copy_data) + { + Assert (std_cxx1x::get<1>(ring_buffer[element]) == 0, + ExcInternalError()); + + std_cxx1x::get<0>(ring_buffer[element]) + .resize (chunk_size, remaining_iterator_range.second); + std_cxx1x::get<1>(ring_buffer[element]) + = new ScratchData(sample_scratch_data); + std_cxx1x::get<2>(ring_buffer[element]) + .resize (chunk_size, sample_copy_data); + } }; - /** - * A class that manages calling the - * worker function on a number of - * parallel threads. Note that it is, in - * the TBB notation, a filter that can - * run in parallel. - */ + /** + * A class that manages calling the + * worker function on a number of + * parallel threads. Note that it is, in + * the TBB notation, a filter that can + * run in parallel. + */ template + typename ScratchData, + typename CopyData> class Worker : public tbb::filter { - public: - /** - * Constructor. Takes a - * reference to the object on - * which we will operate as - * well as a pointer to the - * function that will do the - * assembly. - */ - Worker (const std_cxx1x::function &worker) + public: + /** + * Constructor. Takes a + * reference to the object on + * which we will operate as + * well as a pointer to the + * function that will do the + * assembly. + */ + Worker (const std_cxx1x::function &worker) : tbb::filter (/* is_serial= */ false), worker (worker) - {} + {} - /** - * Work on an item. - */ - void * operator () (void *item) - { - // first unpack the current item - typedef - typename IteratorRangeToItemStream::ItemType - ItemType; - - ItemType *current_item = reinterpret_cast (item); - - // then call the worker function on - // each element of the chunk we - // were given - for (unsigned int i=0; i(*current_item); ++i) - worker (std_cxx1x::get<0>(*current_item)[i], - *std_cxx1x::get<1>(*current_item), - std_cxx1x::get<2>(*current_item)[i]); - - // then return the original pointer - // to the now modified object - return item; - } + /** + * Work on an item. + */ + void *operator () (void *item) + { + // first unpack the current item + typedef + typename IteratorRangeToItemStream::ItemType + ItemType; + + ItemType *current_item = reinterpret_cast (item); + // then call the worker function on + // each element of the chunk we + // were given + for (unsigned int i=0; i(*current_item); ++i) + worker (std_cxx1x::get<0>(*current_item)[i], + *std_cxx1x::get<1>(*current_item), + std_cxx1x::get<2>(*current_item)[i]); + + // then return the original pointer + // to the now modified object + return item; + } - private: - /** - * Pointer to the function - * that does the assembling - * on the sequence of cells. - */ - const std_cxx1x::function worker; + + private: + /** + * Pointer to the function + * that does the assembling + * on the sequence of cells. + */ + const std_cxx1x::function worker; }; - /** - * A class that manages calling the - * copier function. Note that it is, in - * the TBB notation, a filter that runs - * sequentially, ensuring that all items - * are copied in the same order in which - * they are created. - */ + /** + * A class that manages calling the + * copier function. Note that it is, in + * the TBB notation, a filter that runs + * sequentially, ensuring that all items + * are copied in the same order in which + * they are created. + */ template + typename ScratchData, + typename CopyData> class Copier : public tbb::filter { - public: - /** - * Constructor. Takes a - * reference to the object on - * which we will operate as - * well as a pointer to the - * function that will do the - * copying from the - * additional data object to - * the global matrix or - * similar. - */ - Copier (const std_cxx1x::function &copier) - : - tbb::filter (/* is_serial= */ true), - copier (copier) - {} - - - /** - * Work on a single item. - */ - void * operator () (void *item) - { - // first unpack the current item - typedef - typename IteratorRangeToItemStream::ItemType - ItemType; - - ItemType *current_item = reinterpret_cast (item); - - // initiate copying data - for (unsigned int i=0; i(*current_item); ++i) - copier (std_cxx1x::get<2>(*current_item)[i]); - - // return an invalid - // item since we are at - // the end of the - // pipeline - return 0; - } + public: + /** + * Constructor. Takes a + * reference to the object on + * which we will operate as + * well as a pointer to the + * function that will do the + * copying from the + * additional data object to + * the global matrix or + * similar. + */ + Copier (const std_cxx1x::function &copier) + : + tbb::filter (/* is_serial= */ true), + copier (copier) + {} + + + /** + * Work on a single item. + */ + void *operator () (void *item) + { + // first unpack the current item + typedef + typename IteratorRangeToItemStream::ItemType + ItemType; + + ItemType *current_item = reinterpret_cast (item); + + // initiate copying data + for (unsigned int i=0; i(*current_item); ++i) + copier (std_cxx1x::get<2>(*current_item)[i]); + + // return an invalid + // item since we are at + // the end of the + // pipeline + return 0; + } - private: - /** - * Pointer to the function - * that does the copying of - * data. - */ - const std_cxx1x::function copier; + private: + /** + * Pointer to the function + * that does the copying of + * data. + */ + const std_cxx1x::function copier; }; } @@ -443,65 +443,65 @@ namespace WorkStream - /** - * This is the main function of the - * WorkStream concept, doing work as - * described in the introduction to this - * namespace. - * - * This is the function that can be used - * for worker and copier objects that are - * either pointers to non-member - * functions or objects that allow to be - * called with an operator(), for example - * objects created by std::bind. - * - * The argument passed as @p end must be - * convertible to the same type as - * @p begin, but doesn't have to be of the - * same type itself. This allows to write - * code like - * WorkStream().run(dof_handler.begin_active(), - * dof_handler.end(), ... where - * the first is of type - * DoFHandler::active_cell_iterator - * whereas the second is of type - * DoFHandler::raw_cell_iterator. - * - * The two data types - * ScratchData and - * CopyData need to have a - * working copy - * constructor. ScratchData - * is only used in the - * worker function, while - * CopyData is the object - * passed from the worker - * to the copier. - * - * The @p queue_length argument indicates - * the number of items that can be live - * at any given time. Each item consists - * of @p chunk_size elements of the input - * stream that will be worked on by the - * worker and copier functions one after - * the other on the same thread. - * - * @note If your data objects are large, - * or their constructors are expensive, - * it is helpful to keep in mind - * that queue_length - * copies of the ScratchData - * object and - * queue_length*chunk_size - * copies of the CopyData - * object are generated. - */ + /** + * This is the main function of the + * WorkStream concept, doing work as + * described in the introduction to this + * namespace. + * + * This is the function that can be used + * for worker and copier objects that are + * either pointers to non-member + * functions or objects that allow to be + * called with an operator(), for example + * objects created by std::bind. + * + * The argument passed as @p end must be + * convertible to the same type as + * @p begin, but doesn't have to be of the + * same type itself. This allows to write + * code like + * WorkStream().run(dof_handler.begin_active(), + * dof_handler.end(), ... where + * the first is of type + * DoFHandler::active_cell_iterator + * whereas the second is of type + * DoFHandler::raw_cell_iterator. + * + * The two data types + * ScratchData and + * CopyData need to have a + * working copy + * constructor. ScratchData + * is only used in the + * worker function, while + * CopyData is the object + * passed from the worker + * to the copier. + * + * The @p queue_length argument indicates + * the number of items that can be live + * at any given time. Each item consists + * of @p chunk_size elements of the input + * stream that will be worked on by the + * worker and copier functions one after + * the other on the same thread. + * + * @note If your data objects are large, + * or their constructors are expensive, + * it is helpful to keep in mind + * that queue_length + * copies of the ScratchData + * object and + * queue_length*chunk_size + * copies of the CopyData + * object are generated. + */ template + typename Copier, + typename Iterator, + typename ScratchData, + typename CopyData> void run (const Iterator &begin, const typename identity::type &end, @@ -520,42 +520,42 @@ namespace WorkStream ExcMessage ("The chunk_size must be at least one.")); (void)chunk_size; // removes -Wunused-parameter warning in optimized mode - // if no work then skip. (only use - // operator!= for iterators since we may - // not have an equality comparison - // operator) + // if no work then skip. (only use + // operator!= for iterators since we may + // not have an equality comparison + // operator) if (!(begin != end)) return; #if DEAL_II_USE_MT == 1 - // create the three stages of the - // pipeline + // create the three stages of the + // pipeline internal::IteratorRangeToItemStream - iterator_range_to_item_stream (begin, end, - queue_length, - chunk_size, - sample_scratch_data, - sample_copy_data); + iterator_range_to_item_stream (begin, end, + queue_length, + chunk_size, + sample_scratch_data, + sample_copy_data); internal::Worker worker_filter (worker); internal::Copier copier_filter (copier); - // now create a pipeline from - // these stages + // now create a pipeline from + // these stages tbb::pipeline assembly_line; assembly_line.add_filter (iterator_range_to_item_stream); assembly_line.add_filter (worker_filter); assembly_line.add_filter (copier_filter); - // and run it + // and run it assembly_line.run (queue_length); assembly_line.clear (); #else - // need to copy the sample since it is - // marked const + // need to copy the sample since it is + // marked const ScratchData scratch_data = sample_scratch_data; CopyData copy_data = sample_copy_data; @@ -569,50 +569,50 @@ namespace WorkStream - /** - * This is the main function of the - * WorkStream concept, doing work as - * described in the introduction to this - * namespace. - * - * This is the function that can be - * used for worker and copier functions - * that are member functions of a class. - * - * The argument passed as @p end must be - * convertible to the same type as - * @p begin, but doesn't have to be of the - * same type itself. This allows to write - * code like - * WorkStream().run(dof_handler.begin_active(), - * dof_handler.end(), ... where - * the first is of type - * DoFHandler::active_cell_iterator - * whereas the second is of type - * DoFHandler::raw_cell_iterator. - * - * The @p queue_length argument indicates - * the number of items that can be live - * at any given time. Each item consists - * of @p chunk_size elements of the input - * stream that will be worked on by the - * worker and copier functions one after - * the other on the same thread. - * - * @note If your data objects are large, - * or their constructors are expensive, - * it is helpful to keep in mind - * that queue_length - * copies of the ScratchData - * object and - * queue_length*chunk_size - * copies of the CopyData - * object are generated. - */ + /** + * This is the main function of the + * WorkStream concept, doing work as + * described in the introduction to this + * namespace. + * + * This is the function that can be + * used for worker and copier functions + * that are member functions of a class. + * + * The argument passed as @p end must be + * convertible to the same type as + * @p begin, but doesn't have to be of the + * same type itself. This allows to write + * code like + * WorkStream().run(dof_handler.begin_active(), + * dof_handler.end(), ... where + * the first is of type + * DoFHandler::active_cell_iterator + * whereas the second is of type + * DoFHandler::raw_cell_iterator. + * + * The @p queue_length argument indicates + * the number of items that can be live + * at any given time. Each item consists + * of @p chunk_size elements of the input + * stream that will be worked on by the + * worker and copier functions one after + * the other on the same thread. + * + * @note If your data objects are large, + * or their constructors are expensive, + * it is helpful to keep in mind + * that queue_length + * copies of the ScratchData + * object and + * queue_length*chunk_size + * copies of the CopyData + * object are generated. + */ template + typename Iterator, + typename ScratchData, + typename CopyData> void run (const Iterator &begin, const typename identity::type &end, @@ -626,7 +626,7 @@ namespace WorkStream const unsigned int queue_length = 2*multithread_info.n_default_threads, const unsigned int chunk_size = 8) { - // forward to the other function + // forward to the other function run (begin, end, std_cxx1x::bind (worker, std_cxx1x::ref (main_object), diff --git a/deal.II/include/deal.II/distributed/grid_refinement.h b/deal.II/include/deal.II/distributed/grid_refinement.h index 2c65aae571..07d6c9aa2a 100644 --- a/deal.II/include/deal.II/distributed/grid_refinement.h +++ b/deal.II/include/deal.II/distributed/grid_refinement.h @@ -26,57 +26,57 @@ namespace parallel { namespace distributed { - // forward declarations + // forward declarations template class Triangulation; -/** - * Collection of functions controlling refinement and coarsening of - * parallel::distributed::Triangulation objects. This namespace - * provides similar functionality to the dealii::GridRefinement - * namespace, except that it works for meshes that are parallel and - * distributed. - * - * @ingroup grid - * @author Wolfgang Bangerth, 2009 - */ + /** + * Collection of functions controlling refinement and coarsening of + * parallel::distributed::Triangulation objects. This namespace + * provides similar functionality to the dealii::GridRefinement + * namespace, except that it works for meshes that are parallel and + * distributed. + * + * @ingroup grid + * @author Wolfgang Bangerth, 2009 + */ namespace GridRefinement { - /** - * Like - * dealii::GridRefinement::refine_and_coarsen_fixed_number, - * but for parallel distributed - * triangulation. - * - * The vector of criteria needs to be a - * vector of refinement criteria for - * all cells active on the current - * triangulation, - * i.e. tria.n_active_cells() - * (and not - * tria.n_locally_owned_active_cells()). However, - * the function will only look at the - * indicators that correspond to those - * cells that are actually locally - * owned, and ignore the indicators for - * all other cells. The function will - * then coordinate among all processors - * that store part of the triangulation - * so that at the end @p - * top_fraction_of_cells are refined, - * where the fraction is enforced as a - * fraction of - * Triangulation::n_global_active_cells, - * not - * Triangulation::n_locally_active_cells - * on each processor individually. In - * other words, it may be that on some - * processors, no cells are refined at - * all. - * - * The same is true for the fraction of - * cells that is coarsened. - */ + /** + * Like + * dealii::GridRefinement::refine_and_coarsen_fixed_number, + * but for parallel distributed + * triangulation. + * + * The vector of criteria needs to be a + * vector of refinement criteria for + * all cells active on the current + * triangulation, + * i.e. tria.n_active_cells() + * (and not + * tria.n_locally_owned_active_cells()). However, + * the function will only look at the + * indicators that correspond to those + * cells that are actually locally + * owned, and ignore the indicators for + * all other cells. The function will + * then coordinate among all processors + * that store part of the triangulation + * so that at the end @p + * top_fraction_of_cells are refined, + * where the fraction is enforced as a + * fraction of + * Triangulation::n_global_active_cells, + * not + * Triangulation::n_locally_active_cells + * on each processor individually. In + * other words, it may be that on some + * processors, no cells are refined at + * all. + * + * The same is true for the fraction of + * cells that is coarsened. + */ template void refine_and_coarsen_fixed_number ( @@ -85,41 +85,41 @@ namespace parallel const double top_fraction_of_cells, const double bottom_fraction_of_cells); - /** - * Like - * dealii::GridRefinement::refine_and_coarsen_fixed_fraction, - * but for parallel distributed - * triangulation. - * - * The vector of criteria needs to be a - * vector of refinement criteria for - * all cells active on the current - * triangulation, - * tria.n_active_cells() - * (and not - * tria.n_locally_owned_active_cells()). However, - * the function will only look at the - * indicators that correspond to those - * cells that are actually locally - * owned, and ignore the indicators for - * all other cells. The function will - * then coordinate among all processors - * that store part of the triangulation - * so that at the end the smallest - * fraction of - * Triangulation::n_global_active_cells - * (not - * Triangulation::n_locally_active_cells - * on each processor individually) is - * refined that together make up a - * total of @p top_fraction_of_error of - * the total error. In other words, it - * may be that on some processors, no - * cells are refined at all. - * - * The same is true for the fraction of - * cells that is coarsened. - */ + /** + * Like + * dealii::GridRefinement::refine_and_coarsen_fixed_fraction, + * but for parallel distributed + * triangulation. + * + * The vector of criteria needs to be a + * vector of refinement criteria for + * all cells active on the current + * triangulation, + * tria.n_active_cells() + * (and not + * tria.n_locally_owned_active_cells()). However, + * the function will only look at the + * indicators that correspond to those + * cells that are actually locally + * owned, and ignore the indicators for + * all other cells. The function will + * then coordinate among all processors + * that store part of the triangulation + * so that at the end the smallest + * fraction of + * Triangulation::n_global_active_cells + * (not + * Triangulation::n_locally_active_cells + * on each processor individually) is + * refined that together make up a + * total of @p top_fraction_of_error of + * the total error. In other words, it + * may be that on some processors, no + * cells are refined at all. + * + * The same is true for the fraction of + * cells that is coarsened. + */ template void refine_and_coarsen_fixed_fraction ( diff --git a/deal.II/include/deal.II/distributed/solution_transfer.h b/deal.II/include/deal.II/distributed/solution_transfer.h index 22d79ccd92..aa230fcf1a 100644 --- a/deal.II/include/deal.II/distributed/solution_transfer.h +++ b/deal.II/include/deal.II/distributed/solution_transfer.h @@ -26,234 +26,234 @@ namespace parallel namespace distributed { -/** - * Transfers a discrete FE function (like a solution vector) by - * interpolation while refining and/or - * coarsening a distributed grid and - * handles the necessary communication. - * - * @note: It is important to note, that if you use more than one - * SolutionTransfer object at the same time, that the calls to - * prepare_*() and interpolate()/deserialize() need to be in the same order. - * - *

Note on ghost elements

- * In a parallel computation PETSc or Trilinos vector may contain ghost - * elements or not. For reading in information with - * prepare_for_coarsening_and_refinement() or prepare_serialization() you need - * to supply vectors with ghost elements, so that all locally_active elements - * can be read. On the other hand, ghosted vectors are generally not writable, - * so for calls to interpolate() or deserialize() you need to supply - * distributed vectors without ghost elements. - * - *

Transfering a solution

- * Here VECTOR is your favorite vector type, e.g. PETScWrappers::MPI::Vector, - * TrilinosWrappers::MPI::Vector, or corresponding blockvectors. - * @verbatim - SolutionTransfer soltrans(dof_handler); - // flag some cells for refinement - // and coarsening, e.g. - GridRefinement::refine_and_coarsen_fixed_fraction( - tria, error_indicators, 0.3, 0.05); - // prepare the triangulation, - tria.prepare_coarsening_and_refinement(); - // prepare the SolutionTransfer object - // for coarsening and refinement and give - // the solution vector that we intend to - // interpolate later, - soltrans.prepare_for_coarsening_and_refinement(solution); - // actually execute the refinement, - tria.execute_coarsening_and_refinement (); - // redistribute dofs, - dof_handler.distribute_dofs (fe); - // and interpolate the solution - VECTOR interpolated_solution; - //create VECTOR in the right size here - soltrans.interpolate(interpolated_solution); -@endverbatim - * - *

Use for Serialization

- * - * This class can be used to serialize and later deserialize a distributed mesh with solution vectors - * to a file. If you use more than one DoFHandler and therefore more than one SolutionTransfer object, they need to be serialized and deserialized in the same order. - * - * If vector has the locally relevant DoFs, serialization works as follows: - *@verbatim - -parallel::distributed::SolutionTransfer sol_trans(dof_handler); -sol_trans.prepare_serialization (vector); - -triangulation.save(filename); -@endverbatim -* For deserialization the vector needs to be a distributed vector (without ghost elements): -* @verbatim -//[create coarse mesh...] -triangulation.load(filename); - -parallel::distributed::SolutionTransfer sol_trans(dof_handler); -sol_trans.deserialize (distributed_vector); -@endverbatim - * - * @ingroup distributed - * @author Timo Heister, 2009-2011 - */ + /** + * Transfers a discrete FE function (like a solution vector) by + * interpolation while refining and/or + * coarsening a distributed grid and + * handles the necessary communication. + * + * @note: It is important to note, that if you use more than one + * SolutionTransfer object at the same time, that the calls to + * prepare_*() and interpolate()/deserialize() need to be in the same order. + * + *

Note on ghost elements

+ * In a parallel computation PETSc or Trilinos vector may contain ghost + * elements or not. For reading in information with + * prepare_for_coarsening_and_refinement() or prepare_serialization() you need + * to supply vectors with ghost elements, so that all locally_active elements + * can be read. On the other hand, ghosted vectors are generally not writable, + * so for calls to interpolate() or deserialize() you need to supply + * distributed vectors without ghost elements. + * + *

Transfering a solution

+ * Here VECTOR is your favorite vector type, e.g. PETScWrappers::MPI::Vector, + * TrilinosWrappers::MPI::Vector, or corresponding blockvectors. + * @verbatim + SolutionTransfer soltrans(dof_handler); + // flag some cells for refinement + // and coarsening, e.g. + GridRefinement::refine_and_coarsen_fixed_fraction( + tria, error_indicators, 0.3, 0.05); + // prepare the triangulation, + tria.prepare_coarsening_and_refinement(); + // prepare the SolutionTransfer object + // for coarsening and refinement and give + // the solution vector that we intend to + // interpolate later, + soltrans.prepare_for_coarsening_and_refinement(solution); + // actually execute the refinement, + tria.execute_coarsening_and_refinement (); + // redistribute dofs, + dof_handler.distribute_dofs (fe); + // and interpolate the solution + VECTOR interpolated_solution; + //create VECTOR in the right size here + soltrans.interpolate(interpolated_solution); + @endverbatim + * + *

Use for Serialization

+ * + * This class can be used to serialize and later deserialize a distributed mesh with solution vectors + * to a file. If you use more than one DoFHandler and therefore more than one SolutionTransfer object, they need to be serialized and deserialized in the same order. + * + * If vector has the locally relevant DoFs, serialization works as follows: + *@verbatim + + parallel::distributed::SolutionTransfer sol_trans(dof_handler); + sol_trans.prepare_serialization (vector); + + triangulation.save(filename); + @endverbatim + * For deserialization the vector needs to be a distributed vector (without ghost elements): + * @verbatim + //[create coarse mesh...] + triangulation.load(filename); + + parallel::distributed::SolutionTransfer sol_trans(dof_handler); + sol_trans.deserialize (distributed_vector); + @endverbatim + * + * @ingroup distributed + * @author Timo Heister, 2009-2011 + */ template > class SolutionTransfer { - public: - /** - * Constructor, takes the current DoFHandler - * as argument. - */ - SolutionTransfer(const DH &dof); - /** - * Destructor. - */ - ~SolutionTransfer(); - - /** - * Prepares the @p SolutionTransfer for - * coarsening and refinement. It - * stores the dof indices of each cell and - * stores the dof values of the vectors in - * @p all_in in each cell that'll be coarsened. - * @p all_in includes all vectors - * that are to be interpolated - * onto the new (refined and/or - * coarsenend) grid. - */ - void prepare_for_coarsening_and_refinement (const std::vector &all_in); - - /** - * Same as previous function - * but for only one discrete function - * to be interpolated. - */ - void prepare_for_coarsening_and_refinement (const VECTOR &in); - - /** - * - */ - void interpolate (std::vector &all_out); - - /** - * Same as the previous function. - * It interpolates only one function. - * It assumes the vectors having the - * right sizes (i.e. in.size()==n_dofs_old, - * out.size()==n_dofs_refined) - * - * Multiple calling of this function is - * NOT allowed. Interpolating - * several functions can be performed - * in one step by using - * interpolate (all_in, all_out) - */ - void interpolate (VECTOR &out); - - - /** - * Return the size in bytes that need - * to be stored per cell. - */ - unsigned int get_data_size() const; - - - /** - * Prepare the serialization of the - * given vector. The serialization is - * done by Triangulation::save(). The - * given vector needs all information - * on the locally active DoFs (it must - * be ghosted). See documentation of - * this class for more information. - */ - void prepare_serialization(const VECTOR &in); - - - /** - * Same as the function above, only - * for a list of vectors. - */ - void prepare_serialization(const std::vector &all_in); - - - /** - * Execute the deserialization of the - * given vector. This needs to be - * done after calling - * Triangulation::load(). The given - * vector must be a fully distributed - * vector without ghost elements. See - * documentation of this class for - * more information. - */ - void deserialize(VECTOR &in); - - - /** - * Same as the function above, only - * for a list of vectors. - */ - void deserialize(std::vector &all_in); - - private: - /** - * Pointer to the degree of - * freedom handler to work - * with. - */ - SmartPointer > dof_handler; - - /** - * A vector that stores - * pointers to all the - * vectors we are supposed to - * copy over from the old to - * the new mesh. - */ - std::vector input_vectors; - - /** - * The offset that the - * Triangulation has assigned - * to this object starting at - * which we are allowed to - * write. - */ - unsigned int offset; - - /** - * A callback function used - * to pack the data on the - * current mesh into objects - * that can later be - * retrieved after - * refinement, coarsening and - * repartitioning. - */ - void pack_callback(const typename Triangulation::cell_iterator &cell, + public: + /** + * Constructor, takes the current DoFHandler + * as argument. + */ + SolutionTransfer(const DH &dof); + /** + * Destructor. + */ + ~SolutionTransfer(); + + /** + * Prepares the @p SolutionTransfer for + * coarsening and refinement. It + * stores the dof indices of each cell and + * stores the dof values of the vectors in + * @p all_in in each cell that'll be coarsened. + * @p all_in includes all vectors + * that are to be interpolated + * onto the new (refined and/or + * coarsenend) grid. + */ + void prepare_for_coarsening_and_refinement (const std::vector &all_in); + + /** + * Same as previous function + * but for only one discrete function + * to be interpolated. + */ + void prepare_for_coarsening_and_refinement (const VECTOR &in); + + /** + * + */ + void interpolate (std::vector &all_out); + + /** + * Same as the previous function. + * It interpolates only one function. + * It assumes the vectors having the + * right sizes (i.e. in.size()==n_dofs_old, + * out.size()==n_dofs_refined) + * + * Multiple calling of this function is + * NOT allowed. Interpolating + * several functions can be performed + * in one step by using + * interpolate (all_in, all_out) + */ + void interpolate (VECTOR &out); + + + /** + * Return the size in bytes that need + * to be stored per cell. + */ + unsigned int get_data_size() const; + + + /** + * Prepare the serialization of the + * given vector. The serialization is + * done by Triangulation::save(). The + * given vector needs all information + * on the locally active DoFs (it must + * be ghosted). See documentation of + * this class for more information. + */ + void prepare_serialization(const VECTOR &in); + + + /** + * Same as the function above, only + * for a list of vectors. + */ + void prepare_serialization(const std::vector &all_in); + + + /** + * Execute the deserialization of the + * given vector. This needs to be + * done after calling + * Triangulation::load(). The given + * vector must be a fully distributed + * vector without ghost elements. See + * documentation of this class for + * more information. + */ + void deserialize(VECTOR &in); + + + /** + * Same as the function above, only + * for a list of vectors. + */ + void deserialize(std::vector &all_in); + + private: + /** + * Pointer to the degree of + * freedom handler to work + * with. + */ + SmartPointer > dof_handler; + + /** + * A vector that stores + * pointers to all the + * vectors we are supposed to + * copy over from the old to + * the new mesh. + */ + std::vector input_vectors; + + /** + * The offset that the + * Triangulation has assigned + * to this object starting at + * which we are allowed to + * write. + */ + unsigned int offset; + + /** + * A callback function used + * to pack the data on the + * current mesh into objects + * that can later be + * retrieved after + * refinement, coarsening and + * repartitioning. + */ + void pack_callback(const typename Triangulation::cell_iterator &cell, + const typename Triangulation::CellStatus status, + void *data); + + /** + * A callback function used + * to unpack the data on the + * current mesh that has been + * packed up previously on + * the mesh before + * refinement, coarsening and + * repartitioning. + */ + void unpack_callback(const typename Triangulation::cell_iterator &cell, const typename Triangulation::CellStatus status, - void* data); - - /** - * A callback function used - * to unpack the data on the - * current mesh that has been - * packed up previously on - * the mesh before - * refinement, coarsening and - * repartitioning. - */ - void unpack_callback(const typename Triangulation::cell_iterator &cell, - const typename Triangulation::CellStatus status, - const void* data, - std::vector &all_out); - - - /** - * - */ - void register_data_attach(const std::size_t size); + const void *data, + std::vector &all_out); + + + /** + * + */ + void register_data_attach(const std::size_t size); }; diff --git a/deal.II/include/deal.II/distributed/tria.h b/deal.II/include/deal.II/distributed/tria.h index 7516e362e7..2893c8f50d 100644 --- a/deal.II/include/deal.II/distributed/tria.h +++ b/deal.II/include/deal.II/distributed/tria.h @@ -62,92 +62,92 @@ namespace internal { namespace p4est { - /** - * A structure whose explicit - * specializations contain - * typedefs to the relevant - * p4est_* and p8est_* - * types. Using this - * structure, for example by - * saying - * types@::connectivity - * we can write code in a - * dimension independent way, - * either referring to - * p4est_connectivity_t or - * p8est_connectivity_t, - * depending on template - * argument. - */ + /** + * A structure whose explicit + * specializations contain + * typedefs to the relevant + * p4est_* and p8est_* + * types. Using this + * structure, for example by + * saying + * types@::connectivity + * we can write code in a + * dimension independent way, + * either referring to + * p4est_connectivity_t or + * p8est_connectivity_t, + * depending on template + * argument. + */ template struct types; template <> struct types<2> { - typedef p4est_connectivity_t connectivity; - typedef p4est_t forest; - typedef p4est_tree_t tree; - typedef p4est_quadrant_t quadrant; - typedef p4est_topidx_t topidx; - typedef p4est_locidx_t locidx; - typedef p4est_balance_type_t balance_type; - typedef p4est_ghost_t ghost; + typedef p4est_connectivity_t connectivity; + typedef p4est_t forest; + typedef p4est_tree_t tree; + typedef p4est_quadrant_t quadrant; + typedef p4est_topidx_t topidx; + typedef p4est_locidx_t locidx; + typedef p4est_balance_type_t balance_type; + typedef p4est_ghost_t ghost; }; template <> struct types<3> { - typedef p8est_connectivity_t connectivity; - typedef p8est_t forest; - typedef p8est_tree_t tree; - typedef p8est_quadrant_t quadrant; - typedef p4est_topidx_t topidx; - typedef p4est_locidx_t locidx; - typedef p8est_balance_type_t balance_type; - typedef p8est_ghost_t ghost; + typedef p8est_connectivity_t connectivity; + typedef p8est_t forest; + typedef p8est_tree_t tree; + typedef p8est_quadrant_t quadrant; + typedef p4est_topidx_t topidx; + typedef p4est_locidx_t locidx; + typedef p8est_balance_type_t balance_type; + typedef p8est_ghost_t ghost; }; - /** - * Initialize the - * GeometryInfo::max_children_per_cell - * children of the cell - * p4est_cell. - */ + /** + * Initialize the + * GeometryInfo::max_children_per_cell + * children of the cell + * p4est_cell. + */ template void init_quadrant_children - (const typename types::quadrant & p4est_cell, + (const typename types::quadrant &p4est_cell, typename types::quadrant (&p4est_children)[GeometryInfo::max_children_per_cell]); - /** - * Initialize quadrant to represent a coarse cell. - */ + /** + * Initialize quadrant to represent a coarse cell. + */ template void - init_coarse_quadrant(typename types::quadrant & quad); + init_coarse_quadrant(typename types::quadrant &quad); - /** - * Returns whether q1 and q2 are equal - */ + /** + * Returns whether q1 and q2 are equal + */ template bool - quadrant_is_equal (const typename types::quadrant & q1, - const typename types::quadrant & q2); + quadrant_is_equal (const typename types::quadrant &q1, + const typename types::quadrant &q2); - //TODO: remove these functions from - //public interface somehow? [TH] + //TODO: remove these functions from + //public interface somehow? [TH] - /** - * returns whether q1 is an ancestor of q2 - */ + /** + * returns whether q1 is an ancestor of q2 + */ template bool - quadrant_is_ancestor (const typename types::quadrant & q1, - const typename types::quadrant & q2); + quadrant_is_ancestor (const typename types::quadrant &q1, + const typename types::quadrant &q2); } } @@ -159,713 +159,713 @@ namespace parallel { -/** - * This class acts like the dealii::Triangulation class, but it - * distributes the mesh across a number of different processors when - * using MPI. The class's interface does not add a lot to the - * dealii::Triangulation class but there are a number of difficult - * algorithms under the hood that ensure we always have a - * load-balanced, fully distributed mesh. Use of this class is - * explained in step-40, step-32, the @ref distributed documentation - * module, as well as the @ref distributed_paper . See there for more - * information. - * - * @note This class does not support anisotropic refinement, because - * it relies on the p4est library that does not support this. Attempts - * to refine cells anisotropically will result in errors. - * - * - *

Interaction with boundary description

- * - * Refining and coarsening a distributed triangulation is a complicated - * process because cells may have to be migrated from one processor to - * another. On a single processor, materializing that part of the global - * mesh that we want to store here from what we have stored before therefore - * may involve several cycles of refining and coarsening the locally stored - * set of cells until we have finally gotten from the previous to the next - * triangulation. (This process is described in more detail in the - * @ref distributed_paper.) Unfortunately, in this process, some information - * can get lost relating to flags that are set by user code and that are - * inherited from mother to child cell but that are not moved along with - * a cell if that cell is migrated from one processor to another. - * - * An example are boundary indicators. Assume, for example, that you start - * with a single cell that is refined once globally, yielding four children. - * If you have four processors, each one owns one cell. Assume now that processor - * 1 sets the boundary indicators of the external boundaries of the cell it owns - * to 42. Since processor 0 does not own this cell, it doesn't set the boundary - * indicators of its ghost cell copy of this cell. Now, assume we do several mesh - * refinement cycles and end up with a configuration where suddenly finds itself - * as the owner of this cell. If boundary indicator 42 means that we need to - * integrate Neumann boundary conditions along this boundary, then processor 0 - * will forget to do so because it has never set the boundary indicator along - * this cell's boundary to 42. - * - * The way to avoid this dilemma is to make sure that things like setting - * boundary indicators or material ids is done immediately - * every time a parallel triangulation is refined. This is not necessary - * for sequential triangulations because, there, these flags are inherited - * from mother to child cell and remain with a cell even if it is refined - * and the children are later coarsened again, but this does not hold for - * distributed triangulations. - * - * - * @author Wolfgang Bangerth, Timo Heister 2008, 2009, 2010, 2011 - * @ingroup distributed - */ + /** + * This class acts like the dealii::Triangulation class, but it + * distributes the mesh across a number of different processors when + * using MPI. The class's interface does not add a lot to the + * dealii::Triangulation class but there are a number of difficult + * algorithms under the hood that ensure we always have a + * load-balanced, fully distributed mesh. Use of this class is + * explained in step-40, step-32, the @ref distributed documentation + * module, as well as the @ref distributed_paper . See there for more + * information. + * + * @note This class does not support anisotropic refinement, because + * it relies on the p4est library that does not support this. Attempts + * to refine cells anisotropically will result in errors. + * + * + *

Interaction with boundary description

+ * + * Refining and coarsening a distributed triangulation is a complicated + * process because cells may have to be migrated from one processor to + * another. On a single processor, materializing that part of the global + * mesh that we want to store here from what we have stored before therefore + * may involve several cycles of refining and coarsening the locally stored + * set of cells until we have finally gotten from the previous to the next + * triangulation. (This process is described in more detail in the + * @ref distributed_paper.) Unfortunately, in this process, some information + * can get lost relating to flags that are set by user code and that are + * inherited from mother to child cell but that are not moved along with + * a cell if that cell is migrated from one processor to another. + * + * An example are boundary indicators. Assume, for example, that you start + * with a single cell that is refined once globally, yielding four children. + * If you have four processors, each one owns one cell. Assume now that processor + * 1 sets the boundary indicators of the external boundaries of the cell it owns + * to 42. Since processor 0 does not own this cell, it doesn't set the boundary + * indicators of its ghost cell copy of this cell. Now, assume we do several mesh + * refinement cycles and end up with a configuration where suddenly finds itself + * as the owner of this cell. If boundary indicator 42 means that we need to + * integrate Neumann boundary conditions along this boundary, then processor 0 + * will forget to do so because it has never set the boundary indicator along + * this cell's boundary to 42. + * + * The way to avoid this dilemma is to make sure that things like setting + * boundary indicators or material ids is done immediately + * every time a parallel triangulation is refined. This is not necessary + * for sequential triangulations because, there, these flags are inherited + * from mother to child cell and remain with a cell even if it is refined + * and the children are later coarsened again, but this does not hold for + * distributed triangulations. + * + * + * @author Wolfgang Bangerth, Timo Heister 2008, 2009, 2010, 2011 + * @ingroup distributed + */ template class Triangulation : public dealii::Triangulation { - public: - /** - * Import the various - * iterator typedefs from the - * base class. - */ - typedef typename dealii::Triangulation::active_cell_iterator active_cell_iterator; - typedef typename dealii::Triangulation::cell_iterator cell_iterator; - - /** - * Generic settings for distributed - * Triangulations. If - * mesh_reconstruction_after_repartitioning - * is set, the deal.II mesh will be - * reconstructed from the coarse mesh - * every time a repartioning in p4est - * happens. This can be a bit more - * expensive, but guarantees the same - * memory layout and therefore cell - * ordering in the deal.II mesh. As - * assembly is done in the deal.II - * cell ordering, this flag is - * required to get reproducible - * behaviour after snapshot/resume. - */ - enum Settings - { - default_setting = 0x0, - mesh_reconstruction_after_repartitioning = 0x1 - }; - - - - /** - * Constructor. - * - * @param mpi_communicator denotes - * the MPI communicator to be used for - * the triangulation. - * - * @param smooth_grid Degree - * and kind of mesh smoothing - * to be applied to the - * mesh. See the - * dealii::Triangulation - * class for a description of - * the kinds of smoothing - * operations that can be - * applied. - * - * @param settings See the - * description of the Settings - * enumerator. - * - * @note This class does not - * currently support the - * check_for_distorted_cells - * argument provided by the - * base class. - * - * @note While it is possible to pass - * all of the mesh smoothing flags - * listed in the base class to - * objects of this type, it is not - * always possible to honor all of - * these smoothing options if they - * would require knowledge of - * refinement/coarsening flags on - * cells not locally owned by this - * processor. As a consequence, for - * some of these flags, the ultimate - * number of cells of the parallel - * triangulation may depend on the - * number of processors into which it - * is partitioned. On the other hand, - * if no smoothing flags are passed, - * if you always mark the same cells - * of the mesh, you will always get - * the exact same refined mesh - * independent of the number of - * processors into which the - * triangulation is partitioned. - */ - Triangulation (MPI_Comm mpi_communicator, - const typename dealii::Triangulation::MeshSmoothing - smooth_grid = (dealii::Triangulation::none), - const Settings settings = default_setting); - - /** - * Destructor. - */ - virtual ~Triangulation (); - - /** - * Reset this triangulation into a - * virgin state by deleting all data. - * - * Note that this operation is only - * allowed if no subscriptions to this - * object exist any more, such as - * DoFHandler objects using it. - */ - virtual void clear (); - - /** - * Implementation of the same - * function as in the base - * class. - */ - virtual void copy_triangulation (const dealii::Triangulation &old_tria); - - /** - * Create a triangulation as - * documented in the base - * class. - * - * This function also sets up - * the various data - * structures necessary to - * distribute a mesh across a - * number of processors. This - * will be necessary once the - * mesh is being refined, - * though we will always keep - * the entire coarse mesh - * that is generated by this - * function on all - * processors. - */ - virtual void create_triangulation (const std::vector > &vertices, - const std::vector > &cells, - const SubCellData &subcelldata); - - /** - * Coarsen and refine the - * mesh according to - * refinement and coarsening - * flags set. - * - * Since the current - * processor only has control - * over those cells it owns - * (i.e. the ones for which - * cell-@>subdomain_id() - * == - * this-@>locally_owned_subdomain()), - * refinement and coarsening - * flags are only respected - * for those locally owned - * cells. Flags may be set on - * other cells as well (and - * may often, in fact, if you - * call - * dealii::Triangulation::prepare_coarsening_and_refinement) - * but will be largely - * ignored: the decision to - * refine the global mesh - * will only be affected by - * flags set on locally owned - * cells. - */ - virtual void execute_coarsening_and_refinement (); - - /** - * Return the subdomain id of - * those cells that are owned - * by the current - * processor. All cells in - * the triangulation that do - * not have this subdomain id - * are either owned by - * another processor or have - * children that only exist - * on other processors. - */ - types::subdomain_id locally_owned_subdomain () const; - - /** - * Return the number of - * active cells in the - * triangulation that are - * locally owned, i.e. that - * have a subdomain_id equal - * to - * locally_owned_subdomain(). Note - * that there may be more - * active cells in the - * triangulation stored on - * the present processor, - * such as for example ghost - * cells, or cells further - * away from the locally - * owned block of cells but - * that are needed to ensure - * that the triangulation - * that stores this - * processor's set of active - * cells still remains - * balanced with respect to - * the 2:1 size ratio of - * adjacent cells. - * - * As a consequence of the remark - * above, the result of this function - * is always smaller or equal to the - * result of the function with the - * same name in the ::Triangulation - * base class, which includes the - * active ghost and artificial cells - * (see also @ref GlossArtificialCell - * and @ref GlossGhostCell). - */ - unsigned int n_locally_owned_active_cells () const; - - /** - * Return the sum over all - * processors of the number - * of active cells owned by - * each processor. This - * equals the overall number - * of active cells in the - * distributed triangulation. - */ - unsigned int n_global_active_cells () const; - - /** - * Returns the global maximum level. This may be bigger than n_levels. - */ - virtual unsigned int n_global_levels () const; - - /** - * Return the number of - * active cells owned by each - * of the MPI processes that - * contribute to this - * triangulation. The element - * of this vector indexed by - * locally_owned_subdomain() - * equals the result of - * n_locally_owned_active_cells(). - */ - const std::vector & - n_locally_owned_active_cells_per_processor () const; - - /** - * Return the MPI - * communicator used by this - * triangulation. - */ - MPI_Comm get_communicator () const; - - /** - * Return the local memory - * consumption in bytes. - */ - virtual std::size_t memory_consumption () const; - - /** - * Return the local memory - * consumption contained in the p4est - * data structures alone. This is - * already contained in - * memory_consumption() but made - * available separately for debugging - * purposes. - */ - virtual std::size_t memory_consumption_p4est () const; - - /** - * A collective operation that produces - * a sequence of output files with the - * given file base name that contain - * the mesh in VTK format. - * - * More than anything else, this - * function is useful for debugging the - * interface between deal.II and p4est. - */ - void write_mesh_vtk (const char *file_basename) const; - - /** - * Produce a check sum of the - * triangulation. This is a - * collective operation and - * is mostly useful for - * debugging purposes. - */ - unsigned int get_checksum () const; - - /** - * Save the refinement information from the coarse mesh into the given - * file. This file needs to be reachable from all nodes in the computation - * on a shared network file system. See the SolutionTransfer class - * on how to store solution vectors into this file. - */ - void save(const char* filename) const; - - /** - * Load the refinement information saved with save() back in. The mesh - * must contain the same coarse mesh that was used in save(). You need - * to load with the same number of CPUs that you saved with. - */ - void load(const char* filename); - - /** - * Used to inform in the callbacks of - * register_data_attach() and - * notify_ready_to_unpack() how the - * cell with the given cell_iterator - * is going to change. Note that - * this may me different then the - * refine_flag() and coarsen_flag() - * in the cell_iterator because of - * refinement constraints that this - * machine does not see. - */ - enum CellStatus - { - CELL_PERSIST, CELL_REFINE, CELL_COARSEN, CELL_INVALID - }; - - /** - * Register a function with - * the current Triangulation - * object that will be used - * to attach data to active - * cells before - * execute_coarsening_and_refinement(). In - * execute_coarsening_and_refinement() - * the Triangulation will - * call the given function - * pointer and provide - * @p size bytes to store - * data. If necessary, this data will be - * transferred to the new - * owner of that cell during repartitioning - * the tree. See - * notify_ready_to_unpack() - * on how to retrieve the - * data. - * - * Callers need to store the - * return value. It - * specifies an offset of the - * position at which data can - * later be retrieved during - * a call to - * notify_ready_to_unpack(). - */ - unsigned int - register_data_attach (const std::size_t size, + public: + /** + * Import the various + * iterator typedefs from the + * base class. + */ + typedef typename dealii::Triangulation::active_cell_iterator active_cell_iterator; + typedef typename dealii::Triangulation::cell_iterator cell_iterator; + + /** + * Generic settings for distributed + * Triangulations. If + * mesh_reconstruction_after_repartitioning + * is set, the deal.II mesh will be + * reconstructed from the coarse mesh + * every time a repartioning in p4est + * happens. This can be a bit more + * expensive, but guarantees the same + * memory layout and therefore cell + * ordering in the deal.II mesh. As + * assembly is done in the deal.II + * cell ordering, this flag is + * required to get reproducible + * behaviour after snapshot/resume. + */ + enum Settings + { + default_setting = 0x0, + mesh_reconstruction_after_repartitioning = 0x1 + }; + + + + /** + * Constructor. + * + * @param mpi_communicator denotes + * the MPI communicator to be used for + * the triangulation. + * + * @param smooth_grid Degree + * and kind of mesh smoothing + * to be applied to the + * mesh. See the + * dealii::Triangulation + * class for a description of + * the kinds of smoothing + * operations that can be + * applied. + * + * @param settings See the + * description of the Settings + * enumerator. + * + * @note This class does not + * currently support the + * check_for_distorted_cells + * argument provided by the + * base class. + * + * @note While it is possible to pass + * all of the mesh smoothing flags + * listed in the base class to + * objects of this type, it is not + * always possible to honor all of + * these smoothing options if they + * would require knowledge of + * refinement/coarsening flags on + * cells not locally owned by this + * processor. As a consequence, for + * some of these flags, the ultimate + * number of cells of the parallel + * triangulation may depend on the + * number of processors into which it + * is partitioned. On the other hand, + * if no smoothing flags are passed, + * if you always mark the same cells + * of the mesh, you will always get + * the exact same refined mesh + * independent of the number of + * processors into which the + * triangulation is partitioned. + */ + Triangulation (MPI_Comm mpi_communicator, + const typename dealii::Triangulation::MeshSmoothing + smooth_grid = (dealii::Triangulation::none), + const Settings settings = default_setting); + + /** + * Destructor. + */ + virtual ~Triangulation (); + + /** + * Reset this triangulation into a + * virgin state by deleting all data. + * + * Note that this operation is only + * allowed if no subscriptions to this + * object exist any more, such as + * DoFHandler objects using it. + */ + virtual void clear (); + + /** + * Implementation of the same + * function as in the base + * class. + */ + virtual void copy_triangulation (const dealii::Triangulation &old_tria); + + /** + * Create a triangulation as + * documented in the base + * class. + * + * This function also sets up + * the various data + * structures necessary to + * distribute a mesh across a + * number of processors. This + * will be necessary once the + * mesh is being refined, + * though we will always keep + * the entire coarse mesh + * that is generated by this + * function on all + * processors. + */ + virtual void create_triangulation (const std::vector > &vertices, + const std::vector > &cells, + const SubCellData &subcelldata); + + /** + * Coarsen and refine the + * mesh according to + * refinement and coarsening + * flags set. + * + * Since the current + * processor only has control + * over those cells it owns + * (i.e. the ones for which + * cell-@>subdomain_id() + * == + * this-@>locally_owned_subdomain()), + * refinement and coarsening + * flags are only respected + * for those locally owned + * cells. Flags may be set on + * other cells as well (and + * may often, in fact, if you + * call + * dealii::Triangulation::prepare_coarsening_and_refinement) + * but will be largely + * ignored: the decision to + * refine the global mesh + * will only be affected by + * flags set on locally owned + * cells. + */ + virtual void execute_coarsening_and_refinement (); + + /** + * Return the subdomain id of + * those cells that are owned + * by the current + * processor. All cells in + * the triangulation that do + * not have this subdomain id + * are either owned by + * another processor or have + * children that only exist + * on other processors. + */ + types::subdomain_id locally_owned_subdomain () const; + + /** + * Return the number of + * active cells in the + * triangulation that are + * locally owned, i.e. that + * have a subdomain_id equal + * to + * locally_owned_subdomain(). Note + * that there may be more + * active cells in the + * triangulation stored on + * the present processor, + * such as for example ghost + * cells, or cells further + * away from the locally + * owned block of cells but + * that are needed to ensure + * that the triangulation + * that stores this + * processor's set of active + * cells still remains + * balanced with respect to + * the 2:1 size ratio of + * adjacent cells. + * + * As a consequence of the remark + * above, the result of this function + * is always smaller or equal to the + * result of the function with the + * same name in the ::Triangulation + * base class, which includes the + * active ghost and artificial cells + * (see also @ref GlossArtificialCell + * and @ref GlossGhostCell). + */ + unsigned int n_locally_owned_active_cells () const; + + /** + * Return the sum over all + * processors of the number + * of active cells owned by + * each processor. This + * equals the overall number + * of active cells in the + * distributed triangulation. + */ + unsigned int n_global_active_cells () const; + + /** + * Returns the global maximum level. This may be bigger than n_levels. + */ + virtual unsigned int n_global_levels () const; + + /** + * Return the number of + * active cells owned by each + * of the MPI processes that + * contribute to this + * triangulation. The element + * of this vector indexed by + * locally_owned_subdomain() + * equals the result of + * n_locally_owned_active_cells(). + */ + const std::vector & + n_locally_owned_active_cells_per_processor () const; + + /** + * Return the MPI + * communicator used by this + * triangulation. + */ + MPI_Comm get_communicator () const; + + /** + * Return the local memory + * consumption in bytes. + */ + virtual std::size_t memory_consumption () const; + + /** + * Return the local memory + * consumption contained in the p4est + * data structures alone. This is + * already contained in + * memory_consumption() but made + * available separately for debugging + * purposes. + */ + virtual std::size_t memory_consumption_p4est () const; + + /** + * A collective operation that produces + * a sequence of output files with the + * given file base name that contain + * the mesh in VTK format. + * + * More than anything else, this + * function is useful for debugging the + * interface between deal.II and p4est. + */ + void write_mesh_vtk (const char *file_basename) const; + + /** + * Produce a check sum of the + * triangulation. This is a + * collective operation and + * is mostly useful for + * debugging purposes. + */ + unsigned int get_checksum () const; + + /** + * Save the refinement information from the coarse mesh into the given + * file. This file needs to be reachable from all nodes in the computation + * on a shared network file system. See the SolutionTransfer class + * on how to store solution vectors into this file. + */ + void save(const char *filename) const; + + /** + * Load the refinement information saved with save() back in. The mesh + * must contain the same coarse mesh that was used in save(). You need + * to load with the same number of CPUs that you saved with. + */ + void load(const char *filename); + + /** + * Used to inform in the callbacks of + * register_data_attach() and + * notify_ready_to_unpack() how the + * cell with the given cell_iterator + * is going to change. Note that + * this may me different then the + * refine_flag() and coarsen_flag() + * in the cell_iterator because of + * refinement constraints that this + * machine does not see. + */ + enum CellStatus + { + CELL_PERSIST, CELL_REFINE, CELL_COARSEN, CELL_INVALID + }; + + /** + * Register a function with + * the current Triangulation + * object that will be used + * to attach data to active + * cells before + * execute_coarsening_and_refinement(). In + * execute_coarsening_and_refinement() + * the Triangulation will + * call the given function + * pointer and provide + * @p size bytes to store + * data. If necessary, this data will be + * transferred to the new + * owner of that cell during repartitioning + * the tree. See + * notify_ready_to_unpack() + * on how to retrieve the + * data. + * + * Callers need to store the + * return value. It + * specifies an offset of the + * position at which data can + * later be retrieved during + * a call to + * notify_ready_to_unpack(). + */ + unsigned int + register_data_attach (const std::size_t size, + const std_cxx1x::function & pack_callback); + + /** + * The given function is called for + * each new active cell and supplies + * a pointer to the data saved with + * register_data_attach(). + */ + void + notify_ready_to_unpack (const unsigned int offset, const std_cxx1x::function & pack_callback); - - /** - * The given function is called for - * each new active cell and supplies - * a pointer to the data saved with - * register_data_attach(). - */ - void - notify_ready_to_unpack (const unsigned int offset, - const std_cxx1x::function & unpack_callback); - - /** - * Returns a permutation vector for the order the coarse - * cells are handed of to p4est. For example the first - * element i in this vector denotes that the first cell - * in hierarchical ordering is the ith deal cell starting - * from begin(0). - */ - const std::vector & - get_p4est_tree_to_coarse_cell_permutation() const; - - private: - /** - * MPI communicator to be - * used for the - * triangulation. We create a - * unique communicator for - * this class, which is a - * duplicate of the one - * passed to the constructor. - */ - MPI_Comm mpi_communicator; - - /** - * store the Settings. - */ - Settings settings; - - /** - * The subdomain id to be - * used for the current - * processor. - */ - types::subdomain_id my_subdomain; - - /** - * A flag that indicates whether the - * triangulation has actual content. - */ - bool triangulation_has_content; - - /** - * A structure that contains - * some numbers about the - * distributed triangulation. - */ - struct NumberCache - { - std::vector n_locally_owned_active_cells; - unsigned int n_global_active_cells; - unsigned int n_global_levels; - }; - - NumberCache number_cache; - - /** - * A data structure that holds the - * connectivity between trees. Since - * each tree is rooted in a coarse grid - * cell, this data structure holds the - * connectivity between the cells of - * the coarse grid. - */ - typename dealii::internal::p4est::types::connectivity *connectivity; - - /** - * A data structure that holds the - * local part of the global - * triangulation. - */ - typename dealii::internal::p4est::types::forest *parallel_forest; - - /** - * A flag that indicates - * whether refinement of a - * triangulation is currently - * in progress. This flag is - * used to disambiguate whether - * a call to execute_coarsening_and_triangulation - * came from the outside or - * through a recursive call. While the - * first time we want to take - * over work to copy things - * from a refined p4est, the - * other times we don't want to - * get in the way as these - * latter calls to - * Triangulation::execute_coarsening_and_refinement() - * are simply there in order to - * re-create a triangulation - * that matches the p4est. - */ - bool refinement_in_progress; - - - /** - * number of bytes that get attached to the Triangulation - * through register_data_attach() for example - * SolutionTransfer. - */ - unsigned int attached_data_size; - - /** - * number of functions that get attached to the Triangulation - * through register_data_attach() for example - * SolutionTransfer. - */ - unsigned int n_attached_datas; - - /** - * number of functions that need to unpack their data - * after a call from load() - */ - unsigned int n_attached_deserialize; - - typedef std_cxx1x::function< - void(typename Triangulation::cell_iterator, CellStatus, void*) - > pack_callback_t; - - typedef std::pair callback_pair_t; - - typedef std::list callback_list_t; - - /** - * List of callback functions registered by - * register_data_attach() that are going to be called - * for packing data. - */ - callback_list_t attached_data_pack_callbacks; - - - /** - * Two arrays that store which p4est - * tree corresponds to which coarse - * grid cell and vice versa. We need - * these arrays because p4est goes with - * the original order of coarse cells - * when it sets up its forest, and then - * applies the Morton ordering within - * each tree. But if coarse grid cells - * are badly ordered this may mean that - * individual parts of the forest - * stored on a local machine may be - * split across coarse grid cells that - * are not geometrically - * close. Consequently, we apply a - * Cuthill-McKee preordering to ensure - * that the part of the forest stored - * by p4est is located on geometrically - * close coarse grid cells. - */ - std::vector coarse_cell_to_p4est_tree_permutation; - std::vector p4est_tree_to_coarse_cell_permutation; - - /** - * Return a pointer to the p4est - * tree that belongs to the given - * dealii_coarse_cell_index() - */ - typename dealii::internal::p4est::types::tree * - init_tree(const int dealii_coarse_cell_index) const; - - /** - * The function that computes the - * permutation between the two data - * storage schemes. - */ - void setup_coarse_cell_to_p4est_tree_permutation (); - - /** - * Take the contents of a newly created - * triangulation we are attached to and - * copy it to p4est data structures. - * - * This function exists in 2d - * and 3d variants. - */ - void copy_new_triangulation_to_p4est (dealii::internal::int2type<2>); - void copy_new_triangulation_to_p4est (dealii::internal::int2type<3>); - - /** - * Copy the local part of the refined - * forest from p4est into the attached - * triangulation. - */ - void copy_local_forest_to_triangulation (); - - - /** - * Update the number_cache - * variable after mesh - * creation or refinement. - */ - void update_number_cache (); - - /** - * Internal function notifying all - * registered classes to attach their - * data before repartitioning - * occurs. Called from - * execute_coarsening_and_refinement(). - */ - void attach_mesh_data(); - - - template friend class dealii::internal::DoFHandler::Policy::ParallelDistributed; + const void *)> & unpack_callback); + + /** + * Returns a permutation vector for the order the coarse + * cells are handed of to p4est. For example the first + * element i in this vector denotes that the first cell + * in hierarchical ordering is the ith deal cell starting + * from begin(0). + */ + const std::vector & + get_p4est_tree_to_coarse_cell_permutation() const; + + private: + /** + * MPI communicator to be + * used for the + * triangulation. We create a + * unique communicator for + * this class, which is a + * duplicate of the one + * passed to the constructor. + */ + MPI_Comm mpi_communicator; + + /** + * store the Settings. + */ + Settings settings; + + /** + * The subdomain id to be + * used for the current + * processor. + */ + types::subdomain_id my_subdomain; + + /** + * A flag that indicates whether the + * triangulation has actual content. + */ + bool triangulation_has_content; + + /** + * A structure that contains + * some numbers about the + * distributed triangulation. + */ + struct NumberCache + { + std::vector n_locally_owned_active_cells; + unsigned int n_global_active_cells; + unsigned int n_global_levels; + }; + + NumberCache number_cache; + + /** + * A data structure that holds the + * connectivity between trees. Since + * each tree is rooted in a coarse grid + * cell, this data structure holds the + * connectivity between the cells of + * the coarse grid. + */ + typename dealii::internal::p4est::types::connectivity *connectivity; + + /** + * A data structure that holds the + * local part of the global + * triangulation. + */ + typename dealii::internal::p4est::types::forest *parallel_forest; + + /** + * A flag that indicates + * whether refinement of a + * triangulation is currently + * in progress. This flag is + * used to disambiguate whether + * a call to execute_coarsening_and_triangulation + * came from the outside or + * through a recursive call. While the + * first time we want to take + * over work to copy things + * from a refined p4est, the + * other times we don't want to + * get in the way as these + * latter calls to + * Triangulation::execute_coarsening_and_refinement() + * are simply there in order to + * re-create a triangulation + * that matches the p4est. + */ + bool refinement_in_progress; + + + /** + * number of bytes that get attached to the Triangulation + * through register_data_attach() for example + * SolutionTransfer. + */ + unsigned int attached_data_size; + + /** + * number of functions that get attached to the Triangulation + * through register_data_attach() for example + * SolutionTransfer. + */ + unsigned int n_attached_datas; + + /** + * number of functions that need to unpack their data + * after a call from load() + */ + unsigned int n_attached_deserialize; + + typedef std_cxx1x::function< + void(typename Triangulation::cell_iterator, CellStatus, void *) + > pack_callback_t; + + typedef std::pair callback_pair_t; + + typedef std::list callback_list_t; + + /** + * List of callback functions registered by + * register_data_attach() that are going to be called + * for packing data. + */ + callback_list_t attached_data_pack_callbacks; + + + /** + * Two arrays that store which p4est + * tree corresponds to which coarse + * grid cell and vice versa. We need + * these arrays because p4est goes with + * the original order of coarse cells + * when it sets up its forest, and then + * applies the Morton ordering within + * each tree. But if coarse grid cells + * are badly ordered this may mean that + * individual parts of the forest + * stored on a local machine may be + * split across coarse grid cells that + * are not geometrically + * close. Consequently, we apply a + * Cuthill-McKee preordering to ensure + * that the part of the forest stored + * by p4est is located on geometrically + * close coarse grid cells. + */ + std::vector coarse_cell_to_p4est_tree_permutation; + std::vector p4est_tree_to_coarse_cell_permutation; + + /** + * Return a pointer to the p4est + * tree that belongs to the given + * dealii_coarse_cell_index() + */ + typename dealii::internal::p4est::types::tree * + init_tree(const int dealii_coarse_cell_index) const; + + /** + * The function that computes the + * permutation between the two data + * storage schemes. + */ + void setup_coarse_cell_to_p4est_tree_permutation (); + + /** + * Take the contents of a newly created + * triangulation we are attached to and + * copy it to p4est data structures. + * + * This function exists in 2d + * and 3d variants. + */ + void copy_new_triangulation_to_p4est (dealii::internal::int2type<2>); + void copy_new_triangulation_to_p4est (dealii::internal::int2type<3>); + + /** + * Copy the local part of the refined + * forest from p4est into the attached + * triangulation. + */ + void copy_local_forest_to_triangulation (); + + + /** + * Update the number_cache + * variable after mesh + * creation or refinement. + */ + void update_number_cache (); + + /** + * Internal function notifying all + * registered classes to attach their + * data before repartitioning + * occurs. Called from + * execute_coarsening_and_refinement(). + */ + void attach_mesh_data(); + + + template friend class dealii::internal::DoFHandler::Policy::ParallelDistributed; }; - /** - * Specialization of the general template - * for the 1d case. There is currently no - * support for distributing 1d - * triangulations. Consequently, all this - * class does is throw an exception. - */ + /** + * Specialization of the general template + * for the 1d case. There is currently no + * support for distributing 1d + * triangulations. Consequently, all this + * class does is throw an exception. + */ template class Triangulation<1,spacedim> : public dealii::Triangulation<1,spacedim> { - public: - /** - * Constructor. The argument denotes - * the MPI communicator to be used for - * the triangulation. - */ - Triangulation (MPI_Comm mpi_communicator); - - /** - * Destructor. - */ - virtual ~Triangulation (); - - /** - * Return the MPI - * communicator used by this - * triangulation. - */ - MPI_Comm get_communicator () const; - - /** - * Return the sum over all - * processors of the number - * of active cells owned by - * each processor. This - * equals the overall number - * of active cells in the - * distributed triangulation. - */ - unsigned int n_global_active_cells () const; - virtual unsigned int n_global_levels () const; - - /** - * Returns a permutation vector for the order the coarse - * cells are handed of to p4est. For example the first - * element i in this vector denotes that the first cell - * in hierarchical ordering is the ith deal cell starting - * from begin(0). - */ - const std::vector & - get_p4est_tree_to_coarse_cell_permutation() const; - - /** - * Return the subdomain id of - * those cells that are owned - * by the current - * processor. All cells in - * the triangulation that do - * not have this subdomain id - * are either owned by - * another processor or have - * children that only exist - * on other processors. - */ - types::subdomain_id locally_owned_subdomain () const; - - /** - * Dummy arrays. This class - * isn't usable but the - * compiler wants to see - * these variables at a - * couple places anyway. - */ - std::vector coarse_cell_to_p4est_tree_permutation; - std::vector p4est_tree_to_coarse_cell_permutation; + public: + /** + * Constructor. The argument denotes + * the MPI communicator to be used for + * the triangulation. + */ + Triangulation (MPI_Comm mpi_communicator); + + /** + * Destructor. + */ + virtual ~Triangulation (); + + /** + * Return the MPI + * communicator used by this + * triangulation. + */ + MPI_Comm get_communicator () const; + + /** + * Return the sum over all + * processors of the number + * of active cells owned by + * each processor. This + * equals the overall number + * of active cells in the + * distributed triangulation. + */ + unsigned int n_global_active_cells () const; + virtual unsigned int n_global_levels () const; + + /** + * Returns a permutation vector for the order the coarse + * cells are handed of to p4est. For example the first + * element i in this vector denotes that the first cell + * in hierarchical ordering is the ith deal cell starting + * from begin(0). + */ + const std::vector & + get_p4est_tree_to_coarse_cell_permutation() const; + + /** + * Return the subdomain id of + * those cells that are owned + * by the current + * processor. All cells in + * the triangulation that do + * not have this subdomain id + * are either owned by + * another processor or have + * children that only exist + * on other processors. + */ + types::subdomain_id locally_owned_subdomain () const; + + /** + * Dummy arrays. This class + * isn't usable but the + * compiler wants to see + * these variables at a + * couple places anyway. + */ + std::vector coarse_cell_to_p4est_tree_permutation; + std::vector p4est_tree_to_coarse_cell_permutation; }; } } @@ -877,58 +877,58 @@ namespace parallel { namespace distributed { - /** - * Dummy class the compiler chooses for - * parallel distributed triangulations if - * we didn't actually configure deal.II - * with the p4est library. The existence - * of this class allows us to refer to - * parallel::distributed::Triangulation - * objects throughout the library even if - * it is disabled. - * - * Since the constructor of this class is - * private, no such objects can actually - * be created if we don't have p4est - * available. - */ + /** + * Dummy class the compiler chooses for + * parallel distributed triangulations if + * we didn't actually configure deal.II + * with the p4est library. The existence + * of this class allows us to refer to + * parallel::distributed::Triangulation + * objects throughout the library even if + * it is disabled. + * + * Since the constructor of this class is + * private, no such objects can actually + * be created if we don't have p4est + * available. + */ template class Triangulation : public dealii::Triangulation { - private: - /** - * Constructor. - */ - Triangulation (); - - public: - - /** - * Destructor. - */ - virtual ~Triangulation (); - - /** - * Return the subdomain id of - * those cells that are owned - * by the current - * processor. All cells in - * the triangulation that do - * not have this subdomain id - * are either owned by - * another processor or have - * children that only exist - * on other processors. - */ - types::subdomain_id locally_owned_subdomain () const; - - /** - * Return the MPI - * communicator used by this - * triangulation. - */ + private: + /** + * Constructor. + */ + Triangulation (); + + public: + + /** + * Destructor. + */ + virtual ~Triangulation (); + + /** + * Return the subdomain id of + * those cells that are owned + * by the current + * processor. All cells in + * the triangulation that do + * not have this subdomain id + * are either owned by + * another processor or have + * children that only exist + * on other processors. + */ + types::subdomain_id locally_owned_subdomain () const; + + /** + * Return the MPI + * communicator used by this + * triangulation. + */ #ifdef DEAL_II_COMPILER_SUPPORTS_MPI - MPI_Comm get_communicator () const; + MPI_Comm get_communicator () const; #endif }; } diff --git a/deal.II/include/deal.II/dofs/block_info.h b/deal.II/include/deal.II/dofs/block_info.h index a8a874055a..27341acc0b 100644 --- a/deal.II/include/deal.II/dofs/block_info.h +++ b/deal.II/include/deal.II/dofs/block_info.h @@ -88,147 +88,147 @@ namespace hp */ class BlockInfo : public Subscriptor { - public: - /** - * @brief Fill the object with values - * describing block structure - * of the DoFHandler. - * - * This function will also clear - * the local() indices. - */ - template - void initialize(const DoFHandler&, bool levels_only = false, bool multigrid = false); - - /** - * @brief Fill the object with values - * describing level block - * structure of the - * MGDoFHandler. If - * levels_only is false, - * the other initialize() is - * called as well. - * - * This function will also clear - * the local() indices. - */ - template - void initialize(const MGDoFHandler&, bool levels_only = false); - - /** - * @brief Initialize block structure - * on cells and compute - * renumbering between cell - * dofs and block cell dofs. - */ - template - void initialize_local(const DoFHandler&); - - /** - * Access the BlockIndices - * structure of the global - * system. - */ - const BlockIndices& global() const; - - /** - * Access BlockIndices for the - * local system on a cell. - */ - const BlockIndices& local() const; - - /** - * Access the BlockIndices - * structure of a level in the - * multilevel hierarchy. - */ - const BlockIndices& level(unsigned int level) const; - - /** - * Return the index after local - * renumbering. - * - * The input of this function is - * an index between zero and the - * number of dofs per cell, - * numbered in local block - * ordering, that is first all - * indices of the first system - * block, then all of the second - * block and so forth. The - * function then outputs the index - * in the standard local - * numbering of DoFAccessor. - */ - unsigned int renumber (const unsigned int i) const; - - /** - * The number of base elements. - */ - unsigned int n_base_elements() const; - - /** - * Return the base element of - * this index. - */ - unsigned int base_element (const unsigned int i) const; - - /** - * Write a summary of the block - * structure to the stream. - */ - template - void - print(OS& stream) const; - - /** - * Determine an estimate for the - * memory consumption (in bytes) - * of this object. - */ - std::size_t memory_consumption () const; - - /** - * Read or write the data of this object to or - * from a stream for the purpose of serialization - */ - template - void serialize (Archive & ar, - const unsigned int version); - - private: - /** - * @brief The block structure - * of the global system. - */ - BlockIndices bi_global; - /** - * @brief The multilevel block structure. - */ - std::vector levels; - - /** - * @brief The block structure - * of the cell systems. - */ - BlockIndices bi_local; - - /** - * The base element associated - * with each block. - */ - std::vector base_elements; - - /** - * A vector containing the - * renumbering from the - * standard order of degrees of - * freedom on a cell to a - * component wise - * ordering. Filled by - * initialize(). - */ - std::vector local_renumbering; +public: + /** + * @brief Fill the object with values + * describing block structure + * of the DoFHandler. + * + * This function will also clear + * the local() indices. + */ + template + void initialize(const DoFHandler &, bool levels_only = false, bool multigrid = false); + + /** + * @brief Fill the object with values + * describing level block + * structure of the + * MGDoFHandler. If + * levels_only is false, + * the other initialize() is + * called as well. + * + * This function will also clear + * the local() indices. + */ + template + void initialize(const MGDoFHandler &, bool levels_only = false); + + /** + * @brief Initialize block structure + * on cells and compute + * renumbering between cell + * dofs and block cell dofs. + */ + template + void initialize_local(const DoFHandler &); + + /** + * Access the BlockIndices + * structure of the global + * system. + */ + const BlockIndices &global() const; + + /** + * Access BlockIndices for the + * local system on a cell. + */ + const BlockIndices &local() const; + + /** + * Access the BlockIndices + * structure of a level in the + * multilevel hierarchy. + */ + const BlockIndices &level(unsigned int level) const; + + /** + * Return the index after local + * renumbering. + * + * The input of this function is + * an index between zero and the + * number of dofs per cell, + * numbered in local block + * ordering, that is first all + * indices of the first system + * block, then all of the second + * block and so forth. The + * function then outputs the index + * in the standard local + * numbering of DoFAccessor. + */ + unsigned int renumber (const unsigned int i) const; + + /** + * The number of base elements. + */ + unsigned int n_base_elements() const; + + /** + * Return the base element of + * this index. + */ + unsigned int base_element (const unsigned int i) const; + + /** + * Write a summary of the block + * structure to the stream. + */ + template + void + print(OS &stream) const; + + /** + * Determine an estimate for the + * memory consumption (in bytes) + * of this object. + */ + std::size_t memory_consumption () const; + + /** + * Read or write the data of this object to or + * from a stream for the purpose of serialization + */ + template + void serialize (Archive &ar, + const unsigned int version); + +private: + /** + * @brief The block structure + * of the global system. + */ + BlockIndices bi_global; + /** + * @brief The multilevel block structure. + */ + std::vector levels; + + /** + * @brief The block structure + * of the cell systems. + */ + BlockIndices bi_local; + + /** + * The base element associated + * with each block. + */ + std::vector base_elements; + + /** + * A vector containing the + * renumbering from the + * standard order of degrees of + * freedom on a cell to a + * component wise + * ordering. Filled by + * initialize(). + */ + std::vector local_renumbering; }; @@ -236,7 +236,7 @@ class BlockInfo : public Subscriptor //----------------------------------------------------------------------// inline -const BlockIndices& +const BlockIndices & BlockInfo::global() const { return bi_global; @@ -244,7 +244,7 @@ BlockInfo::global() const inline -const BlockIndices& +const BlockIndices & BlockInfo::local() const { return bi_local; @@ -252,7 +252,7 @@ BlockInfo::local() const inline -const BlockIndices& +const BlockIndices & BlockInfo::level (const unsigned int l) const { AssertIndexRange(l, levels.size()); @@ -290,10 +290,10 @@ BlockInfo::n_base_elements() const template inline void -BlockInfo::print (OS& os) const +BlockInfo::print (OS &os) const { os << "global dofs " << std::setw(5) << global().total_size() << " blocks"; - for (unsigned int i=0;i -void BlockInfo::serialize (Archive & ar, +void BlockInfo::serialize (Archive &ar, const unsigned int version) { - ar & bi_global; - ar & levels; - ar & bi_local; - ar & base_elements; - ar & local_renumbering; + ar &bi_global; + ar &levels; + ar &bi_local; + ar &base_elements; + ar &local_renumbering; } diff --git a/deal.II/include/deal.II/dofs/dof_accessor.h b/deal.II/include/deal.II/dofs/dof_accessor.h index 13d358b470..c87cdde900 100644 --- a/deal.II/include/deal.II/dofs/dof_accessor.h +++ b/deal.II/include/deal.II/dofs/dof_accessor.h @@ -67,49 +67,49 @@ namespace internal { namespace DoFAccessor { -/** - * This is a switch class which only declares a @p typedef. It is meant to - * determine which class a DoFAccessor class is to be derived from. By - * default, DoFAccessor@ derives from the - * typedef in the general - * Inheritance@ class, which is - * TriaAccessor@, but if - * structdim==dim, then the specialization - * Inheritance@ is used which declares - * its local type to be CellAccessor@. Therefore, the - * inheritance is automatically chosen to be from CellAccessor if the object - * under consideration has full dimension, i.e. constitutes a cell. - * - * @ingroup dofs - * @ingroup Accessors - * @author Wolfgang Bangerth, 1999 - */ + /** + * This is a switch class which only declares a @p typedef. It is meant to + * determine which class a DoFAccessor class is to be derived from. By + * default, DoFAccessor@ derives from the + * typedef in the general + * Inheritance@ class, which is + * TriaAccessor@, but if + * structdim==dim, then the specialization + * Inheritance@ is used which declares + * its local type to be CellAccessor@. Therefore, the + * inheritance is automatically chosen to be from CellAccessor if the object + * under consideration has full dimension, i.e. constitutes a cell. + * + * @ingroup dofs + * @ingroup Accessors + * @author Wolfgang Bangerth, 1999 + */ template struct Inheritance { - /** - * Declaration of the @p typedef. - * See the full documentation for - * more information. - */ - typedef dealii::TriaAccessor BaseClass; + /** + * Declaration of the @p typedef. + * See the full documentation for + * more information. + */ + typedef dealii::TriaAccessor BaseClass; }; -/** - * This is the specialization of the general template used for the case where - * an object has full dimension, i.e. is a cell. See the general template for - * more details. - */ + /** + * This is the specialization of the general template used for the case where + * an object has full dimension, i.e. is a cell. See the general template for + * more details. + */ template struct Inheritance { - /** - * Declaration of the @p typedef. - * See the full documentation for - * more information. - */ - typedef dealii::CellAccessor BaseClass; + /** + * Declaration of the @p typedef. + * See the full documentation for + * more information. + */ + typedef dealii::CellAccessor BaseClass; }; } } @@ -174,626 +174,626 @@ namespace internal template class DoFAccessor : public dealii::internal::DoFAccessor::Inheritance::BaseClass { - public: - - /** - * A static variable that allows users of - * this class to discover the value of - * the second template argument. - */ - static const unsigned int dimension=DH::dimension; - - /** - * A static variable that allows users of - * this class to discover the value of - * the third template argument. - */ - static const unsigned int space_dimension=DH::space_dimension; - - /** - * Declare a typedef to the base - * class to make accessing some - * of the exception classes - * simpler. - */ - typedef - typename dealii::internal::DoFAccessor::Inheritance::BaseClass - BaseClass; - - /** - * Data type passed by the iterator class. - */ - typedef DH AccessorData; - - /** - * @name Constructors - */ - /** - * @{ - */ - - /** - * Default constructor. Provides - * an accessor that can't be - * used. - */ - DoFAccessor (); - - /** - * Constructor - */ - DoFAccessor (const Triangulation *tria, - const int level, - const int index, - const DH *local_data); - - /** - * Conversion constructor. This - * constructor exists to make certain - * constructs simpler to write in - * dimension independent code. For - * example, it allows assigning a face - * iterator to a line iterator, an - * operation that is useful in 2d but - * doesn't make any sense in 3d. The - * constructor here exists for the - * purpose of making the code conform to - * C++ but it will unconditionally abort; - * in other words, assigning a face - * iterator to a line iterator is better - * put into an if-statement that checks - * that the dimension is two, and assign - * to a quad iterator in 3d (an operator - * that, without this constructor would - * be illegal if we happen to compile for - * 2d). - */ - template - DoFAccessor (const InvalidAccessor &); - - /** - * Another conversion operator - * between objects that don't - * make sense, just like the - * previous one. - */ - template - DoFAccessor (const DoFAccessor &); - - /** - * @} - */ - - /** - * Return a handle on the - * DoFHandler object which we - * are using. - */ - const DH & - get_dof_handler () const; - - /** - * Implement the copy operator needed - * for the iterator classes. - */ - void copy_from (const DoFAccessor &a); - - /** - * Copy operator used by the - * iterator class. Keeps the - * previously set dof handler, - * but sets the object - * coordinates of the TriaAccessor. - */ - void copy_from (const TriaAccessorBase &da); - - /** - * Return an iterator pointing to - * the the parent. - */ - TriaIterator > - parent () const; - - /** - * @name Accessing sub-objects - */ - /** - * @{ - */ - - /** - * Return an iterator pointing to - * the the @p c-th child. - */ - TriaIterator > - child (const unsigned int c) const; - - /** - * Pointer to the @p ith line - * bounding this object. If the - * current object is a line itself, - * then the only valid index is - * @p i equals to zero, and the - * function returns an iterator - * to itself. - */ - typename dealii::internal::DoFHandler::Iterators::line_iterator - line (const unsigned int i) const; - - /** - * Pointer to the @p ith quad - * bounding this object. If the - * current object is a quad itself, - * then the only valid index is - * @p i equals to zero, and the - * function returns an iterator - * to itself. - */ - typename dealii::internal::DoFHandler::Iterators::quad_iterator - quad (const unsigned int i) const; - - /** - * @} - */ - - /** - * @name Accessing the DoF indices of this object - */ - /** - * @{ - */ - - /** - * Return the indices of the dofs of this - * object in the standard ordering: dofs - * on vertex 0, dofs on vertex 1, etc, - * dofs on line 0, dofs on line 1, etc, - * dofs on quad 0, etc. - * - * The vector has to have the - * right size before being passed - * to this function. - * - * This function is most often - * used on active objects (edges, - * faces, cells). It can be used - * on non-active objects as well - * (i.e. objects that have - * children), but only if the - * finite element under - * consideration has degrees of - * freedom exclusively on - * vertices. Otherwise, the - * function doesn't make much - * sense, since for example - * inactive edges do not have - * degrees of freedom associated - * with them at all. - * - * The last argument denotes the - * finite element index. For the - * standard ::DoFHandler class, - * this value must be equal to - * its default value since that - * class only supports the same - * finite element on all cells - * anyway. - * - * However, for hp objects - * (i.e. the hp::DoFHandler - * class), different finite - * element objects may be used on - * different cells. On faces - * between two cells, as well as - * vertices, there may therefore - * be two sets of degrees of - * freedom, one for each of the - * finite elements used on the - * adjacent cells. In order to - * specify which set of degrees - * of freedom to work on, the - * last argument is used to - * disambiguate. Finally, if this - * function is called for a cell - * object, there can only be a - * single set of degrees of - * freedom, and fe_index has to - * match the result of - * active_fe_index(). - * - * For cells, there is only a - * single possible finite element - * index (namely the one for that - * cell, returned by - * cell-@>active_fe_index. Consequently, - * the derived DoFCellAccessor - * class has an overloaded - * version of this function that - * calls the present function - * with - * cell-@>active_fe_index - * as last argument. - */ - void get_dof_indices (std::vector &dof_indices, - const unsigned int fe_index = DH::default_fe_index) const; - - void get_mg_dof_indices (const int level, std::vector& dof_indices, const unsigned int fe_index = DH::default_fe_index) const; - - /** - * Global DoF index of the i - * degree associated with the @p vertexth - * vertex of the present cell. - * - * The last argument denotes the - * finite element index. For the - * standard ::DoFHandler class, - * this value must be equal to - * its default value since that - * class only supports the same - * finite element on all cells - * anyway. - * - * However, for hp objects - * (i.e. the hp::DoFHandler - * class), different finite - * element objects may be used on - * different cells. On faces - * between two cells, as well as - * vertices, there may therefore - * be two sets of degrees of - * freedom, one for each of the - * finite elements used on the - * adjacent cells. In order to - * specify which set of degrees - * of freedom to work on, the - * last argument is used to - * disambiguate. Finally, if this - * function is called for a cell - * object, there can only be a - * single set of degrees of - * freedom, and fe_index has to - * match the result of - * active_fe_index(). - */ - types::global_dof_index vertex_dof_index (const unsigned int vertex, - const unsigned int i, - const unsigned int fe_index = DH::default_fe_index) const; - - types::global_dof_index mg_vertex_dof_index (const int level, const unsigned int vertex, const unsigned int i, const unsigned int fe_index = DH::default_fe_index) const; - - /** - * Index of the ith degree - * of freedom of this object. - * - * The last argument denotes the - * finite element index. For the - * standard ::DoFHandler class, - * this value must be equal to - * its default value since that - * class only supports the same - * finite element on all cells - * anyway. - * - * However, for hp objects - * (i.e. the hp::DoFHandler - * class), different finite - * element objects may be used on - * different cells. On faces - * between two cells, as well as - * vertices, there may therefore - * be two sets of degrees of - * freedom, one for each of the - * finite elements used on the - * adjacent cells. In order to - * specify which set of degrees - * of freedom to work on, the - * last argument is used to - * disambiguate. Finally, if this - * function is called for a cell - * object, there can only be a - * single set of degrees of - * freedom, and fe_index has to - * match the result of - * active_fe_index(). - * - * @note While the get_dof_indices() - * function returns an array that - * contains the indices of all degrees of - * freedom that somehow live on this - * object (i.e. on the vertices, edges or - * interior of this object), the current - * dof_index() function only considers - * the DoFs that really belong to this - * particular object's interior. In other - * words, as an example, if the current - * object refers to a quad (a cell in 2d, - * a face in 3d) and the finite element - * associated with it is a bilinear one, - * then the get_dof_indices() will return - * an array of size 4 while dof_index() - * will produce an exception because no - * degrees are defined in the interior of - * the face. - */ - types::global_dof_index dof_index (const unsigned int i, - const unsigned int fe_index = DH::default_fe_index) const; - - types::global_dof_index mg_dof_index (const int level, const unsigned int i) const; - - /** - * @} - */ - - /** - * @name Accessing the finite element associated with this object - */ - /** - * @{ - */ - - /** - * Return the number of finite - * elements that are active on a - * given object. - * - * For non-hp DoFHandler objects, - * the answer is of course always - * one. However, for - * hp::DoFHandler objects, this - * isn't the case: If this is a - * cell, the answer is of course - * one. If it is a face, the - * answer may be one or two, - * depending on whether the two - * adjacent cells use the same - * finite element or not. If it - * is an edge in 3d, the possible - * return value may be one or any - * other value larger than that. - */ - unsigned int - n_active_fe_indices () const; - - /** - * Return the @p n-th active fe - * index on this object. For - * cells and all non-hp objects, - * there is only a single active - * fe index, so the argument must - * be equal to zero. For - * lower-dimensional hp objects, - * there are - * n_active_fe_indices() active - * finite elements, and this - * function can be queried for - * their indices. - */ - unsigned int - nth_active_fe_index (const unsigned int n) const; - - /** - * Return true if the finite - * element with given index is - * active on the present - * object. For non-hp DoF - * accessors, this is of course - * the case only if @p fe_index - * equals zero. For cells, it is - * the case if @p fe_index equals - * active_fe_index() of this - * cell. For faces and other - * lower-dimensional objects, - * there may be more than one @p - * fe_index that are active on - * any given object (see - * n_active_fe_indices()). - */ - bool - fe_index_is_active (const unsigned int fe_index) const; - - /** - * Return a reference to the finite - * element used on this object with the - * given @p fe_index. @p fe_index must be - * used on this object, - * i.e. fe_index_is_active(fe_index) - * must return true. - */ - const FiniteElement & - get_fe (const unsigned int fe_index) const; - - /** - * @} - */ - - /** - * Exceptions for child classes - * - * @ingroup Exceptions - */ - DeclException0 (ExcInvalidObject); - /** - * Exception - * - * @ingroup Exceptions - */ - DeclException0 (ExcVectorNotEmpty); - /** - * Exception - * - * @ingroup Exceptions - */ - DeclException0 (ExcVectorDoesNotMatch); - /** - * Exception - * - * @ingroup Exceptions - */ - DeclException0 (ExcMatrixDoesNotMatch); - /** - * A function has been called for - * a cell which should be active, - * but is refined. @ref GlossActive - * - * @ingroup Exceptions - */ - DeclException0 (ExcNotActive); - /** - * Exception - * - * @ingroup Exceptions - */ - DeclException0 (ExcCantCompareIterators); - - protected: - - /** - * Store the address of the DoFHandler object - * to be accessed. - */ - DH *dof_handler; - - /** - * Compare for equality. - */ - bool operator == (const DoFAccessor &) const; - - /** - * Compare for inequality. - */ - bool operator != (const DoFAccessor &) const; - - /** - * Reset the DoF handler pointer. - */ - void set_dof_handler (DH *dh); - - /** - * Set the index of the - * ith degree of freedom - * of this object to @p index. - * - * The last argument denotes the - * finite element index. For the - * standard ::DoFHandler class, - * this value must be equal to - * its default value since that - * class only supports the same - * finite element on all cells - * anyway. - * - * However, for hp objects - * (i.e. the hp::DoFHandler - * class), different finite - * element objects may be used on - * different cells. On faces - * between two cells, as well as - * vertices, there may therefore - * be two sets of degrees of - * freedom, one for each of the - * finite elements used on the - * adjacent cells. In order to - * specify which set of degrees - * of freedom to work on, the - * last argument is used to - * disambiguate. Finally, if this - * function is called for a cell - * object, there can only be a - * single set of degrees of - * freedom, and fe_index has to - * match the result of - * active_fe_index(). - */ - void set_dof_index (const unsigned int i, - const types::global_dof_index index, - const unsigned int fe_index = DH::default_fe_index) const; - - void set_mg_dof_index (const int level, const unsigned int i, const types::global_dof_index index) const; - - /** - * Set the global index of the i - * degree on the @p vertex-th vertex of - * the present cell to @p index. - * - * The last argument denotes the - * finite element index. For the - * standard ::DoFHandler class, - * this value must be equal to - * its default value since that - * class only supports the same - * finite element on all cells - * anyway. - * - * However, for hp objects - * (i.e. the hp::DoFHandler - * class), different finite - * element objects may be used on - * different cells. On faces - * between two cells, as well as - * vertices, there may therefore - * be two sets of degrees of - * freedom, one for each of the - * finite elements used on the - * adjacent cells. In order to - * specify which set of degrees - * of freedom to work on, the - * last argument is used to - * disambiguate. Finally, if this - * function is called for a cell - * object, there can only be a - * single set of degrees of - * freedom, and fe_index has to - * match the result of - * active_fe_index(). - */ - void set_vertex_dof_index (const unsigned int vertex, - const unsigned int i, - const types::global_dof_index index, - const unsigned int fe_index = DH::default_fe_index) const; - - void set_mg_vertex_dof_index (const int level, const unsigned int vertex, const unsigned int i, const types::global_dof_index index, const unsigned int fe_index = DH::default_fe_index) const; - - /** - * Iterator classes need to be friends - * because they need to access operator== - * and operator!=. - */ - template friend class TriaRawIterator; - - - private: - /** - * Copy operator. This is normally used - * in a context like iterator a,b; - * *a=*b;. Presumably, the intent - * here is to copy the object pointed to - * by @p b to the object pointed to by - * @p a. However, the result of - * dereferencing an iterator is not an - * object but an accessor; consequently, - * this operation is not useful for - * iterators on triangulations. We - * declare this function here private, - * thus it may not be used from outside. - * Furthermore it is not implemented and - * will give a linker error if used - * anyway. - */ - DoFAccessor & - operator = (const DoFAccessor &da); - - /** - * Make the DoFHandler class a friend so - * that it can call the set_xxx() - * functions. - */ - template friend class DoFHandler; - template friend class hp::DoFHandler; - - friend struct dealii::internal::DoFHandler::Policy::Implementation; - friend struct dealii::internal::DoFHandler::Implementation; - friend struct dealii::internal::hp::DoFHandler::Implementation; - friend struct dealii::internal::DoFCellAccessor::Implementation; +public: + + /** + * A static variable that allows users of + * this class to discover the value of + * the second template argument. + */ + static const unsigned int dimension=DH::dimension; + + /** + * A static variable that allows users of + * this class to discover the value of + * the third template argument. + */ + static const unsigned int space_dimension=DH::space_dimension; + + /** + * Declare a typedef to the base + * class to make accessing some + * of the exception classes + * simpler. + */ + typedef + typename dealii::internal::DoFAccessor::Inheritance::BaseClass + BaseClass; + + /** + * Data type passed by the iterator class. + */ + typedef DH AccessorData; + + /** + * @name Constructors + */ + /** + * @{ + */ + + /** + * Default constructor. Provides + * an accessor that can't be + * used. + */ + DoFAccessor (); + + /** + * Constructor + */ + DoFAccessor (const Triangulation *tria, + const int level, + const int index, + const DH *local_data); + + /** + * Conversion constructor. This + * constructor exists to make certain + * constructs simpler to write in + * dimension independent code. For + * example, it allows assigning a face + * iterator to a line iterator, an + * operation that is useful in 2d but + * doesn't make any sense in 3d. The + * constructor here exists for the + * purpose of making the code conform to + * C++ but it will unconditionally abort; + * in other words, assigning a face + * iterator to a line iterator is better + * put into an if-statement that checks + * that the dimension is two, and assign + * to a quad iterator in 3d (an operator + * that, without this constructor would + * be illegal if we happen to compile for + * 2d). + */ + template + DoFAccessor (const InvalidAccessor &); + + /** + * Another conversion operator + * between objects that don't + * make sense, just like the + * previous one. + */ + template + DoFAccessor (const DoFAccessor &); + + /** + * @} + */ + + /** + * Return a handle on the + * DoFHandler object which we + * are using. + */ + const DH & + get_dof_handler () const; + + /** + * Implement the copy operator needed + * for the iterator classes. + */ + void copy_from (const DoFAccessor &a); + + /** + * Copy operator used by the + * iterator class. Keeps the + * previously set dof handler, + * but sets the object + * coordinates of the TriaAccessor. + */ + void copy_from (const TriaAccessorBase &da); + + /** + * Return an iterator pointing to + * the the parent. + */ + TriaIterator > + parent () const; + + /** + * @name Accessing sub-objects + */ + /** + * @{ + */ + + /** + * Return an iterator pointing to + * the the @p c-th child. + */ + TriaIterator > + child (const unsigned int c) const; + + /** + * Pointer to the @p ith line + * bounding this object. If the + * current object is a line itself, + * then the only valid index is + * @p i equals to zero, and the + * function returns an iterator + * to itself. + */ + typename dealii::internal::DoFHandler::Iterators::line_iterator + line (const unsigned int i) const; + + /** + * Pointer to the @p ith quad + * bounding this object. If the + * current object is a quad itself, + * then the only valid index is + * @p i equals to zero, and the + * function returns an iterator + * to itself. + */ + typename dealii::internal::DoFHandler::Iterators::quad_iterator + quad (const unsigned int i) const; + + /** + * @} + */ + + /** + * @name Accessing the DoF indices of this object + */ + /** + * @{ + */ + + /** + * Return the indices of the dofs of this + * object in the standard ordering: dofs + * on vertex 0, dofs on vertex 1, etc, + * dofs on line 0, dofs on line 1, etc, + * dofs on quad 0, etc. + * + * The vector has to have the + * right size before being passed + * to this function. + * + * This function is most often + * used on active objects (edges, + * faces, cells). It can be used + * on non-active objects as well + * (i.e. objects that have + * children), but only if the + * finite element under + * consideration has degrees of + * freedom exclusively on + * vertices. Otherwise, the + * function doesn't make much + * sense, since for example + * inactive edges do not have + * degrees of freedom associated + * with them at all. + * + * The last argument denotes the + * finite element index. For the + * standard ::DoFHandler class, + * this value must be equal to + * its default value since that + * class only supports the same + * finite element on all cells + * anyway. + * + * However, for hp objects + * (i.e. the hp::DoFHandler + * class), different finite + * element objects may be used on + * different cells. On faces + * between two cells, as well as + * vertices, there may therefore + * be two sets of degrees of + * freedom, one for each of the + * finite elements used on the + * adjacent cells. In order to + * specify which set of degrees + * of freedom to work on, the + * last argument is used to + * disambiguate. Finally, if this + * function is called for a cell + * object, there can only be a + * single set of degrees of + * freedom, and fe_index has to + * match the result of + * active_fe_index(). + * + * For cells, there is only a + * single possible finite element + * index (namely the one for that + * cell, returned by + * cell-@>active_fe_index. Consequently, + * the derived DoFCellAccessor + * class has an overloaded + * version of this function that + * calls the present function + * with + * cell-@>active_fe_index + * as last argument. + */ + void get_dof_indices (std::vector &dof_indices, + const unsigned int fe_index = DH::default_fe_index) const; + + void get_mg_dof_indices (const int level, std::vector &dof_indices, const unsigned int fe_index = DH::default_fe_index) const; + + /** + * Global DoF index of the i + * degree associated with the @p vertexth + * vertex of the present cell. + * + * The last argument denotes the + * finite element index. For the + * standard ::DoFHandler class, + * this value must be equal to + * its default value since that + * class only supports the same + * finite element on all cells + * anyway. + * + * However, for hp objects + * (i.e. the hp::DoFHandler + * class), different finite + * element objects may be used on + * different cells. On faces + * between two cells, as well as + * vertices, there may therefore + * be two sets of degrees of + * freedom, one for each of the + * finite elements used on the + * adjacent cells. In order to + * specify which set of degrees + * of freedom to work on, the + * last argument is used to + * disambiguate. Finally, if this + * function is called for a cell + * object, there can only be a + * single set of degrees of + * freedom, and fe_index has to + * match the result of + * active_fe_index(). + */ + types::global_dof_index vertex_dof_index (const unsigned int vertex, + const unsigned int i, + const unsigned int fe_index = DH::default_fe_index) const; + + types::global_dof_index mg_vertex_dof_index (const int level, const unsigned int vertex, const unsigned int i, const unsigned int fe_index = DH::default_fe_index) const; + + /** + * Index of the ith degree + * of freedom of this object. + * + * The last argument denotes the + * finite element index. For the + * standard ::DoFHandler class, + * this value must be equal to + * its default value since that + * class only supports the same + * finite element on all cells + * anyway. + * + * However, for hp objects + * (i.e. the hp::DoFHandler + * class), different finite + * element objects may be used on + * different cells. On faces + * between two cells, as well as + * vertices, there may therefore + * be two sets of degrees of + * freedom, one for each of the + * finite elements used on the + * adjacent cells. In order to + * specify which set of degrees + * of freedom to work on, the + * last argument is used to + * disambiguate. Finally, if this + * function is called for a cell + * object, there can only be a + * single set of degrees of + * freedom, and fe_index has to + * match the result of + * active_fe_index(). + * + * @note While the get_dof_indices() + * function returns an array that + * contains the indices of all degrees of + * freedom that somehow live on this + * object (i.e. on the vertices, edges or + * interior of this object), the current + * dof_index() function only considers + * the DoFs that really belong to this + * particular object's interior. In other + * words, as an example, if the current + * object refers to a quad (a cell in 2d, + * a face in 3d) and the finite element + * associated with it is a bilinear one, + * then the get_dof_indices() will return + * an array of size 4 while dof_index() + * will produce an exception because no + * degrees are defined in the interior of + * the face. + */ + types::global_dof_index dof_index (const unsigned int i, + const unsigned int fe_index = DH::default_fe_index) const; + + types::global_dof_index mg_dof_index (const int level, const unsigned int i) const; + + /** + * @} + */ + + /** + * @name Accessing the finite element associated with this object + */ + /** + * @{ + */ + + /** + * Return the number of finite + * elements that are active on a + * given object. + * + * For non-hp DoFHandler objects, + * the answer is of course always + * one. However, for + * hp::DoFHandler objects, this + * isn't the case: If this is a + * cell, the answer is of course + * one. If it is a face, the + * answer may be one or two, + * depending on whether the two + * adjacent cells use the same + * finite element or not. If it + * is an edge in 3d, the possible + * return value may be one or any + * other value larger than that. + */ + unsigned int + n_active_fe_indices () const; + + /** + * Return the @p n-th active fe + * index on this object. For + * cells and all non-hp objects, + * there is only a single active + * fe index, so the argument must + * be equal to zero. For + * lower-dimensional hp objects, + * there are + * n_active_fe_indices() active + * finite elements, and this + * function can be queried for + * their indices. + */ + unsigned int + nth_active_fe_index (const unsigned int n) const; + + /** + * Return true if the finite + * element with given index is + * active on the present + * object. For non-hp DoF + * accessors, this is of course + * the case only if @p fe_index + * equals zero. For cells, it is + * the case if @p fe_index equals + * active_fe_index() of this + * cell. For faces and other + * lower-dimensional objects, + * there may be more than one @p + * fe_index that are active on + * any given object (see + * n_active_fe_indices()). + */ + bool + fe_index_is_active (const unsigned int fe_index) const; + + /** + * Return a reference to the finite + * element used on this object with the + * given @p fe_index. @p fe_index must be + * used on this object, + * i.e. fe_index_is_active(fe_index) + * must return true. + */ + const FiniteElement & + get_fe (const unsigned int fe_index) const; + + /** + * @} + */ + + /** + * Exceptions for child classes + * + * @ingroup Exceptions + */ + DeclException0 (ExcInvalidObject); + /** + * Exception + * + * @ingroup Exceptions + */ + DeclException0 (ExcVectorNotEmpty); + /** + * Exception + * + * @ingroup Exceptions + */ + DeclException0 (ExcVectorDoesNotMatch); + /** + * Exception + * + * @ingroup Exceptions + */ + DeclException0 (ExcMatrixDoesNotMatch); + /** + * A function has been called for + * a cell which should be active, + * but is refined. @ref GlossActive + * + * @ingroup Exceptions + */ + DeclException0 (ExcNotActive); + /** + * Exception + * + * @ingroup Exceptions + */ + DeclException0 (ExcCantCompareIterators); + +protected: + + /** + * Store the address of the DoFHandler object + * to be accessed. + */ + DH *dof_handler; + + /** + * Compare for equality. + */ + bool operator == (const DoFAccessor &) const; + + /** + * Compare for inequality. + */ + bool operator != (const DoFAccessor &) const; + + /** + * Reset the DoF handler pointer. + */ + void set_dof_handler (DH *dh); + + /** + * Set the index of the + * ith degree of freedom + * of this object to @p index. + * + * The last argument denotes the + * finite element index. For the + * standard ::DoFHandler class, + * this value must be equal to + * its default value since that + * class only supports the same + * finite element on all cells + * anyway. + * + * However, for hp objects + * (i.e. the hp::DoFHandler + * class), different finite + * element objects may be used on + * different cells. On faces + * between two cells, as well as + * vertices, there may therefore + * be two sets of degrees of + * freedom, one for each of the + * finite elements used on the + * adjacent cells. In order to + * specify which set of degrees + * of freedom to work on, the + * last argument is used to + * disambiguate. Finally, if this + * function is called for a cell + * object, there can only be a + * single set of degrees of + * freedom, and fe_index has to + * match the result of + * active_fe_index(). + */ + void set_dof_index (const unsigned int i, + const types::global_dof_index index, + const unsigned int fe_index = DH::default_fe_index) const; + + void set_mg_dof_index (const int level, const unsigned int i, const types::global_dof_index index) const; + + /** + * Set the global index of the i + * degree on the @p vertex-th vertex of + * the present cell to @p index. + * + * The last argument denotes the + * finite element index. For the + * standard ::DoFHandler class, + * this value must be equal to + * its default value since that + * class only supports the same + * finite element on all cells + * anyway. + * + * However, for hp objects + * (i.e. the hp::DoFHandler + * class), different finite + * element objects may be used on + * different cells. On faces + * between two cells, as well as + * vertices, there may therefore + * be two sets of degrees of + * freedom, one for each of the + * finite elements used on the + * adjacent cells. In order to + * specify which set of degrees + * of freedom to work on, the + * last argument is used to + * disambiguate. Finally, if this + * function is called for a cell + * object, there can only be a + * single set of degrees of + * freedom, and fe_index has to + * match the result of + * active_fe_index(). + */ + void set_vertex_dof_index (const unsigned int vertex, + const unsigned int i, + const types::global_dof_index index, + const unsigned int fe_index = DH::default_fe_index) const; + + void set_mg_vertex_dof_index (const int level, const unsigned int vertex, const unsigned int i, const types::global_dof_index index, const unsigned int fe_index = DH::default_fe_index) const; + + /** + * Iterator classes need to be friends + * because they need to access operator== + * and operator!=. + */ + template friend class TriaRawIterator; + + +private: + /** + * Copy operator. This is normally used + * in a context like iterator a,b; + * *a=*b;. Presumably, the intent + * here is to copy the object pointed to + * by @p b to the object pointed to by + * @p a. However, the result of + * dereferencing an iterator is not an + * object but an accessor; consequently, + * this operation is not useful for + * iterators on triangulations. We + * declare this function here private, + * thus it may not be used from outside. + * Furthermore it is not implemented and + * will give a linker error if used + * anyway. + */ + DoFAccessor & + operator = (const DoFAccessor &da); + + /** + * Make the DoFHandler class a friend so + * that it can call the set_xxx() + * functions. + */ + template friend class DoFHandler; + template friend class hp::DoFHandler; + + friend struct dealii::internal::DoFHandler::Policy::Implementation; + friend struct dealii::internal::DoFHandler::Implementation; + friend struct dealii::internal::hp::DoFHandler::Implementation; + friend struct dealii::internal::DoFCellAccessor::Implementation; }; @@ -811,642 +811,642 @@ class DoFAccessor : public dealii::internal::DoFAccessor::Inheritance class DH, int spacedim> class DoFAccessor<0,DH<1,spacedim> > : public TriaAccessor<0,1,spacedim> { - public: - - /** - * A static variable that allows users of - * this class to discover the value of - * the second template argument. - */ - static const unsigned int dimension=1; - - /** - * A static variable that allows users of - * this class to discover the value of - * the third template argument. - */ - static const unsigned int space_dimension=spacedim; - - /** - * Declare a typedef to the base - * class to make accessing some - * of the exception classes - * simpler. - */ - typedef TriaAccessor<0,1,spacedim> BaseClass; - - /** - * Data type passed by the iterator class. - */ - typedef DH<1,spacedim> AccessorData; - - /** - * @name Constructors - */ - /** - * @{ - */ - - /** - * Default constructor. Provides - * an accessor that can't be - * used. - */ - DoFAccessor (); - - /** - * Constructor to be used if the - * object here refers to a vertex - * of a one-dimensional - * triangulation, i.e. a face of - * the triangulation. - * - * Since there is no mapping from - * vertices to cells, an accessor - * object for a point has no way - * to figure out whether it is at - * the boundary of the domain or - * not. Consequently, the second - * argument must be passed by the - * object that generates this - * accessor -- e.g. a 1d cell - * that can figure out whether - * its left or right vertex are - * at the boundary. - * - * The third argument is the - * global index of the vertex we - * point to. - * - * The fourth argument is a - * pointer to the DoFHandler - * object. - * - * This iterator can only be - * called for one-dimensional - * triangulations. - */ - DoFAccessor (const Triangulation<1,spacedim> * tria, - const typename TriaAccessor<0,1,spacedim>::VertexKind vertex_kind, - const unsigned int vertex_index, - const DH<1,spacedim> * dof_handler); - - /** - * Constructor. This constructor - * exists in order to maintain - * interface compatibility with - * the other accessor - * classes. However, it doesn't - * do anything useful here and so - * may not actually be called. - */ - DoFAccessor (const Triangulation<1,spacedim> *, - const int = 0, - const int = 0, - const DH<1,spacedim> * = 0); - - /** - * Conversion constructor. This - * constructor exists to make certain - * constructs simpler to write in - * dimension independent code. For - * example, it allows assigning a face - * iterator to a line iterator, an - * operation that is useful in 2d but - * doesn't make any sense in 3d. The - * constructor here exists for the - * purpose of making the code conform to - * C++ but it will unconditionally abort; - * in other words, assigning a face - * iterator to a line iterator is better - * put into an if-statement that checks - * that the dimension is two, and assign - * to a quad iterator in 3d (an operator - * that, without this constructor would - * be illegal if we happen to compile for - * 2d). - */ - template - DoFAccessor (const InvalidAccessor &); - - /** - * Another conversion operator - * between objects that don't - * make sense, just like the - * previous one. - */ - template - DoFAccessor (const DoFAccessor &); - - /** - * @} - */ - - /** - * Return a handle on the - * DoFHandler object which we - * are using. - */ - const DH<1,spacedim> & - get_dof_handler () const; - - /** - * Copy operator. - */ - DoFAccessor<0,DH<1,spacedim> > & - operator = (const DoFAccessor<0,DH<1,spacedim> > &da); - - /** - * Implement the copy operator needed - * for the iterator classes. - */ - void copy_from (const DoFAccessor<0, DH<1,spacedim> > &a); - - /** - * Copy operator used by the - * iterator class. Keeps the - * previously set dof handler, - * but sets the object - * coordinates of the TriaAccessor. - */ - void copy_from (const TriaAccessorBase<0, 1, spacedim> &da); - - /** - * Return an iterator pointing to - * the the parent. - */ - TriaIterator > > - parent () const; - - /** - * @name Accessing sub-objects - */ - /** - * @{ - */ - - /** - * Return an iterator pointing to - * the the @p c-th child. - */ - TriaIterator > > - child (const unsigned int c) const; - - /** - * Pointer to the @p ith line - * bounding this object. If the - * current object is a line itself, - * then the only valid index is - * @p i equals to zero, and the - * function returns an iterator - * to itself. - */ - typename dealii::internal::DoFHandler::Iterators >::line_iterator - line (const unsigned int i) const; - - /** - * Pointer to the @p ith quad - * bounding this object. If the - * current object is a quad itself, - * then the only valid index is - * @p i equals to zero, and the - * function returns an iterator - * to itself. - */ - typename dealii::internal::DoFHandler::Iterators >::quad_iterator - quad (const unsigned int i) const; - - /** - * @} - */ - - /** - * @name Accessing the DoF indices of this object - */ - /** - * @{ - */ - - /** - * Return the indices of the dofs of this - * object in the standard ordering: dofs - * on vertex 0, dofs on vertex 1, etc, - * dofs on line 0, dofs on line 1, etc, - * dofs on quad 0, etc. - * - * The vector has to have the - * right size before being passed - * to this function. - * - * This function is most often - * used on active objects (edges, - * faces, cells). It can be used - * on non-active objects as well - * (i.e. objects that have - * children), but only if the - * finite element under - * consideration has degrees of - * freedom exclusively on - * vertices. Otherwise, the - * function doesn't make much - * sense, since for example - * inactive edges do not have - * degrees of freedom associated - * with them at all. - * - * The last argument denotes the - * finite element index. For the - * standard ::DoFHandler class, - * this value must be equal to - * its default value since that - * class only supports the same - * finite element on all cells - * anyway. - * - * However, for hp objects - * (i.e. the hp::DoFHandler - * class), different finite - * element objects may be used on - * different cells. On faces - * between two cells, as well as - * vertices, there may therefore - * be two sets of degrees of - * freedom, one for each of the - * finite elements used on the - * adjacent cells. In order to - * specify which set of degrees - * of freedom to work on, the - * last argument is used to - * disambiguate. Finally, if this - * function is called for a cell - * object, there can only be a - * single set of degrees of - * freedom, and fe_index has to - * match the result of - * active_fe_index(). - * - * For cells, there is only a - * single possible finite element - * index (namely the one for that - * cell, returned by - * cell-@>active_fe_index. Consequently, - * the derived DoFCellAccessor - * class has an overloaded - * version of this function that - * calls the present function - * with - * cell-@>active_fe_index - * as last argument. - */ - void get_dof_indices (std::vector &dof_indices, - const unsigned int fe_index = AccessorData::default_fe_index) const; - - /** - * Global DoF index of the i - * degree associated with the @p vertexth - * vertex of the present cell. - * - * The last argument denotes the - * finite element index. For the - * standard ::DoFHandler class, - * this value must be equal to - * its default value since that - * class only supports the same - * finite element on all cells - * anyway. - * - * However, for hp objects - * (i.e. the hp::DoFHandler - * class), different finite - * element objects may be used on - * different cells. On faces - * between two cells, as well as - * vertices, there may therefore - * be two sets of degrees of - * freedom, one for each of the - * finite elements used on the - * adjacent cells. In order to - * specify which set of degrees - * of freedom to work on, the - * last argument is used to - * disambiguate. Finally, if this - * function is called for a cell - * object, there can only be a - * single set of degrees of - * freedom, and fe_index has to - * match the result of - * active_fe_index(). - */ - types::global_dof_index vertex_dof_index (const unsigned int vertex, - const unsigned int i, - const unsigned int fe_index = AccessorData::default_fe_index) const; - - /** - * Index of the ith degree - * of freedom of this object. - * - * The last argument denotes the - * finite element index. For the - * standard ::DoFHandler class, - * this value must be equal to - * its default value since that - * class only supports the same - * finite element on all cells - * anyway. - * - * However, for hp objects - * (i.e. the hp::DoFHandler - * class), different finite - * element objects may be used on - * different cells. On faces - * between two cells, as well as - * vertices, there may therefore - * be two sets of degrees of - * freedom, one for each of the - * finite elements used on the - * adjacent cells. In order to - * specify which set of degrees - * of freedom to work on, the - * last argument is used to - * disambiguate. Finally, if this - * function is called for a cell - * object, there can only be a - * single set of degrees of - * freedom, and fe_index has to - * match the result of - * active_fe_index(). - * - * @note While the get_dof_indices() - * function returns an array that - * contains the indices of all degrees of - * freedom that somehow live on this - * object (i.e. on the vertices, edges or - * interior of this object), the current - * dof_index() function only considers - * the DoFs that really belong to this - * particular object's interior. In other - * words, as an example, if the current - * object refers to a quad (a cell in 2d, - * a face in 3d) and the finite element - * associated with it is a bilinear one, - * then the get_dof_indices() will return - * an array of size 4 while dof_index() - * will produce an exception because no - * degrees are defined in the interior of - * the face. - */ - types::global_dof_index dof_index (const unsigned int i, - const unsigned int fe_index = AccessorData::default_fe_index) const; - - /** - * @} - */ - - /** - * @name Accessing the finite element associated with this object - */ - /** - * @{ - */ - - /** - * Return the number of finite - * elements that are active on a - * given object. - * - * For non-hp DoFHandler objects, - * the answer is of course always - * one. However, for - * hp::DoFHandler objects, this - * isn't the case: If this is a - * cell, the answer is of course - * one. If it is a face, the - * answer may be one or two, - * depending on whether the two - * adjacent cells use the same - * finite element or not. If it - * is an edge in 3d, the possible - * return value may be one or any - * other value larger than that. - */ - unsigned int - n_active_fe_indices () const; - - /** - * Return the @p n-th active fe - * index on this object. For - * cells and all non-hp objects, - * there is only a single active - * fe index, so the argument must - * be equal to zero. For - * lower-dimensional hp objects, - * there are - * n_active_fe_indices() active - * finite elements, and this - * function can be queried for - * their indices. - */ - unsigned int - nth_active_fe_index (const unsigned int n) const; - - /** - * Return true if the finite - * element with given index is - * active on the present - * object. For non-hp DoF - * accessors, this is of course - * the case only if @p fe_index - * equals zero. For cells, it is - * the case if @p fe_index equals - * active_fe_index() of this - * cell. For faces and other - * lower-dimensional objects, - * there may be more than one @p - * fe_index that are active on - * any given object (see - * n_active_fe_indices()). - */ - bool - fe_index_is_active (const unsigned int fe_index) const; - - /** - * Return a reference to the finite - * element used on this object with the - * given @p fe_index. @p fe_index must be - * used on this object, - * i.e. fe_index_is_active(fe_index) - * must return true. - */ - const FiniteElement::dimension,DH<1,spacedim>::space_dimension> & - get_fe (const unsigned int fe_index) const; - - /** - * @} - */ - - /** - * Exceptions for child classes - * - * @ingroup Exceptions - */ - DeclException0 (ExcInvalidObject); - /** - * Exception - * - * @ingroup Exceptions - */ - DeclException0 (ExcVectorNotEmpty); - /** - * Exception - * - * @ingroup Exceptions - */ - DeclException0 (ExcVectorDoesNotMatch); - /** - * Exception - * - * @ingroup Exceptions - */ - DeclException0 (ExcMatrixDoesNotMatch); - /** - * A function has been called for - * a cell which should be active, - * but is refined. @ref GlossActive - * - * @ingroup Exceptions - */ - DeclException0 (ExcNotActive); - /** - * Exception - * - * @ingroup Exceptions - */ - DeclException0 (ExcCantCompareIterators); - - protected: - - /** - * Store the address of the DoFHandler object - * to be accessed. - */ - DH<1,spacedim> *dof_handler; - - /** - * Compare for equality. - */ - bool operator == (const DoFAccessor &) const; - - /** - * Compare for inequality. - */ - bool operator != (const DoFAccessor &) const; - - /** - * Reset the DoF handler pointer. - */ - void set_dof_handler (DH<1,spacedim> *dh); - - /** - * Set the index of the - * ith degree of freedom - * of this object to @p index. - * - * The last argument denotes the - * finite element index. For the - * standard ::DoFHandler class, - * this value must be equal to - * its default value since that - * class only supports the same - * finite element on all cells - * anyway. - * - * However, for hp objects - * (i.e. the hp::DoFHandler - * class), different finite - * element objects may be used on - * different cells. On faces - * between two cells, as well as - * vertices, there may therefore - * be two sets of degrees of - * freedom, one for each of the - * finite elements used on the - * adjacent cells. In order to - * specify which set of degrees - * of freedom to work on, the - * last argument is used to - * disambiguate. Finally, if this - * function is called for a cell - * object, there can only be a - * single set of degrees of - * freedom, and fe_index has to - * match the result of - * active_fe_index(). - */ - void set_dof_index (const unsigned int i, - const types::global_dof_index index, - const unsigned int fe_index = AccessorData::default_fe_index) const; - - /** - * Set the global index of the i - * degree on the @p vertex-th vertex of - * the present cell to @p index. - * - * The last argument denotes the - * finite element index. For the - * standard ::DoFHandler class, - * this value must be equal to - * its default value since that - * class only supports the same - * finite element on all cells - * anyway. - * - * However, for hp objects - * (i.e. the hp::DoFHandler - * class), different finite - * element objects may be used on - * different cells. On faces - * between two cells, as well as - * vertices, there may therefore - * be two sets of degrees of - * freedom, one for each of the - * finite elements used on the - * adjacent cells. In order to - * specify which set of degrees - * of freedom to work on, the - * last argument is used to - * disambiguate. Finally, if this - * function is called for a cell - * object, there can only be a - * single set of degrees of - * freedom, and fe_index has to - * match the result of - * active_fe_index(). - */ - void set_vertex_dof_index (const unsigned int vertex, - const unsigned int i, - const types::global_dof_index index, - const unsigned int fe_index = AccessorData::default_fe_index) const; - - /** - * Iterator classes need to be friends - * because they need to access operator== - * and operator!=. - */ - template friend class TriaRawIterator; - - - /** - * Make the DoFHandler class a friend so - * that it can call the set_xxx() - * functions. - */ - template friend class DoFHandler; - template friend class hp::DoFHandler; - - friend struct dealii::internal::DoFHandler::Policy::Implementation; - friend struct dealii::internal::DoFHandler::Implementation; - friend struct dealii::internal::hp::DoFHandler::Implementation; - friend struct dealii::internal::DoFCellAccessor::Implementation; +public: + + /** + * A static variable that allows users of + * this class to discover the value of + * the second template argument. + */ + static const unsigned int dimension=1; + + /** + * A static variable that allows users of + * this class to discover the value of + * the third template argument. + */ + static const unsigned int space_dimension=spacedim; + + /** + * Declare a typedef to the base + * class to make accessing some + * of the exception classes + * simpler. + */ + typedef TriaAccessor<0,1,spacedim> BaseClass; + + /** + * Data type passed by the iterator class. + */ + typedef DH<1,spacedim> AccessorData; + + /** + * @name Constructors + */ + /** + * @{ + */ + + /** + * Default constructor. Provides + * an accessor that can't be + * used. + */ + DoFAccessor (); + + /** + * Constructor to be used if the + * object here refers to a vertex + * of a one-dimensional + * triangulation, i.e. a face of + * the triangulation. + * + * Since there is no mapping from + * vertices to cells, an accessor + * object for a point has no way + * to figure out whether it is at + * the boundary of the domain or + * not. Consequently, the second + * argument must be passed by the + * object that generates this + * accessor -- e.g. a 1d cell + * that can figure out whether + * its left or right vertex are + * at the boundary. + * + * The third argument is the + * global index of the vertex we + * point to. + * + * The fourth argument is a + * pointer to the DoFHandler + * object. + * + * This iterator can only be + * called for one-dimensional + * triangulations. + */ + DoFAccessor (const Triangulation<1,spacedim> *tria, + const typename TriaAccessor<0,1,spacedim>::VertexKind vertex_kind, + const unsigned int vertex_index, + const DH<1,spacedim> *dof_handler); + + /** + * Constructor. This constructor + * exists in order to maintain + * interface compatibility with + * the other accessor + * classes. However, it doesn't + * do anything useful here and so + * may not actually be called. + */ + DoFAccessor (const Triangulation<1,spacedim> *, + const int = 0, + const int = 0, + const DH<1,spacedim> *= 0); + + /** + * Conversion constructor. This + * constructor exists to make certain + * constructs simpler to write in + * dimension independent code. For + * example, it allows assigning a face + * iterator to a line iterator, an + * operation that is useful in 2d but + * doesn't make any sense in 3d. The + * constructor here exists for the + * purpose of making the code conform to + * C++ but it will unconditionally abort; + * in other words, assigning a face + * iterator to a line iterator is better + * put into an if-statement that checks + * that the dimension is two, and assign + * to a quad iterator in 3d (an operator + * that, without this constructor would + * be illegal if we happen to compile for + * 2d). + */ + template + DoFAccessor (const InvalidAccessor &); + + /** + * Another conversion operator + * between objects that don't + * make sense, just like the + * previous one. + */ + template + DoFAccessor (const DoFAccessor &); + + /** + * @} + */ + + /** + * Return a handle on the + * DoFHandler object which we + * are using. + */ + const DH<1,spacedim> & + get_dof_handler () const; + + /** + * Copy operator. + */ + DoFAccessor<0,DH<1,spacedim> > & + operator = (const DoFAccessor<0,DH<1,spacedim> > &da); + + /** + * Implement the copy operator needed + * for the iterator classes. + */ + void copy_from (const DoFAccessor<0, DH<1,spacedim> > &a); + + /** + * Copy operator used by the + * iterator class. Keeps the + * previously set dof handler, + * but sets the object + * coordinates of the TriaAccessor. + */ + void copy_from (const TriaAccessorBase<0, 1, spacedim> &da); + + /** + * Return an iterator pointing to + * the the parent. + */ + TriaIterator > > + parent () const; + + /** + * @name Accessing sub-objects + */ + /** + * @{ + */ + + /** + * Return an iterator pointing to + * the the @p c-th child. + */ + TriaIterator > > + child (const unsigned int c) const; + + /** + * Pointer to the @p ith line + * bounding this object. If the + * current object is a line itself, + * then the only valid index is + * @p i equals to zero, and the + * function returns an iterator + * to itself. + */ + typename dealii::internal::DoFHandler::Iterators >::line_iterator + line (const unsigned int i) const; + + /** + * Pointer to the @p ith quad + * bounding this object. If the + * current object is a quad itself, + * then the only valid index is + * @p i equals to zero, and the + * function returns an iterator + * to itself. + */ + typename dealii::internal::DoFHandler::Iterators >::quad_iterator + quad (const unsigned int i) const; + + /** + * @} + */ + + /** + * @name Accessing the DoF indices of this object + */ + /** + * @{ + */ + + /** + * Return the indices of the dofs of this + * object in the standard ordering: dofs + * on vertex 0, dofs on vertex 1, etc, + * dofs on line 0, dofs on line 1, etc, + * dofs on quad 0, etc. + * + * The vector has to have the + * right size before being passed + * to this function. + * + * This function is most often + * used on active objects (edges, + * faces, cells). It can be used + * on non-active objects as well + * (i.e. objects that have + * children), but only if the + * finite element under + * consideration has degrees of + * freedom exclusively on + * vertices. Otherwise, the + * function doesn't make much + * sense, since for example + * inactive edges do not have + * degrees of freedom associated + * with them at all. + * + * The last argument denotes the + * finite element index. For the + * standard ::DoFHandler class, + * this value must be equal to + * its default value since that + * class only supports the same + * finite element on all cells + * anyway. + * + * However, for hp objects + * (i.e. the hp::DoFHandler + * class), different finite + * element objects may be used on + * different cells. On faces + * between two cells, as well as + * vertices, there may therefore + * be two sets of degrees of + * freedom, one for each of the + * finite elements used on the + * adjacent cells. In order to + * specify which set of degrees + * of freedom to work on, the + * last argument is used to + * disambiguate. Finally, if this + * function is called for a cell + * object, there can only be a + * single set of degrees of + * freedom, and fe_index has to + * match the result of + * active_fe_index(). + * + * For cells, there is only a + * single possible finite element + * index (namely the one for that + * cell, returned by + * cell-@>active_fe_index. Consequently, + * the derived DoFCellAccessor + * class has an overloaded + * version of this function that + * calls the present function + * with + * cell-@>active_fe_index + * as last argument. + */ + void get_dof_indices (std::vector &dof_indices, + const unsigned int fe_index = AccessorData::default_fe_index) const; + + /** + * Global DoF index of the i + * degree associated with the @p vertexth + * vertex of the present cell. + * + * The last argument denotes the + * finite element index. For the + * standard ::DoFHandler class, + * this value must be equal to + * its default value since that + * class only supports the same + * finite element on all cells + * anyway. + * + * However, for hp objects + * (i.e. the hp::DoFHandler + * class), different finite + * element objects may be used on + * different cells. On faces + * between two cells, as well as + * vertices, there may therefore + * be two sets of degrees of + * freedom, one for each of the + * finite elements used on the + * adjacent cells. In order to + * specify which set of degrees + * of freedom to work on, the + * last argument is used to + * disambiguate. Finally, if this + * function is called for a cell + * object, there can only be a + * single set of degrees of + * freedom, and fe_index has to + * match the result of + * active_fe_index(). + */ + types::global_dof_index vertex_dof_index (const unsigned int vertex, + const unsigned int i, + const unsigned int fe_index = AccessorData::default_fe_index) const; + + /** + * Index of the ith degree + * of freedom of this object. + * + * The last argument denotes the + * finite element index. For the + * standard ::DoFHandler class, + * this value must be equal to + * its default value since that + * class only supports the same + * finite element on all cells + * anyway. + * + * However, for hp objects + * (i.e. the hp::DoFHandler + * class), different finite + * element objects may be used on + * different cells. On faces + * between two cells, as well as + * vertices, there may therefore + * be two sets of degrees of + * freedom, one for each of the + * finite elements used on the + * adjacent cells. In order to + * specify which set of degrees + * of freedom to work on, the + * last argument is used to + * disambiguate. Finally, if this + * function is called for a cell + * object, there can only be a + * single set of degrees of + * freedom, and fe_index has to + * match the result of + * active_fe_index(). + * + * @note While the get_dof_indices() + * function returns an array that + * contains the indices of all degrees of + * freedom that somehow live on this + * object (i.e. on the vertices, edges or + * interior of this object), the current + * dof_index() function only considers + * the DoFs that really belong to this + * particular object's interior. In other + * words, as an example, if the current + * object refers to a quad (a cell in 2d, + * a face in 3d) and the finite element + * associated with it is a bilinear one, + * then the get_dof_indices() will return + * an array of size 4 while dof_index() + * will produce an exception because no + * degrees are defined in the interior of + * the face. + */ + types::global_dof_index dof_index (const unsigned int i, + const unsigned int fe_index = AccessorData::default_fe_index) const; + + /** + * @} + */ + + /** + * @name Accessing the finite element associated with this object + */ + /** + * @{ + */ + + /** + * Return the number of finite + * elements that are active on a + * given object. + * + * For non-hp DoFHandler objects, + * the answer is of course always + * one. However, for + * hp::DoFHandler objects, this + * isn't the case: If this is a + * cell, the answer is of course + * one. If it is a face, the + * answer may be one or two, + * depending on whether the two + * adjacent cells use the same + * finite element or not. If it + * is an edge in 3d, the possible + * return value may be one or any + * other value larger than that. + */ + unsigned int + n_active_fe_indices () const; + + /** + * Return the @p n-th active fe + * index on this object. For + * cells and all non-hp objects, + * there is only a single active + * fe index, so the argument must + * be equal to zero. For + * lower-dimensional hp objects, + * there are + * n_active_fe_indices() active + * finite elements, and this + * function can be queried for + * their indices. + */ + unsigned int + nth_active_fe_index (const unsigned int n) const; + + /** + * Return true if the finite + * element with given index is + * active on the present + * object. For non-hp DoF + * accessors, this is of course + * the case only if @p fe_index + * equals zero. For cells, it is + * the case if @p fe_index equals + * active_fe_index() of this + * cell. For faces and other + * lower-dimensional objects, + * there may be more than one @p + * fe_index that are active on + * any given object (see + * n_active_fe_indices()). + */ + bool + fe_index_is_active (const unsigned int fe_index) const; + + /** + * Return a reference to the finite + * element used on this object with the + * given @p fe_index. @p fe_index must be + * used on this object, + * i.e. fe_index_is_active(fe_index) + * must return true. + */ + const FiniteElement::dimension,DH<1,spacedim>::space_dimension> & + get_fe (const unsigned int fe_index) const; + + /** + * @} + */ + + /** + * Exceptions for child classes + * + * @ingroup Exceptions + */ + DeclException0 (ExcInvalidObject); + /** + * Exception + * + * @ingroup Exceptions + */ + DeclException0 (ExcVectorNotEmpty); + /** + * Exception + * + * @ingroup Exceptions + */ + DeclException0 (ExcVectorDoesNotMatch); + /** + * Exception + * + * @ingroup Exceptions + */ + DeclException0 (ExcMatrixDoesNotMatch); + /** + * A function has been called for + * a cell which should be active, + * but is refined. @ref GlossActive + * + * @ingroup Exceptions + */ + DeclException0 (ExcNotActive); + /** + * Exception + * + * @ingroup Exceptions + */ + DeclException0 (ExcCantCompareIterators); + +protected: + + /** + * Store the address of the DoFHandler object + * to be accessed. + */ + DH<1,spacedim> *dof_handler; + + /** + * Compare for equality. + */ + bool operator == (const DoFAccessor &) const; + + /** + * Compare for inequality. + */ + bool operator != (const DoFAccessor &) const; + + /** + * Reset the DoF handler pointer. + */ + void set_dof_handler (DH<1,spacedim> *dh); + + /** + * Set the index of the + * ith degree of freedom + * of this object to @p index. + * + * The last argument denotes the + * finite element index. For the + * standard ::DoFHandler class, + * this value must be equal to + * its default value since that + * class only supports the same + * finite element on all cells + * anyway. + * + * However, for hp objects + * (i.e. the hp::DoFHandler + * class), different finite + * element objects may be used on + * different cells. On faces + * between two cells, as well as + * vertices, there may therefore + * be two sets of degrees of + * freedom, one for each of the + * finite elements used on the + * adjacent cells. In order to + * specify which set of degrees + * of freedom to work on, the + * last argument is used to + * disambiguate. Finally, if this + * function is called for a cell + * object, there can only be a + * single set of degrees of + * freedom, and fe_index has to + * match the result of + * active_fe_index(). + */ + void set_dof_index (const unsigned int i, + const types::global_dof_index index, + const unsigned int fe_index = AccessorData::default_fe_index) const; + + /** + * Set the global index of the i + * degree on the @p vertex-th vertex of + * the present cell to @p index. + * + * The last argument denotes the + * finite element index. For the + * standard ::DoFHandler class, + * this value must be equal to + * its default value since that + * class only supports the same + * finite element on all cells + * anyway. + * + * However, for hp objects + * (i.e. the hp::DoFHandler + * class), different finite + * element objects may be used on + * different cells. On faces + * between two cells, as well as + * vertices, there may therefore + * be two sets of degrees of + * freedom, one for each of the + * finite elements used on the + * adjacent cells. In order to + * specify which set of degrees + * of freedom to work on, the + * last argument is used to + * disambiguate. Finally, if this + * function is called for a cell + * object, there can only be a + * single set of degrees of + * freedom, and fe_index has to + * match the result of + * active_fe_index(). + */ + void set_vertex_dof_index (const unsigned int vertex, + const unsigned int i, + const types::global_dof_index index, + const unsigned int fe_index = AccessorData::default_fe_index) const; + + /** + * Iterator classes need to be friends + * because they need to access operator== + * and operator!=. + */ + template friend class TriaRawIterator; + + + /** + * Make the DoFHandler class a friend so + * that it can call the set_xxx() + * functions. + */ + template friend class DoFHandler; + template friend class hp::DoFHandler; + + friend struct dealii::internal::DoFHandler::Policy::Implementation; + friend struct dealii::internal::DoFHandler::Implementation; + friend struct dealii::internal::hp::DoFHandler::Implementation; + friend struct dealii::internal::DoFCellAccessor::Implementation; }; @@ -1468,680 +1468,680 @@ class DoFAccessor<0,DH<1,spacedim> > : public TriaAccessor<0,1,spacedim> template class DoFCellAccessor : public DoFAccessor { - public: - /** - * Extract dimension from DH. - */ - static const unsigned int dim = DH::dimension; - - /** - * Extract space dimension from DH. - */ - static const unsigned int spacedim = DH::space_dimension; - - /** - * Declare the data type that - * this accessor class expects to - * get passed from the iterator - * classes. - */ - typedef typename DoFAccessor::AccessorData AccessorData; - - /** - * Declare a typedef to the base - * class to make accessing some - * of the exception classes - * simpler. - */ - typedef DoFAccessor BaseClass; - - /** - * Define the type of the - * container this is part of. - */ - typedef DH Container; - - /** - * @name Constructors - */ - /** - * @{ - */ - - /** - * Constructor - */ - DoFCellAccessor (const Triangulation *tria, - const int level, - const int index, - const AccessorData *local_data); - - /** - * Conversion constructor. This - * constructor exists to make certain - * constructs simpler to write in - * dimension independent code. For - * example, it allows assigning a face - * iterator to a line iterator, an - * operation that is useful in 2d but - * doesn't make any sense in 3d. The - * constructor here exists for the - * purpose of making the code conform to - * C++ but it will unconditionally abort; - * in other words, assigning a face - * iterator to a line iterator is better - * put into an if-statement that checks - * that the dimension is two, and assign - * to a quad iterator in 3d (an operator - * that, without this constructor would - * be illegal if we happen to compile for - * 2d). - */ - template - DoFCellAccessor (const InvalidAccessor &); - - /** - * Another conversion operator - * between objects that don't - * make sense, just like the - * previous one. - */ - template - DoFCellAccessor (const DoFAccessor &); - - /** - * @} - */ - - /** - * Return the parent as a DoF - * cell iterator. This - * function is needed since the - * parent function of the base - * class returns a cell accessor - * without access to the DoF - * data. - */ - typename dealii::internal::DoFHandler::Iterators::cell_iterator - parent () const; - - /** - * @name Accessing sub-objects and neighbors - */ - /** - * @{ - */ - - /** - * Return the @p ith neighbor as - * a DoF cell iterator. This - * function is needed since the - * neighbor function of the base - * class returns a cell accessor - * without access to the DoF - * data. - */ - typename dealii::internal::DoFHandler::Iterators::cell_iterator - neighbor (const unsigned int) const; - - /** - * Return the @p ith child as a - * DoF cell iterator. This - * function is needed since the - * child function of the base - * class returns a cell accessor - * without access to the DoF - * data. - */ - typename dealii::internal::DoFHandler::Iterators::cell_iterator - child (const unsigned int) const; - - /** - * Return an iterator to the @p ith face - * of this cell. - * - * This function is not implemented in - * 1D, and maps to DoFAccessor::line - * in 2D. - */ - typename dealii::internal::DoFHandler::Iterators::face_iterator - face (const unsigned int i) const; - - /** - * Return the result of the - * @p neighbor_child_on_subface - * function of the base class, - * but convert it so that one can - * also access the DoF data (the - * function in the base class - * only returns an iterator with - * access to the triangulation - * data). - */ - typename dealii::internal::DoFHandler::Iterators::cell_iterator - neighbor_child_on_subface (const unsigned int face_no, - const unsigned int subface_no) const; - - /** - * @} - */ - - /** - * @name Extracting values from global vectors - */ - /** - * @{ - */ - - /** - * Return the values of the given vector - * restricted to the dofs of this - * cell in the standard ordering: dofs - * on vertex 0, dofs on vertex 1, etc, - * dofs on line 0, dofs on line 1, etc, - * dofs on quad 0, etc. - * - * The vector has to have the - * right size before being passed - * to this function. This - * function is only callable for - * active cells. - * - * The input vector may be either - * a Vector, - * Vector, or a - * BlockVector, or a - * PETSc or Trilinos vector if - * deal.II is compiled to support - * these libraries. It is in the - * responsibility of the caller - * to assure that the types of - * the numbers stored in input - * and output vectors are - * compatible and with similar - * accuracy. - */ - template - void get_dof_values (const InputVector &values, - Vector &local_values) const; - - /** - * Return the values of the given vector - * restricted to the dofs of this - * cell in the standard ordering: dofs - * on vertex 0, dofs on vertex 1, etc, - * dofs on line 0, dofs on line 1, etc, - * dofs on quad 0, etc. - * - * The vector has to have the - * right size before being passed - * to this function. This - * function is only callable for - * active cells. - * - * The input vector may be either - * a Vector, - * Vector, or a - * BlockVector, or a - * PETSc or Trilinos vector if - * deal.II is compiled to support - * these libraries. It is in the - * responsibility of the caller - * to assure that the types of - * the numbers stored in input - * and output vectors are - * compatible and with similar - * accuracy. - */ - template - void get_dof_values (const InputVector &values, - ForwardIterator local_values_begin, - ForwardIterator local_values_end) const; - - /** - * Return the values of the given vector - * restricted to the dofs of this - * cell in the standard ordering: dofs - * on vertex 0, dofs on vertex 1, etc, - * dofs on line 0, dofs on line 1, etc, - * dofs on quad 0, etc. - * - * The vector has to have the - * right size before being passed - * to this function. This - * function is only callable for - * active cells. - * - * The input vector may be either a - * Vector, - * Vector, or a - * BlockVector, or a PETSc or - * Trilinos vector if deal.II is - * compiled to support these - * libraries. It is in the - * responsibility of the caller to - * assure that the types of the numbers - * stored in input and output vectors - * are compatible and with similar - * accuracy. The ConstraintMatrix - * passed as an argument to this - * function makes sure that constraints - * are correctly distributed when the - * dof values are calculated. - */ - template - void get_dof_values (const ConstraintMatrix &constraints, - const InputVector &values, - ForwardIterator local_values_begin, - ForwardIterator local_values_end) const; - - /** - * This function is the counterpart to - * get_dof_values(): it takes a vector - * of values for the degrees of freedom - * of the cell pointed to by this iterator - * and writes these values into the global - * data vector @p values. This function - * is only callable for active cells. - * - * Note that for continuous finite - * elements, calling this function affects - * the dof values on neighboring cells as - * well. It may also violate continuity - * requirements for hanging nodes, if - * neighboring cells are less refined than - * the present one. These requirements - * are not taken care of and must be - * enforced by the user afterwards. - * - * The vector has to have the - * right size before being passed - * to this function. - * - * The output vector may be either a - * Vector, - * Vector, or a - * BlockVector, or a - * PETSc vector if deal.II is compiled to - * support these libraries. It is in the - * responsibility of the caller to assure - * that the types of the numbers stored - * in input and output vectors are - * compatible and with similar accuracy. - */ - template - void set_dof_values (const Vector &local_values, - OutputVector &values) const; - - /** - * Return the interpolation of - * the given finite element - * function to the present - * cell. In the simplest case, - * the cell is a terminal one, - * i.e. has no children; then, - * the returned value is the - * vector of nodal values on that - * cell. You could then as well - * get the desired values through - * the @p get_dof_values - * function. In the other case, - * when the cell has children, we - * use the restriction matrices - * provided by the finite element - * class to compute the - * interpolation from the - * children to the present cell. - * - * It is assumed that both - * vectors already have the right - * size beforehand. - * - * Unlike the get_dof_values() - * function, this function works - * on cells rather than to lines, - * quads, and hexes, since - * interpolation is presently - * only provided for cells by the - * finite element classes. - */ - template - void get_interpolated_dof_values (const InputVector &values, - Vector &interpolated_values) const; - - /** - * This, again, is the - * counterpart to - * get_interpolated_dof_values(): - * you specify the dof values on - * a cell and these are - * interpolated to the children - * of the present cell and set on - * the terminal cells. - * - * In principle, it works as - * follows: if the cell pointed - * to by this object is terminal, - * then the dof values are set in - * the global data vector by - * calling the set_dof_values() - * function; otherwise, the - * values are prolonged to each - * of the children and this - * function is called for each of - * them. - * - * Using the - * get_interpolated_dof_values() - * and this function, you can - * compute the interpolation of a - * finite element function to a - * coarser grid by first getting - * the interpolated solution on a - * cell of the coarse grid and - * afterwards redistributing it - * using this function. - * - * Note that for continuous - * finite elements, calling this - * function affects the dof - * values on neighboring cells as - * well. It may also violate - * continuity requirements for - * hanging nodes, if neighboring - * cells are less refined than - * the present one, or if their - * children are less refined than - * the children of this - * cell. These requirements are - * not taken care of and must be - * enforced by the user - * afterward. - * - * It is assumed that both - * vectors already have the right - * size beforehand. This function - * relies on the existence of a - * natural interpolation property - * of finite element spaces of a - * cell to its children, denoted - * by the prolongation matrices - * of finite element classes. For - * some elements, the spaces on - * coarse and fine grids are not - * nested, in which case the - * interpolation to a child is - * not the identity; refer to the - * documentation of the - * respective finite element - * class for a description of - * what the prolongation matrices - * represent in this case. - * - * Unlike the set_dof_values() - * function, this function is - * associated to cells rather - * than to lines, quads, and - * hexes, since interpolation is - * presently only provided for - * cells by the finite element - * objects. - * - * The output vector may be either a - * Vector, - * Vector, or a - * BlockVector, or a - * PETSc vector if deal.II is compiled to - * support these libraries. It is in the - * responsibility of the caller to assure - * that the types of the numbers stored - * in input and output vectors are - * compatible and with similar accuracy. - */ - template - void set_dof_values_by_interpolation (const Vector &local_values, - OutputVector &values) const; - - /** - * Distribute a local (cell - * based) vector to a global one - * by mapping the local numbering - * of the degrees of freedom to - * the global one and entering - * the local values into the - * global vector. - * - * The elements are - * added up to the - * elements in the global vector, - * rather than just set, since - * this is usually what one - * wants. - */ - template - void - distribute_local_to_global (const Vector &local_source, - OutputVector &global_destination) const; - - /** - * Distribute a local (cell based) - * vector in iterator format to a - * global one by mapping the local - * numbering of the degrees of freedom - * to the global one and entering the - * local values into the global vector. - * - * The elements are added up - * to the elements in the global - * vector, rather than just set, since - * this is usually what one wants. - */ - template - void - distribute_local_to_global (ForwardIterator local_source_begin, - ForwardIterator local_source_end, - OutputVector &global_destination) const; - - /** - * Distribute a local (cell based) - * vector in iterator format to a - * global one by mapping the local - * numbering of the degrees of freedom - * to the global one and entering the - * local values into the global vector. - * - * The elements are added up - * to the elements in the global - * vector, rather than just set, since - * this is usually what one - * wants. Moreover, the - * ConstraintMatrix passed to this - * function makes sure that also - * constraints are eliminated in this - * process. - */ - template - void - distribute_local_to_global (const ConstraintMatrix &constraints, - ForwardIterator local_source_begin, - ForwardIterator local_source_end, - OutputVector &global_destination) const; - - /** - * This function does much the - * same as the - * distribute_local_to_global(Vector,Vector) - * function, but operates on - * matrices instead of - * vectors. If the matrix type is - * a sparse matrix then it is - * supposed to have non-zero - * entry slots where required. - */ - template - void - distribute_local_to_global (const FullMatrix &local_source, - OutputMatrix &global_destination) const; - - /** - * This function does what the two - * distribute_local_to_global - * functions with vector and matrix - * argument do, but all at once. - */ - template - void - distribute_local_to_global (const FullMatrix &local_matrix, - const Vector &local_vector, - OutputMatrix &global_matrix, - OutputVector &global_vector) const; - - /** - * @} - */ - - /** - * @name Accessing the DoF indices of this object - */ - /** - * @{ - */ - - /** - * Return the indices of the dofs of this - * quad in the standard ordering: dofs - * on vertex 0, dofs on vertex 1, etc, - * dofs on line 0, dofs on line 1, etc, - * dofs on quad 0, etc. - * - * It is assumed that the vector already - * has the right size beforehand. - * - * This function reimplements the - * same function in the base - * class. The functions in the - * base classes are available for - * all geometric objects, - * i.e. even in 3d they can be - * used to access the dof indices - * of edges, for example. On the - * other hand, the most common - * case is clearly the use on - * cells, which is why we cache - * the array for each cell, but - * not edge. To retrieve the - * cached values, rather than - * collect the necessary - * information every time, this - * function overwrites the one in - * the base class. - * - * This function is most often - * used on active objects (edges, - * faces, cells). It can be used - * on non-active objects as well - * (i.e. objects that have - * children), but only if the - * finite element under - * consideration has degrees of - * freedom exclusively on - * vertices. Otherwise, the - * function doesn't make much - * sense, since for example - * inactive edges do not have - * degrees of freedom associated - * with them at all. - */ - void get_dof_indices (std::vector &dof_indices) const; - - void get_mg_dof_indices (std::vector& dof_indices) const; - - /** - * @} - */ - - /** - * @name Accessing the finite element associated with this object - */ - /** - * @{ - */ - - /** - * Return the finite element that - * is used on the cell pointed to - * by this iterator. For non-hp - * DoF handlers, this is of - * course always the same - * element, independent of the - * cell we are presently on, but - * for hp DoF handlers, this may - * change from cell to cell. - */ - const FiniteElement & - get_fe () const; - - /** - * Returns the index inside the - * hp::FECollection of the FiniteElement - * used for this cell. - */ - unsigned int active_fe_index () const; - - /** - * Sets the index of the FiniteElement used for - * this cell. - */ - void set_active_fe_index (const unsigned int i); - /** - * @} - */ - - /** - * Set the DoF indices of this - * cell to the given values. This - * function bypasses the DoF - * cache, if one exists for the - * given DoF handler class. - */ - void set_dof_indices (const std::vector &dof_indices); - - /** - * Update the cache in which we - * store the dof indices of this - * cell. - */ - void update_cell_dof_indices_cache () const; - - private: - /** - * Copy operator. This is normally used - * in a context like iterator a,b; - * *a=*b;. Presumably, the intent - * here is to copy the object pointed to - * by @p b to the object pointed to by - * @p a. However, the result of - * dereferencing an iterator is not an - * object but an accessor; consequently, - * this operation is not useful for - * iterators on triangulations. We - * declare this function here private, - * thus it may not be used from outside. - * Furthermore it is not implemented and - * will give a linker error if used - * anyway. - */ - DoFCellAccessor & - operator = (const DoFCellAccessor &da); - - /** - * Make the DoFHandler class a - * friend so that it can call the - * update_cell_dof_indices_cache() - * function - */ - template friend class DoFHandler; - friend struct dealii::internal::DoFCellAccessor::Implementation; +public: + /** + * Extract dimension from DH. + */ + static const unsigned int dim = DH::dimension; + + /** + * Extract space dimension from DH. + */ + static const unsigned int spacedim = DH::space_dimension; + + /** + * Declare the data type that + * this accessor class expects to + * get passed from the iterator + * classes. + */ + typedef typename DoFAccessor::AccessorData AccessorData; + + /** + * Declare a typedef to the base + * class to make accessing some + * of the exception classes + * simpler. + */ + typedef DoFAccessor BaseClass; + + /** + * Define the type of the + * container this is part of. + */ + typedef DH Container; + + /** + * @name Constructors + */ + /** + * @{ + */ + + /** + * Constructor + */ + DoFCellAccessor (const Triangulation *tria, + const int level, + const int index, + const AccessorData *local_data); + + /** + * Conversion constructor. This + * constructor exists to make certain + * constructs simpler to write in + * dimension independent code. For + * example, it allows assigning a face + * iterator to a line iterator, an + * operation that is useful in 2d but + * doesn't make any sense in 3d. The + * constructor here exists for the + * purpose of making the code conform to + * C++ but it will unconditionally abort; + * in other words, assigning a face + * iterator to a line iterator is better + * put into an if-statement that checks + * that the dimension is two, and assign + * to a quad iterator in 3d (an operator + * that, without this constructor would + * be illegal if we happen to compile for + * 2d). + */ + template + DoFCellAccessor (const InvalidAccessor &); + + /** + * Another conversion operator + * between objects that don't + * make sense, just like the + * previous one. + */ + template + DoFCellAccessor (const DoFAccessor &); + + /** + * @} + */ + + /** + * Return the parent as a DoF + * cell iterator. This + * function is needed since the + * parent function of the base + * class returns a cell accessor + * without access to the DoF + * data. + */ + typename dealii::internal::DoFHandler::Iterators::cell_iterator + parent () const; + + /** + * @name Accessing sub-objects and neighbors + */ + /** + * @{ + */ + + /** + * Return the @p ith neighbor as + * a DoF cell iterator. This + * function is needed since the + * neighbor function of the base + * class returns a cell accessor + * without access to the DoF + * data. + */ + typename dealii::internal::DoFHandler::Iterators::cell_iterator + neighbor (const unsigned int) const; + + /** + * Return the @p ith child as a + * DoF cell iterator. This + * function is needed since the + * child function of the base + * class returns a cell accessor + * without access to the DoF + * data. + */ + typename dealii::internal::DoFHandler::Iterators::cell_iterator + child (const unsigned int) const; + + /** + * Return an iterator to the @p ith face + * of this cell. + * + * This function is not implemented in + * 1D, and maps to DoFAccessor::line + * in 2D. + */ + typename dealii::internal::DoFHandler::Iterators::face_iterator + face (const unsigned int i) const; + + /** + * Return the result of the + * @p neighbor_child_on_subface + * function of the base class, + * but convert it so that one can + * also access the DoF data (the + * function in the base class + * only returns an iterator with + * access to the triangulation + * data). + */ + typename dealii::internal::DoFHandler::Iterators::cell_iterator + neighbor_child_on_subface (const unsigned int face_no, + const unsigned int subface_no) const; + + /** + * @} + */ + + /** + * @name Extracting values from global vectors + */ + /** + * @{ + */ + + /** + * Return the values of the given vector + * restricted to the dofs of this + * cell in the standard ordering: dofs + * on vertex 0, dofs on vertex 1, etc, + * dofs on line 0, dofs on line 1, etc, + * dofs on quad 0, etc. + * + * The vector has to have the + * right size before being passed + * to this function. This + * function is only callable for + * active cells. + * + * The input vector may be either + * a Vector, + * Vector, or a + * BlockVector, or a + * PETSc or Trilinos vector if + * deal.II is compiled to support + * these libraries. It is in the + * responsibility of the caller + * to assure that the types of + * the numbers stored in input + * and output vectors are + * compatible and with similar + * accuracy. + */ + template + void get_dof_values (const InputVector &values, + Vector &local_values) const; + + /** + * Return the values of the given vector + * restricted to the dofs of this + * cell in the standard ordering: dofs + * on vertex 0, dofs on vertex 1, etc, + * dofs on line 0, dofs on line 1, etc, + * dofs on quad 0, etc. + * + * The vector has to have the + * right size before being passed + * to this function. This + * function is only callable for + * active cells. + * + * The input vector may be either + * a Vector, + * Vector, or a + * BlockVector, or a + * PETSc or Trilinos vector if + * deal.II is compiled to support + * these libraries. It is in the + * responsibility of the caller + * to assure that the types of + * the numbers stored in input + * and output vectors are + * compatible and with similar + * accuracy. + */ + template + void get_dof_values (const InputVector &values, + ForwardIterator local_values_begin, + ForwardIterator local_values_end) const; + + /** + * Return the values of the given vector + * restricted to the dofs of this + * cell in the standard ordering: dofs + * on vertex 0, dofs on vertex 1, etc, + * dofs on line 0, dofs on line 1, etc, + * dofs on quad 0, etc. + * + * The vector has to have the + * right size before being passed + * to this function. This + * function is only callable for + * active cells. + * + * The input vector may be either a + * Vector, + * Vector, or a + * BlockVector, or a PETSc or + * Trilinos vector if deal.II is + * compiled to support these + * libraries. It is in the + * responsibility of the caller to + * assure that the types of the numbers + * stored in input and output vectors + * are compatible and with similar + * accuracy. The ConstraintMatrix + * passed as an argument to this + * function makes sure that constraints + * are correctly distributed when the + * dof values are calculated. + */ + template + void get_dof_values (const ConstraintMatrix &constraints, + const InputVector &values, + ForwardIterator local_values_begin, + ForwardIterator local_values_end) const; + + /** + * This function is the counterpart to + * get_dof_values(): it takes a vector + * of values for the degrees of freedom + * of the cell pointed to by this iterator + * and writes these values into the global + * data vector @p values. This function + * is only callable for active cells. + * + * Note that for continuous finite + * elements, calling this function affects + * the dof values on neighboring cells as + * well. It may also violate continuity + * requirements for hanging nodes, if + * neighboring cells are less refined than + * the present one. These requirements + * are not taken care of and must be + * enforced by the user afterwards. + * + * The vector has to have the + * right size before being passed + * to this function. + * + * The output vector may be either a + * Vector, + * Vector, or a + * BlockVector, or a + * PETSc vector if deal.II is compiled to + * support these libraries. It is in the + * responsibility of the caller to assure + * that the types of the numbers stored + * in input and output vectors are + * compatible and with similar accuracy. + */ + template + void set_dof_values (const Vector &local_values, + OutputVector &values) const; + + /** + * Return the interpolation of + * the given finite element + * function to the present + * cell. In the simplest case, + * the cell is a terminal one, + * i.e. has no children; then, + * the returned value is the + * vector of nodal values on that + * cell. You could then as well + * get the desired values through + * the @p get_dof_values + * function. In the other case, + * when the cell has children, we + * use the restriction matrices + * provided by the finite element + * class to compute the + * interpolation from the + * children to the present cell. + * + * It is assumed that both + * vectors already have the right + * size beforehand. + * + * Unlike the get_dof_values() + * function, this function works + * on cells rather than to lines, + * quads, and hexes, since + * interpolation is presently + * only provided for cells by the + * finite element classes. + */ + template + void get_interpolated_dof_values (const InputVector &values, + Vector &interpolated_values) const; + + /** + * This, again, is the + * counterpart to + * get_interpolated_dof_values(): + * you specify the dof values on + * a cell and these are + * interpolated to the children + * of the present cell and set on + * the terminal cells. + * + * In principle, it works as + * follows: if the cell pointed + * to by this object is terminal, + * then the dof values are set in + * the global data vector by + * calling the set_dof_values() + * function; otherwise, the + * values are prolonged to each + * of the children and this + * function is called for each of + * them. + * + * Using the + * get_interpolated_dof_values() + * and this function, you can + * compute the interpolation of a + * finite element function to a + * coarser grid by first getting + * the interpolated solution on a + * cell of the coarse grid and + * afterwards redistributing it + * using this function. + * + * Note that for continuous + * finite elements, calling this + * function affects the dof + * values on neighboring cells as + * well. It may also violate + * continuity requirements for + * hanging nodes, if neighboring + * cells are less refined than + * the present one, or if their + * children are less refined than + * the children of this + * cell. These requirements are + * not taken care of and must be + * enforced by the user + * afterward. + * + * It is assumed that both + * vectors already have the right + * size beforehand. This function + * relies on the existence of a + * natural interpolation property + * of finite element spaces of a + * cell to its children, denoted + * by the prolongation matrices + * of finite element classes. For + * some elements, the spaces on + * coarse and fine grids are not + * nested, in which case the + * interpolation to a child is + * not the identity; refer to the + * documentation of the + * respective finite element + * class for a description of + * what the prolongation matrices + * represent in this case. + * + * Unlike the set_dof_values() + * function, this function is + * associated to cells rather + * than to lines, quads, and + * hexes, since interpolation is + * presently only provided for + * cells by the finite element + * objects. + * + * The output vector may be either a + * Vector, + * Vector, or a + * BlockVector, or a + * PETSc vector if deal.II is compiled to + * support these libraries. It is in the + * responsibility of the caller to assure + * that the types of the numbers stored + * in input and output vectors are + * compatible and with similar accuracy. + */ + template + void set_dof_values_by_interpolation (const Vector &local_values, + OutputVector &values) const; + + /** + * Distribute a local (cell + * based) vector to a global one + * by mapping the local numbering + * of the degrees of freedom to + * the global one and entering + * the local values into the + * global vector. + * + * The elements are + * added up to the + * elements in the global vector, + * rather than just set, since + * this is usually what one + * wants. + */ + template + void + distribute_local_to_global (const Vector &local_source, + OutputVector &global_destination) const; + + /** + * Distribute a local (cell based) + * vector in iterator format to a + * global one by mapping the local + * numbering of the degrees of freedom + * to the global one and entering the + * local values into the global vector. + * + * The elements are added up + * to the elements in the global + * vector, rather than just set, since + * this is usually what one wants. + */ + template + void + distribute_local_to_global (ForwardIterator local_source_begin, + ForwardIterator local_source_end, + OutputVector &global_destination) const; + + /** + * Distribute a local (cell based) + * vector in iterator format to a + * global one by mapping the local + * numbering of the degrees of freedom + * to the global one and entering the + * local values into the global vector. + * + * The elements are added up + * to the elements in the global + * vector, rather than just set, since + * this is usually what one + * wants. Moreover, the + * ConstraintMatrix passed to this + * function makes sure that also + * constraints are eliminated in this + * process. + */ + template + void + distribute_local_to_global (const ConstraintMatrix &constraints, + ForwardIterator local_source_begin, + ForwardIterator local_source_end, + OutputVector &global_destination) const; + + /** + * This function does much the + * same as the + * distribute_local_to_global(Vector,Vector) + * function, but operates on + * matrices instead of + * vectors. If the matrix type is + * a sparse matrix then it is + * supposed to have non-zero + * entry slots where required. + */ + template + void + distribute_local_to_global (const FullMatrix &local_source, + OutputMatrix &global_destination) const; + + /** + * This function does what the two + * distribute_local_to_global + * functions with vector and matrix + * argument do, but all at once. + */ + template + void + distribute_local_to_global (const FullMatrix &local_matrix, + const Vector &local_vector, + OutputMatrix &global_matrix, + OutputVector &global_vector) const; + + /** + * @} + */ + + /** + * @name Accessing the DoF indices of this object + */ + /** + * @{ + */ + + /** + * Return the indices of the dofs of this + * quad in the standard ordering: dofs + * on vertex 0, dofs on vertex 1, etc, + * dofs on line 0, dofs on line 1, etc, + * dofs on quad 0, etc. + * + * It is assumed that the vector already + * has the right size beforehand. + * + * This function reimplements the + * same function in the base + * class. The functions in the + * base classes are available for + * all geometric objects, + * i.e. even in 3d they can be + * used to access the dof indices + * of edges, for example. On the + * other hand, the most common + * case is clearly the use on + * cells, which is why we cache + * the array for each cell, but + * not edge. To retrieve the + * cached values, rather than + * collect the necessary + * information every time, this + * function overwrites the one in + * the base class. + * + * This function is most often + * used on active objects (edges, + * faces, cells). It can be used + * on non-active objects as well + * (i.e. objects that have + * children), but only if the + * finite element under + * consideration has degrees of + * freedom exclusively on + * vertices. Otherwise, the + * function doesn't make much + * sense, since for example + * inactive edges do not have + * degrees of freedom associated + * with them at all. + */ + void get_dof_indices (std::vector &dof_indices) const; + + void get_mg_dof_indices (std::vector &dof_indices) const; + + /** + * @} + */ + + /** + * @name Accessing the finite element associated with this object + */ + /** + * @{ + */ + + /** + * Return the finite element that + * is used on the cell pointed to + * by this iterator. For non-hp + * DoF handlers, this is of + * course always the same + * element, independent of the + * cell we are presently on, but + * for hp DoF handlers, this may + * change from cell to cell. + */ + const FiniteElement & + get_fe () const; + + /** + * Returns the index inside the + * hp::FECollection of the FiniteElement + * used for this cell. + */ + unsigned int active_fe_index () const; + + /** + * Sets the index of the FiniteElement used for + * this cell. + */ + void set_active_fe_index (const unsigned int i); + /** + * @} + */ + + /** + * Set the DoF indices of this + * cell to the given values. This + * function bypasses the DoF + * cache, if one exists for the + * given DoF handler class. + */ + void set_dof_indices (const std::vector &dof_indices); + + /** + * Update the cache in which we + * store the dof indices of this + * cell. + */ + void update_cell_dof_indices_cache () const; + +private: + /** + * Copy operator. This is normally used + * in a context like iterator a,b; + * *a=*b;. Presumably, the intent + * here is to copy the object pointed to + * by @p b to the object pointed to by + * @p a. However, the result of + * dereferencing an iterator is not an + * object but an accessor; consequently, + * this operation is not useful for + * iterators on triangulations. We + * declare this function here private, + * thus it may not be used from outside. + * Furthermore it is not implemented and + * will give a linker error if used + * anyway. + */ + DoFCellAccessor & + operator = (const DoFCellAccessor &da); + + /** + * Make the DoFHandler class a + * friend so that it can call the + * update_cell_dof_indices_cache() + * function + */ + template friend class DoFHandler; + friend struct dealii::internal::DoFCellAccessor::Implementation; }; diff --git a/deal.II/include/deal.II/dofs/dof_accessor.templates.h b/deal.II/include/deal.II/dofs/dof_accessor.templates.h index 8643bd7694..5258f683bb 100644 --- a/deal.II/include/deal.II/dofs/dof_accessor.templates.h +++ b/deal.II/include/deal.II/dofs/dof_accessor.templates.h @@ -49,12 +49,12 @@ DoFAccessor::DoFAccessor ( const int level, const int index, const DH *dof_handler) - : - dealii::internal::DoFAccessor::Inheritance::BaseClass (tria, - level, - index), - dof_handler(const_cast(dof_handler)) + : + dealii::internal::DoFAccessor::Inheritance::BaseClass (tria, + level, + index), + dof_handler(const_cast(dof_handler)) {} @@ -159,17 +159,17 @@ DoFAccessor::child (const unsigned int i) const next_level = 0; TriaIterator > q (this->tria, - next_level, - this->child_index (i), - this->dof_handler); + next_level, + this->child_index (i), + this->dof_handler); - // make sure that we either created - // a past-the-end iterator or one - // pointing to a used cell + // make sure that we either created + // a past-the-end iterator or one + // pointing to a used cell Assert ((q.state() == IteratorState::past_the_end) - || - q->used(), - TriaAccessorExceptions::ExcUnusedCellAsChild()); + || + q->used(), + TriaAccessorExceptions::ExcUnusedCellAsChild()); return q; } @@ -194,9 +194,9 @@ DoFAccessor::parent () const previous_level = 0; TriaIterator > q (this->tria, - previous_level, - this->parent_index (), - this->dof_handler); + previous_level, + this->parent_index (), + this->dof_handler); return q; } @@ -207,1236 +207,1235 @@ namespace internal { namespace DoFAccessor { - /** - * A class like the one with same - * name in tria.cc. See there for - * more information. - */ + /** + * A class like the one with same + * name in tria.cc. See there for + * more information. + */ struct Implementation { - /** - * Implementations of the - * get_dof_index/set_dof_index functions. - */ - template - static - types::global_dof_index - get_dof_index (const dealii::DoFHandler<1,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - dealii::internal::int2type<1>) - { - return dof_handler.levels[obj_level]->dof_object. - get_dof_index (dof_handler, - obj_index, - fe_index, - local_index); - } - - - template - static - void - set_dof_index (const dealii::DoFHandler<1,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - dealii::internal::int2type<1>, - const types::global_dof_index global_index) - { - dof_handler.levels[obj_level]->dof_object. - set_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - global_index); - } - - - template - static - types::global_dof_index - get_dof_index (const dealii::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - dealii::internal::int2type<1>) - { - // faces have no levels - Assert (obj_level == 0, ExcInternalError()); - return dof_handler.faces->lines. - get_dof_index (dof_handler, - obj_index, - fe_index, - local_index); - } - - - template - static - void - set_dof_index (const dealii::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - dealii::internal::int2type<1>, - const types::global_dof_index global_index) - { - // faces have no levels - Assert (obj_level == 0, ExcInternalError()); - dof_handler.faces->lines. - set_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - global_index); - } - - - template - static - types::global_dof_index - get_dof_index (const dealii::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - dealii::internal::int2type<2>) - { - return dof_handler.levels[obj_level]->dof_object. - get_dof_index (dof_handler, - obj_index, - fe_index, - local_index); - } - - - template - static - void - set_dof_index (const dealii::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - dealii::internal::int2type<2>, - const types::global_dof_index global_index) - { - dof_handler.levels[obj_level]->dof_object. - set_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - global_index); - } - - - template - static - types::global_dof_index - get_dof_index (const dealii::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - dealii::internal::int2type<1>) - { - // faces have no levels - Assert (obj_level == 0, ExcInternalError()); - return dof_handler.faces->lines. - get_dof_index (dof_handler, - obj_index, - fe_index, - local_index); - } - - - template - static - void - set_dof_index (const dealii::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - dealii::internal::int2type<1>, - const types::global_dof_index global_index) - { - // faces have no levels - Assert (obj_level == 0, ExcInternalError()); - dof_handler.faces->lines. - set_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - global_index); - } - - - - template - static - types::global_dof_index - get_dof_index (const dealii::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - dealii::internal::int2type<2>) - { - // faces have no levels - Assert (obj_level == 0, ExcInternalError()); - return dof_handler.faces->quads. - get_dof_index (dof_handler, - obj_index, - fe_index, - local_index); - } - - - template - static - void - set_dof_index (const dealii::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - dealii::internal::int2type<2>, - const types::global_dof_index global_index) - { - // faces have no levels - Assert (obj_level == 0, ExcInternalError()); - dof_handler.faces->quads. - set_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - global_index); - } - - - - template - static - types::global_dof_index - get_dof_index (const dealii::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - dealii::internal::int2type<3>) - { - return dof_handler.levels[obj_level]->dof_object. - get_dof_index (dof_handler, - obj_index, - fe_index, - local_index); - } - - - template - static - void - set_dof_index (const dealii::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - dealii::internal::int2type<3>, - const types::global_dof_index global_index) - { - dof_handler.levels[obj_level]->dof_object. - set_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - global_index); - } - - - template - static - types::global_dof_index - get_dof_index (const dealii::hp::DoFHandler<1,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - const dealii::internal::int2type<1> &) - { - return dof_handler.levels[obj_level]->dof_object. - get_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - obj_level); - } - - - template - static - void - set_dof_index (const dealii::hp::DoFHandler<1,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - const dealii::internal::int2type<1> &, - const types::global_dof_index global_index) - { - dof_handler.levels[obj_level]->dof_object. - set_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - global_index, - obj_level); - } - - - template - static - types::global_dof_index - get_dof_index (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - const dealii::internal::int2type<1> &) - { - return dof_handler.faces->lines. - get_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - obj_level); - } - - - template - static - void - set_dof_index (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - const dealii::internal::int2type<1> &, - const types::global_dof_index global_index) - { - dof_handler.faces->lines. - set_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - global_index, - obj_level); - } - - - template - static - types::global_dof_index - get_dof_index (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - const dealii::internal::int2type<2> &) - { - return dof_handler.levels[obj_level]->dof_object. - get_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - obj_level); - } - - - template - static - void - set_dof_index (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - const dealii::internal::int2type<2> &, - const types::global_dof_index global_index) - { - dof_handler.levels[obj_level]->dof_object. - set_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - global_index, - obj_level); - } - - - template - static - types::global_dof_index - get_dof_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - const dealii::internal::int2type<1> &) - { - return dof_handler.faces->lines. - get_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - obj_level); - } - - - template - static - void - set_dof_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - const dealii::internal::int2type<1> &, - const types::global_dof_index global_index) - { - dof_handler.faces->lines. - set_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - global_index, - obj_level); - } - - - template - static - types::global_dof_index - get_dof_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - const dealii::internal::int2type<2> &) - { - return dof_handler.faces->quads. - get_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - obj_level); - } - - - template - static - void - set_dof_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - const dealii::internal::int2type<2> &, - const types::global_dof_index global_index) - { - dof_handler.faces->quads. - set_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - global_index, - obj_level); - } - - - template - static - types::global_dof_index - get_dof_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - const dealii::internal::int2type<3> &) - { - return dof_handler.levels[obj_level]->dof_object. - get_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - obj_level); - } - - - template - static - void - set_dof_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const unsigned int local_index, - const dealii::internal::int2type<3> &, - const types::global_dof_index global_index) - { - dof_handler.levels[obj_level]->dof_object. - set_dof_index (dof_handler, - obj_index, - fe_index, - local_index, - global_index, - obj_level); - } - - - template - static - bool - fe_index_is_active (const dealii::DoFHandler &, - const unsigned int, - const unsigned int, - const unsigned int fe_index, - const dealii::internal::int2type &) - { - return (fe_index == 0); - } - - - - template - static - unsigned int - n_active_fe_indices (const dealii::DoFHandler &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const dealii::internal::int2type &) - { - // check that the object we look - // at is in fact active. the - // problem is that we have - // templatized on the - // dimensionality of the object, - // so it may be a cell, a face, - // or a line. we have a bit of - // trouble doing this all in the - // generic case, so only check if - // it is either a cell or a - // line. the only case this - // leaves out is faces in 3d -- - // let's hope that this never is - // a problem - Assert ((dim==structdim - ? - typename - dealii::internal::DoFHandler:: - Iterators >:: - raw_cell_iterator (&dof_handler.get_tria(), - obj_level, - obj_index, - &dof_handler)->used() - : - (structdim==1 - ? - typename - dealii::internal::DoFHandler:: - Iterators >:: - raw_line_iterator (&dof_handler.get_tria(), - obj_level, - obj_index, - &dof_handler)->used() - : - true)) - == true, - ExcMessage ("This cell is not active and therefore can't be " - "queried for its active FE indices")); - return 1; - } - - - - template - static - unsigned int - nth_active_fe_index (const dealii::DoFHandler &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int n, - const dealii::internal::int2type &) - { - // check that the object we look - // at is in fact active. the - // problem is that we have - // templatized on the - // dimensionality of the object, - // so it may be a cell, a face, - // or a line. we have a bit of - // trouble doing this all in the - // generic case, so only check if - // it is either a cell or a - // line. the only case this - // leaves out is faces in 3d -- - // let's hope that this never is - // a problem - Assert ((dim==structdim - ? - typename - dealii::internal::DoFHandler:: - Iterators >:: - raw_cell_iterator (&dof_handler.get_tria(), - obj_level, - obj_index, - &dof_handler)->used() - : - (structdim==1 - ? - typename - dealii::internal::DoFHandler:: - Iterators >:: - raw_line_iterator (&dof_handler.get_tria(), - obj_level, - obj_index, - &dof_handler)->used() - : - true)) - == true, - ExcMessage ("This cell is not active and therefore can't be " - "queried for its active FE indices")); - Assert (n == 0, ExcIndexRange (n, 0, 1)); - - return dealii::DoFHandler::default_fe_index; - } - - - template - static - bool - fe_index_is_active (const dealii::hp::DoFHandler<1,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const dealii::internal::int2type<1> &) - { - return dof_handler.levels[obj_level]->dof_object.fe_index_is_active(dof_handler, - obj_index, - fe_index, - obj_level); - } - - - template - static - unsigned int - n_active_fe_indices (const dealii::hp::DoFHandler<1,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const dealii::internal::int2type<1> &) - { - return dof_handler.levels[obj_level]->dof_object.n_active_fe_indices (dof_handler, - obj_index); - } - - - - template - static - unsigned int - nth_active_fe_index (const dealii::hp::DoFHandler<1,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int n, - const dealii::internal::int2type<1> &) - { - return dof_handler.levels[obj_level]->dof_object.nth_active_fe_index (dof_handler, - obj_level, - obj_index, - n); - } - - - template - static - bool - fe_index_is_active (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const dealii::internal::int2type<1> &) - { - return dof_handler.faces->lines.fe_index_is_active(dof_handler, - obj_index, - fe_index, - obj_level); - } - - - template - static - unsigned int - n_active_fe_indices (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, - const unsigned int , - const unsigned int obj_index, - const dealii::internal::int2type<1> &) - { - return dof_handler.faces->lines.n_active_fe_indices (dof_handler, - obj_index); - } - - - template - static - unsigned int - nth_active_fe_index (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int n, - const dealii::internal::int2type<1> &) - { - return dof_handler.faces->lines.nth_active_fe_index (dof_handler, - obj_level, - obj_index, - n); - } - - - - template - static - bool - fe_index_is_active (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const dealii::internal::int2type<2> &) - { - return dof_handler.levels[obj_level]->dof_object.fe_index_is_active(dof_handler, - obj_index, - fe_index, - obj_level); - } - - - template - static - unsigned int - n_active_fe_indices (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const dealii::internal::int2type<2> &) - { - return dof_handler.levels[obj_level]->dof_object.n_active_fe_indices (dof_handler, - obj_index); - } - - - - template - static - unsigned int - nth_active_fe_index (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int n, - const dealii::internal::int2type<2> &) - { - return dof_handler.levels[obj_level]->dof_object.nth_active_fe_index (dof_handler, - obj_level, - obj_index, - n); - } - - - - template - static - bool - fe_index_is_active (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const dealii::internal::int2type<1> &) - { - return dof_handler.faces->lines.fe_index_is_active(dof_handler, - obj_index, - fe_index, - obj_level); - } - - - template - static - unsigned int - n_active_fe_indices (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int, - const unsigned int obj_index, - const dealii::internal::int2type<1> &) - { - return dof_handler.faces->lines.n_active_fe_indices (dof_handler, - obj_index); - } - - - - template - static - unsigned int - nth_active_fe_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int n, - const dealii::internal::int2type<1> &) - { - return dof_handler.faces->lines.nth_active_fe_index (dof_handler, - obj_level, - obj_index, - n); - } - - - - template - static - bool - fe_index_is_active (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const dealii::internal::int2type<2> &) - { - return dof_handler.faces->quads.fe_index_is_active(dof_handler, - obj_index, - fe_index, - obj_level); - } - - template - static - bool - fe_index_is_active (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int fe_index, - const dealii::internal::int2type<3> &) - { - return dof_handler.levels[obj_level]->dof_object.fe_index_is_active(dof_handler, - obj_index, - fe_index, - obj_level); - } - - - template - static - unsigned int - n_active_fe_indices (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int , - const unsigned int obj_index, - const dealii::internal::int2type<2> &) - { - return dof_handler.faces->quads.n_active_fe_indices (dof_handler, - obj_index); - } - - - - template - static - unsigned int - nth_active_fe_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int n, - const dealii::internal::int2type<2> &) - { - return dof_handler.faces->quads.nth_active_fe_index (dof_handler, - obj_level, - obj_index, - n); - } - - - - template - static - unsigned int - n_active_fe_indices (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const dealii::internal::int2type<3> &) - { - return dof_handler.levels[obj_level]->dof_object.n_active_fe_indices (dof_handler, - obj_index); - } - - - - template - static - unsigned int - nth_active_fe_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, - const unsigned int obj_level, - const unsigned int obj_index, - const unsigned int n, - const dealii::internal::int2type<3> &) - { - return dof_handler.levels[obj_level]->dof_object.nth_active_fe_index (dof_handler, - obj_level, - obj_index, - n); - } - - /** - * Set the @p local_index-th - * degree of freedom - * corresponding to the finite - * element specified by @p - * fe_index on the vertex with - * global number @p - * vertex_index to @p - * global_index. - */ - template - static - void - set_vertex_dof_index (dealii::DoFHandler &dof_handler, - const unsigned int vertex_index, - const unsigned int fe_index, - const unsigned int local_index, - const types::global_dof_index global_index) - { - Assert ((fe_index == dealii::DoFHandler::default_fe_index), - ExcMessage ("Only the default FE index is allowed for non-hp DoFHandler objects")); - Assert (dof_handler.selected_fe != 0, - ExcMessage ("No finite element collection is associated with " - "this DoFHandler")); - Assert (local_index < dof_handler.selected_fe->dofs_per_vertex, - ExcIndexRange(local_index, 0, - dof_handler.selected_fe->dofs_per_vertex)); - - dof_handler.vertex_dofs[vertex_index * - dof_handler.selected_fe->dofs_per_vertex - + local_index] - = global_index; - } - - - template - static - void - set_vertex_dof_index (dealii::hp::DoFHandler &dof_handler, - const unsigned int vertex_index, - const unsigned int fe_index, - const unsigned int local_index, - const types::global_dof_index global_index) - { - Assert ( (fe_index != dealii::hp::DoFHandler::default_fe_index), - ExcMessage ("You need to specify a FE index when working " - "with hp DoFHandlers")); - Assert (dof_handler.finite_elements != 0, - ExcMessage ("No finite element collection is associated with " - "this DoFHandler")); - Assert (local_index < (*dof_handler.finite_elements)[fe_index].dofs_per_vertex, - ExcIndexRange(local_index, 0, - (*dof_handler.finite_elements)[fe_index].dofs_per_vertex)); - Assert (fe_index < dof_handler.finite_elements->size(), - ExcInternalError()); - Assert (dof_handler.vertex_dofs_offsets[vertex_index] != - numbers::invalid_unsigned_int, - ExcMessage ("This vertex is unused and has no DoFs associated with it")); - - // hop along the list of index - // sets until we find the one - // with the correct fe_index, and - // then poke into that - // part. trigger an exception if - // we can't find a set for this - // particular fe_index - const unsigned int starting_offset = dof_handler.vertex_dofs_offsets[vertex_index]; - types::global_dof_index *pointer = &dof_handler.vertex_dofs[starting_offset]; - while (true) - { - Assert (pointer <= &dof_handler.vertex_dofs.back(), ExcInternalError()); - - // a fe index is always small - Assert((*pointer)::max(), ExcInternalError()); - const unsigned int this_fe_index = static_cast(*pointer); - - Assert (this_fe_index != numbers::invalid_unsigned_int, - ExcInternalError()); - Assert (this_fe_index < dof_handler.finite_elements->size(), - ExcInternalError()); - - if (this_fe_index == fe_index) - { - *(pointer + 1 + local_index) = global_index; - return; - } - else - pointer += (*dof_handler.finite_elements)[this_fe_index].dofs_per_vertex + 1; - } - } - - - /** - * Get the @p local_index-th - * degree of freedom - * corresponding to the finite - * element specified by @p - * fe_index on the vertex with - * global number @p - * vertex_index to @p - * global_index. - */ - - template - static - unsigned int - get_vertex_dof_index (const dealii::DoFHandler &dof_handler, - const unsigned int vertex_index, - const unsigned int fe_index, - const unsigned int local_index) - { - Assert ((fe_index == dealii::DoFHandler::default_fe_index), - ExcMessage ("Only the default FE index is allowed for non-hp DoFHandler objects")); - Assert (dof_handler.selected_fe != 0, - ExcMessage ("No finite element collection is associated with " - "this DoFHandler")); - Assert (local_index < dof_handler.selected_fe->dofs_per_vertex, - ExcIndexRange(local_index, 0, - dof_handler.selected_fe->dofs_per_vertex)); - - return - dof_handler.vertex_dofs[vertex_index * - dof_handler.selected_fe->dofs_per_vertex - + local_index]; - } - - - template - static - unsigned int - get_vertex_dof_index (const dealii::hp::DoFHandler &dof_handler, - const unsigned int vertex_index, - const unsigned int fe_index, - const unsigned int local_index) - { - Assert ( (fe_index != dealii::hp::DoFHandler::default_fe_index), - ExcMessage ("You need to specify a FE index when working " - "with hp DoFHandlers")); - Assert (dof_handler.finite_elements != 0, - ExcMessage ("No finite element collection is associated with " - "this DoFHandler")); - Assert (local_index < (*dof_handler.finite_elements)[fe_index].dofs_per_vertex, - ExcIndexRange(local_index, 0, - (*dof_handler.finite_elements)[fe_index].dofs_per_vertex)); - Assert (vertex_index < dof_handler.vertex_dofs_offsets.size(), - ExcIndexRange (vertex_index, 0, - dof_handler.vertex_dofs_offsets.size())); - Assert (dof_handler.vertex_dofs_offsets[vertex_index] != - numbers::invalid_unsigned_int, - ExcMessage ("This vertex is unused and has no DoFs associated with it")); - - // hop along the list of index - // sets until we find the one - // with the correct fe_index, and - // then poke into that - // part. trigger an exception if - // we can't find a set for this - // particular fe_index - const unsigned int starting_offset = dof_handler.vertex_dofs_offsets[vertex_index]; - const types::global_dof_index *pointer = &dof_handler.vertex_dofs[starting_offset]; - while (true) - { - Assert (pointer <= &dof_handler.vertex_dofs.back(), ExcInternalError()); - - Assert((*pointer)::max(), ExcInternalError()); - const unsigned int this_fe_index = static_cast(*pointer); - - Assert (this_fe_index != numbers::invalid_unsigned_int, - ExcInternalError()); - Assert (this_fe_index < dof_handler.finite_elements->size(), - ExcInternalError()); - - if (this_fe_index == fe_index) - return *(pointer + 1 + local_index); - else - pointer += (*dof_handler.finite_elements)[this_fe_index].dofs_per_vertex + 1; - } - } - - - /** - * Return the number of - * different finite elements - * that are active on a given - * vertex. - */ - template - static - unsigned int - n_active_vertex_fe_indices (const dealii::hp::DoFHandler &dof_handler, - const unsigned int vertex_index) - { - Assert (dof_handler.finite_elements != 0, - ExcMessage ("No finite element collection is associated with " - "this DoFHandler")); - - // if this vertex is unused, return 0 - if (dof_handler.vertex_dofs_offsets[vertex_index] == numbers::invalid_unsigned_int) - return 0; - - // hop along the list of index - // sets and count the number of - // hops - const unsigned int starting_offset = dof_handler.vertex_dofs_offsets[vertex_index]; - const types::global_dof_index *pointer = &dof_handler.vertex_dofs[starting_offset]; - - Assert (*pointer != numbers::invalid_unsigned_int, - ExcInternalError()); - - unsigned int counter = 0; - while (true) - { - Assert (pointer <= &dof_handler.vertex_dofs.back(), ExcInternalError()); - - Assert((*pointer)::max(), ExcInternalError()); - const unsigned int this_fe_index = static_cast(*pointer); - - if (this_fe_index == numbers::invalid_unsigned_int) - return counter; - else - { - pointer += (*dof_handler.finite_elements)[this_fe_index].dofs_per_vertex + 1; - ++counter; - } - } - } - - - - /** - * Return the fe index of the - * n-th finite element active - * on a given vertex. - */ - template - static - unsigned int - nth_active_vertex_fe_index (const dealii::hp::DoFHandler &dof_handler, - const unsigned int vertex_index, - const unsigned int n) - { - Assert (dof_handler.finite_elements != 0, - ExcMessage ("No finite element collection is associated with " - "this DoFHandler")); - Assert (n < n_active_vertex_fe_indices(dof_handler, vertex_index), - ExcIndexRange (n, 0, n_active_vertex_fe_indices(dof_handler, - vertex_index))); - // make sure we don't ask on - // unused vertices - Assert (dof_handler.vertex_dofs_offsets[vertex_index] != - numbers::invalid_unsigned_int, - ExcInternalError()); - - // hop along the list of index - // sets and count the number of - // hops - const unsigned int starting_offset = dof_handler.vertex_dofs_offsets[vertex_index]; - const types::global_dof_index *pointer = &dof_handler.vertex_dofs[starting_offset]; - - Assert (*pointer != numbers::invalid_unsigned_int, - ExcInternalError()); - - unsigned int counter = 0; - while (true) - { - Assert (pointer <= &dof_handler.vertex_dofs.back(), ExcInternalError()); - - Assert((*pointer)::max(), ExcInternalError()); - const unsigned int this_fe_index = static_cast(*pointer); - - Assert (this_fe_index < dof_handler.finite_elements->size(), - ExcInternalError()); - - if (counter == n) - return this_fe_index; - - Assert (this_fe_index != numbers::invalid_unsigned_int, - ExcInternalError()); - - pointer += (*dof_handler.finite_elements)[this_fe_index].dofs_per_vertex + 1; - ++counter; - } - } - - - - /** - * Return whether a particular - * finite element index is - * active on the specified - * vertex. - */ - template - static - bool - fe_is_active_on_vertex (const dealii::hp::DoFHandler &dof_handler, - const unsigned int vertex_index, - const unsigned int fe_index) - { - Assert ( (fe_index != dealii::hp::DoFHandler::default_fe_index), - ExcMessage ("You need to specify a FE index when working " - "with hp DoFHandlers")); - Assert (dof_handler.finite_elements != 0, - ExcMessage ("No finite element collection is associated with " - "this DoFHandler")); - Assert (fe_index < dof_handler.finite_elements->size(), - ExcInternalError()); - - // make sure we don't ask on - // unused vertices - Assert (dof_handler.vertex_dofs_offsets[vertex_index] != - numbers::invalid_unsigned_int, - ExcInternalError()); - - // hop along the list of index - // sets and see whether we find - // the given index - const unsigned int starting_offset = dof_handler.vertex_dofs_offsets[vertex_index]; - const types::global_dof_index *pointer = &dof_handler.vertex_dofs[starting_offset]; - - Assert (*pointer != numbers::invalid_unsigned_int, - ExcInternalError()); - - while (true) - { - Assert (pointer <= &dof_handler.vertex_dofs.back(), ExcInternalError()); - - Assert((*pointer)::max(), ExcInternalError()); - const unsigned int this_fe_index = static_cast(*pointer); - - Assert (this_fe_index < dof_handler.finite_elements->size(), - ExcInternalError()); - - if (this_fe_index == numbers::invalid_unsigned_int) - return false; - else - if (this_fe_index == fe_index) - return true; - else - pointer += (*dof_handler.finite_elements)[this_fe_index].dofs_per_vertex + 1; - } - } + /** + * Implementations of the + * get_dof_index/set_dof_index functions. + */ + template + static + types::global_dof_index + get_dof_index (const dealii::DoFHandler<1,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + dealii::internal::int2type<1>) + { + return dof_handler.levels[obj_level]->dof_object. + get_dof_index (dof_handler, + obj_index, + fe_index, + local_index); + } + + + template + static + void + set_dof_index (const dealii::DoFHandler<1,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + dealii::internal::int2type<1>, + const types::global_dof_index global_index) + { + dof_handler.levels[obj_level]->dof_object. + set_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + global_index); + } + + + template + static + types::global_dof_index + get_dof_index (const dealii::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + dealii::internal::int2type<1>) + { + // faces have no levels + Assert (obj_level == 0, ExcInternalError()); + return dof_handler.faces->lines. + get_dof_index (dof_handler, + obj_index, + fe_index, + local_index); + } + + + template + static + void + set_dof_index (const dealii::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + dealii::internal::int2type<1>, + const types::global_dof_index global_index) + { + // faces have no levels + Assert (obj_level == 0, ExcInternalError()); + dof_handler.faces->lines. + set_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + global_index); + } + + + template + static + types::global_dof_index + get_dof_index (const dealii::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + dealii::internal::int2type<2>) + { + return dof_handler.levels[obj_level]->dof_object. + get_dof_index (dof_handler, + obj_index, + fe_index, + local_index); + } + + + template + static + void + set_dof_index (const dealii::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + dealii::internal::int2type<2>, + const types::global_dof_index global_index) + { + dof_handler.levels[obj_level]->dof_object. + set_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + global_index); + } + + + template + static + types::global_dof_index + get_dof_index (const dealii::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + dealii::internal::int2type<1>) + { + // faces have no levels + Assert (obj_level == 0, ExcInternalError()); + return dof_handler.faces->lines. + get_dof_index (dof_handler, + obj_index, + fe_index, + local_index); + } + + + template + static + void + set_dof_index (const dealii::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + dealii::internal::int2type<1>, + const types::global_dof_index global_index) + { + // faces have no levels + Assert (obj_level == 0, ExcInternalError()); + dof_handler.faces->lines. + set_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + global_index); + } + + + + template + static + types::global_dof_index + get_dof_index (const dealii::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + dealii::internal::int2type<2>) + { + // faces have no levels + Assert (obj_level == 0, ExcInternalError()); + return dof_handler.faces->quads. + get_dof_index (dof_handler, + obj_index, + fe_index, + local_index); + } + + + template + static + void + set_dof_index (const dealii::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + dealii::internal::int2type<2>, + const types::global_dof_index global_index) + { + // faces have no levels + Assert (obj_level == 0, ExcInternalError()); + dof_handler.faces->quads. + set_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + global_index); + } + + + + template + static + types::global_dof_index + get_dof_index (const dealii::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + dealii::internal::int2type<3>) + { + return dof_handler.levels[obj_level]->dof_object. + get_dof_index (dof_handler, + obj_index, + fe_index, + local_index); + } + + + template + static + void + set_dof_index (const dealii::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + dealii::internal::int2type<3>, + const types::global_dof_index global_index) + { + dof_handler.levels[obj_level]->dof_object. + set_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + global_index); + } + + + template + static + types::global_dof_index + get_dof_index (const dealii::hp::DoFHandler<1,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + const dealii::internal::int2type<1> &) + { + return dof_handler.levels[obj_level]->dof_object. + get_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + obj_level); + } + + + template + static + void + set_dof_index (const dealii::hp::DoFHandler<1,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + const dealii::internal::int2type<1> &, + const types::global_dof_index global_index) + { + dof_handler.levels[obj_level]->dof_object. + set_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + global_index, + obj_level); + } + + + template + static + types::global_dof_index + get_dof_index (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + const dealii::internal::int2type<1> &) + { + return dof_handler.faces->lines. + get_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + obj_level); + } + + + template + static + void + set_dof_index (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + const dealii::internal::int2type<1> &, + const types::global_dof_index global_index) + { + dof_handler.faces->lines. + set_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + global_index, + obj_level); + } + + + template + static + types::global_dof_index + get_dof_index (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + const dealii::internal::int2type<2> &) + { + return dof_handler.levels[obj_level]->dof_object. + get_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + obj_level); + } + + + template + static + void + set_dof_index (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + const dealii::internal::int2type<2> &, + const types::global_dof_index global_index) + { + dof_handler.levels[obj_level]->dof_object. + set_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + global_index, + obj_level); + } + + + template + static + types::global_dof_index + get_dof_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + const dealii::internal::int2type<1> &) + { + return dof_handler.faces->lines. + get_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + obj_level); + } + + + template + static + void + set_dof_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + const dealii::internal::int2type<1> &, + const types::global_dof_index global_index) + { + dof_handler.faces->lines. + set_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + global_index, + obj_level); + } + + + template + static + types::global_dof_index + get_dof_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + const dealii::internal::int2type<2> &) + { + return dof_handler.faces->quads. + get_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + obj_level); + } + + + template + static + void + set_dof_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + const dealii::internal::int2type<2> &, + const types::global_dof_index global_index) + { + dof_handler.faces->quads. + set_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + global_index, + obj_level); + } + + + template + static + types::global_dof_index + get_dof_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + const dealii::internal::int2type<3> &) + { + return dof_handler.levels[obj_level]->dof_object. + get_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + obj_level); + } + + + template + static + void + set_dof_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const unsigned int local_index, + const dealii::internal::int2type<3> &, + const types::global_dof_index global_index) + { + dof_handler.levels[obj_level]->dof_object. + set_dof_index (dof_handler, + obj_index, + fe_index, + local_index, + global_index, + obj_level); + } + + + template + static + bool + fe_index_is_active (const dealii::DoFHandler &, + const unsigned int, + const unsigned int, + const unsigned int fe_index, + const dealii::internal::int2type &) + { + return (fe_index == 0); + } + + + + template + static + unsigned int + n_active_fe_indices (const dealii::DoFHandler &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const dealii::internal::int2type &) + { + // check that the object we look + // at is in fact active. the + // problem is that we have + // templatized on the + // dimensionality of the object, + // so it may be a cell, a face, + // or a line. we have a bit of + // trouble doing this all in the + // generic case, so only check if + // it is either a cell or a + // line. the only case this + // leaves out is faces in 3d -- + // let's hope that this never is + // a problem + Assert ((dim==structdim + ? + typename + dealii::internal::DoFHandler:: + Iterators >:: + raw_cell_iterator (&dof_handler.get_tria(), + obj_level, + obj_index, + &dof_handler)->used() + : + (structdim==1 + ? + typename + dealii::internal::DoFHandler:: + Iterators >:: + raw_line_iterator (&dof_handler.get_tria(), + obj_level, + obj_index, + &dof_handler)->used() + : + true)) + == true, + ExcMessage ("This cell is not active and therefore can't be " + "queried for its active FE indices")); + return 1; + } + + + + template + static + unsigned int + nth_active_fe_index (const dealii::DoFHandler &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int n, + const dealii::internal::int2type &) + { + // check that the object we look + // at is in fact active. the + // problem is that we have + // templatized on the + // dimensionality of the object, + // so it may be a cell, a face, + // or a line. we have a bit of + // trouble doing this all in the + // generic case, so only check if + // it is either a cell or a + // line. the only case this + // leaves out is faces in 3d -- + // let's hope that this never is + // a problem + Assert ((dim==structdim + ? + typename + dealii::internal::DoFHandler:: + Iterators >:: + raw_cell_iterator (&dof_handler.get_tria(), + obj_level, + obj_index, + &dof_handler)->used() + : + (structdim==1 + ? + typename + dealii::internal::DoFHandler:: + Iterators >:: + raw_line_iterator (&dof_handler.get_tria(), + obj_level, + obj_index, + &dof_handler)->used() + : + true)) + == true, + ExcMessage ("This cell is not active and therefore can't be " + "queried for its active FE indices")); + Assert (n == 0, ExcIndexRange (n, 0, 1)); + + return dealii::DoFHandler::default_fe_index; + } + + + template + static + bool + fe_index_is_active (const dealii::hp::DoFHandler<1,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const dealii::internal::int2type<1> &) + { + return dof_handler.levels[obj_level]->dof_object.fe_index_is_active(dof_handler, + obj_index, + fe_index, + obj_level); + } + + + template + static + unsigned int + n_active_fe_indices (const dealii::hp::DoFHandler<1,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const dealii::internal::int2type<1> &) + { + return dof_handler.levels[obj_level]->dof_object.n_active_fe_indices (dof_handler, + obj_index); + } + + + + template + static + unsigned int + nth_active_fe_index (const dealii::hp::DoFHandler<1,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int n, + const dealii::internal::int2type<1> &) + { + return dof_handler.levels[obj_level]->dof_object.nth_active_fe_index (dof_handler, + obj_level, + obj_index, + n); + } + + + template + static + bool + fe_index_is_active (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const dealii::internal::int2type<1> &) + { + return dof_handler.faces->lines.fe_index_is_active(dof_handler, + obj_index, + fe_index, + obj_level); + } + + + template + static + unsigned int + n_active_fe_indices (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, + const unsigned int , + const unsigned int obj_index, + const dealii::internal::int2type<1> &) + { + return dof_handler.faces->lines.n_active_fe_indices (dof_handler, + obj_index); + } + + + template + static + unsigned int + nth_active_fe_index (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int n, + const dealii::internal::int2type<1> &) + { + return dof_handler.faces->lines.nth_active_fe_index (dof_handler, + obj_level, + obj_index, + n); + } + + + + template + static + bool + fe_index_is_active (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const dealii::internal::int2type<2> &) + { + return dof_handler.levels[obj_level]->dof_object.fe_index_is_active(dof_handler, + obj_index, + fe_index, + obj_level); + } + + + template + static + unsigned int + n_active_fe_indices (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const dealii::internal::int2type<2> &) + { + return dof_handler.levels[obj_level]->dof_object.n_active_fe_indices (dof_handler, + obj_index); + } + + + + template + static + unsigned int + nth_active_fe_index (const dealii::hp::DoFHandler<2,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int n, + const dealii::internal::int2type<2> &) + { + return dof_handler.levels[obj_level]->dof_object.nth_active_fe_index (dof_handler, + obj_level, + obj_index, + n); + } + + + + template + static + bool + fe_index_is_active (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const dealii::internal::int2type<1> &) + { + return dof_handler.faces->lines.fe_index_is_active(dof_handler, + obj_index, + fe_index, + obj_level); + } + + + template + static + unsigned int + n_active_fe_indices (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int, + const unsigned int obj_index, + const dealii::internal::int2type<1> &) + { + return dof_handler.faces->lines.n_active_fe_indices (dof_handler, + obj_index); + } + + + + template + static + unsigned int + nth_active_fe_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int n, + const dealii::internal::int2type<1> &) + { + return dof_handler.faces->lines.nth_active_fe_index (dof_handler, + obj_level, + obj_index, + n); + } + + + + template + static + bool + fe_index_is_active (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const dealii::internal::int2type<2> &) + { + return dof_handler.faces->quads.fe_index_is_active(dof_handler, + obj_index, + fe_index, + obj_level); + } + + template + static + bool + fe_index_is_active (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int fe_index, + const dealii::internal::int2type<3> &) + { + return dof_handler.levels[obj_level]->dof_object.fe_index_is_active(dof_handler, + obj_index, + fe_index, + obj_level); + } + + + template + static + unsigned int + n_active_fe_indices (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int , + const unsigned int obj_index, + const dealii::internal::int2type<2> &) + { + return dof_handler.faces->quads.n_active_fe_indices (dof_handler, + obj_index); + } + + + + template + static + unsigned int + nth_active_fe_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int n, + const dealii::internal::int2type<2> &) + { + return dof_handler.faces->quads.nth_active_fe_index (dof_handler, + obj_level, + obj_index, + n); + } + + + + template + static + unsigned int + n_active_fe_indices (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const dealii::internal::int2type<3> &) + { + return dof_handler.levels[obj_level]->dof_object.n_active_fe_indices (dof_handler, + obj_index); + } + + + + template + static + unsigned int + nth_active_fe_index (const dealii::hp::DoFHandler<3,spacedim> &dof_handler, + const unsigned int obj_level, + const unsigned int obj_index, + const unsigned int n, + const dealii::internal::int2type<3> &) + { + return dof_handler.levels[obj_level]->dof_object.nth_active_fe_index (dof_handler, + obj_level, + obj_index, + n); + } + + /** + * Set the @p local_index-th + * degree of freedom + * corresponding to the finite + * element specified by @p + * fe_index on the vertex with + * global number @p + * vertex_index to @p + * global_index. + */ + template + static + void + set_vertex_dof_index (dealii::DoFHandler &dof_handler, + const unsigned int vertex_index, + const unsigned int fe_index, + const unsigned int local_index, + const types::global_dof_index global_index) + { + Assert ((fe_index == dealii::DoFHandler::default_fe_index), + ExcMessage ("Only the default FE index is allowed for non-hp DoFHandler objects")); + Assert (dof_handler.selected_fe != 0, + ExcMessage ("No finite element collection is associated with " + "this DoFHandler")); + Assert (local_index < dof_handler.selected_fe->dofs_per_vertex, + ExcIndexRange(local_index, 0, + dof_handler.selected_fe->dofs_per_vertex)); + + dof_handler.vertex_dofs[vertex_index * + dof_handler.selected_fe->dofs_per_vertex + + local_index] + = global_index; + } + + + template + static + void + set_vertex_dof_index (dealii::hp::DoFHandler &dof_handler, + const unsigned int vertex_index, + const unsigned int fe_index, + const unsigned int local_index, + const types::global_dof_index global_index) + { + Assert ( (fe_index != dealii::hp::DoFHandler::default_fe_index), + ExcMessage ("You need to specify a FE index when working " + "with hp DoFHandlers")); + Assert (dof_handler.finite_elements != 0, + ExcMessage ("No finite element collection is associated with " + "this DoFHandler")); + Assert (local_index < (*dof_handler.finite_elements)[fe_index].dofs_per_vertex, + ExcIndexRange(local_index, 0, + (*dof_handler.finite_elements)[fe_index].dofs_per_vertex)); + Assert (fe_index < dof_handler.finite_elements->size(), + ExcInternalError()); + Assert (dof_handler.vertex_dofs_offsets[vertex_index] != + numbers::invalid_unsigned_int, + ExcMessage ("This vertex is unused and has no DoFs associated with it")); + + // hop along the list of index + // sets until we find the one + // with the correct fe_index, and + // then poke into that + // part. trigger an exception if + // we can't find a set for this + // particular fe_index + const unsigned int starting_offset = dof_handler.vertex_dofs_offsets[vertex_index]; + types::global_dof_index *pointer = &dof_handler.vertex_dofs[starting_offset]; + while (true) + { + Assert (pointer <= &dof_handler.vertex_dofs.back(), ExcInternalError()); + + // a fe index is always small + Assert((*pointer)::max(), ExcInternalError()); + const unsigned int this_fe_index = static_cast(*pointer); + + Assert (this_fe_index != numbers::invalid_unsigned_int, + ExcInternalError()); + Assert (this_fe_index < dof_handler.finite_elements->size(), + ExcInternalError()); + + if (this_fe_index == fe_index) + { + *(pointer + 1 + local_index) = global_index; + return; + } + else + pointer += (*dof_handler.finite_elements)[this_fe_index].dofs_per_vertex + 1; + } + } + + + /** + * Get the @p local_index-th + * degree of freedom + * corresponding to the finite + * element specified by @p + * fe_index on the vertex with + * global number @p + * vertex_index to @p + * global_index. + */ + + template + static + unsigned int + get_vertex_dof_index (const dealii::DoFHandler &dof_handler, + const unsigned int vertex_index, + const unsigned int fe_index, + const unsigned int local_index) + { + Assert ((fe_index == dealii::DoFHandler::default_fe_index), + ExcMessage ("Only the default FE index is allowed for non-hp DoFHandler objects")); + Assert (dof_handler.selected_fe != 0, + ExcMessage ("No finite element collection is associated with " + "this DoFHandler")); + Assert (local_index < dof_handler.selected_fe->dofs_per_vertex, + ExcIndexRange(local_index, 0, + dof_handler.selected_fe->dofs_per_vertex)); + + return + dof_handler.vertex_dofs[vertex_index * + dof_handler.selected_fe->dofs_per_vertex + + local_index]; + } + + + template + static + unsigned int + get_vertex_dof_index (const dealii::hp::DoFHandler &dof_handler, + const unsigned int vertex_index, + const unsigned int fe_index, + const unsigned int local_index) + { + Assert ( (fe_index != dealii::hp::DoFHandler::default_fe_index), + ExcMessage ("You need to specify a FE index when working " + "with hp DoFHandlers")); + Assert (dof_handler.finite_elements != 0, + ExcMessage ("No finite element collection is associated with " + "this DoFHandler")); + Assert (local_index < (*dof_handler.finite_elements)[fe_index].dofs_per_vertex, + ExcIndexRange(local_index, 0, + (*dof_handler.finite_elements)[fe_index].dofs_per_vertex)); + Assert (vertex_index < dof_handler.vertex_dofs_offsets.size(), + ExcIndexRange (vertex_index, 0, + dof_handler.vertex_dofs_offsets.size())); + Assert (dof_handler.vertex_dofs_offsets[vertex_index] != + numbers::invalid_unsigned_int, + ExcMessage ("This vertex is unused and has no DoFs associated with it")); + + // hop along the list of index + // sets until we find the one + // with the correct fe_index, and + // then poke into that + // part. trigger an exception if + // we can't find a set for this + // particular fe_index + const unsigned int starting_offset = dof_handler.vertex_dofs_offsets[vertex_index]; + const types::global_dof_index *pointer = &dof_handler.vertex_dofs[starting_offset]; + while (true) + { + Assert (pointer <= &dof_handler.vertex_dofs.back(), ExcInternalError()); + + Assert((*pointer)::max(), ExcInternalError()); + const unsigned int this_fe_index = static_cast(*pointer); + + Assert (this_fe_index != numbers::invalid_unsigned_int, + ExcInternalError()); + Assert (this_fe_index < dof_handler.finite_elements->size(), + ExcInternalError()); + + if (this_fe_index == fe_index) + return *(pointer + 1 + local_index); + else + pointer += (*dof_handler.finite_elements)[this_fe_index].dofs_per_vertex + 1; + } + } + + + /** + * Return the number of + * different finite elements + * that are active on a given + * vertex. + */ + template + static + unsigned int + n_active_vertex_fe_indices (const dealii::hp::DoFHandler &dof_handler, + const unsigned int vertex_index) + { + Assert (dof_handler.finite_elements != 0, + ExcMessage ("No finite element collection is associated with " + "this DoFHandler")); + + // if this vertex is unused, return 0 + if (dof_handler.vertex_dofs_offsets[vertex_index] == numbers::invalid_unsigned_int) + return 0; + + // hop along the list of index + // sets and count the number of + // hops + const unsigned int starting_offset = dof_handler.vertex_dofs_offsets[vertex_index]; + const types::global_dof_index *pointer = &dof_handler.vertex_dofs[starting_offset]; + + Assert (*pointer != numbers::invalid_unsigned_int, + ExcInternalError()); + + unsigned int counter = 0; + while (true) + { + Assert (pointer <= &dof_handler.vertex_dofs.back(), ExcInternalError()); + + Assert((*pointer)::max(), ExcInternalError()); + const unsigned int this_fe_index = static_cast(*pointer); + + if (this_fe_index == numbers::invalid_unsigned_int) + return counter; + else + { + pointer += (*dof_handler.finite_elements)[this_fe_index].dofs_per_vertex + 1; + ++counter; + } + } + } + + + + /** + * Return the fe index of the + * n-th finite element active + * on a given vertex. + */ + template + static + unsigned int + nth_active_vertex_fe_index (const dealii::hp::DoFHandler &dof_handler, + const unsigned int vertex_index, + const unsigned int n) + { + Assert (dof_handler.finite_elements != 0, + ExcMessage ("No finite element collection is associated with " + "this DoFHandler")); + Assert (n < n_active_vertex_fe_indices(dof_handler, vertex_index), + ExcIndexRange (n, 0, n_active_vertex_fe_indices(dof_handler, + vertex_index))); + // make sure we don't ask on + // unused vertices + Assert (dof_handler.vertex_dofs_offsets[vertex_index] != + numbers::invalid_unsigned_int, + ExcInternalError()); + + // hop along the list of index + // sets and count the number of + // hops + const unsigned int starting_offset = dof_handler.vertex_dofs_offsets[vertex_index]; + const types::global_dof_index *pointer = &dof_handler.vertex_dofs[starting_offset]; + + Assert (*pointer != numbers::invalid_unsigned_int, + ExcInternalError()); + + unsigned int counter = 0; + while (true) + { + Assert (pointer <= &dof_handler.vertex_dofs.back(), ExcInternalError()); + + Assert((*pointer)::max(), ExcInternalError()); + const unsigned int this_fe_index = static_cast(*pointer); + + Assert (this_fe_index < dof_handler.finite_elements->size(), + ExcInternalError()); + + if (counter == n) + return this_fe_index; + + Assert (this_fe_index != numbers::invalid_unsigned_int, + ExcInternalError()); + + pointer += (*dof_handler.finite_elements)[this_fe_index].dofs_per_vertex + 1; + ++counter; + } + } + + + + /** + * Return whether a particular + * finite element index is + * active on the specified + * vertex. + */ + template + static + bool + fe_is_active_on_vertex (const dealii::hp::DoFHandler &dof_handler, + const unsigned int vertex_index, + const unsigned int fe_index) + { + Assert ( (fe_index != dealii::hp::DoFHandler::default_fe_index), + ExcMessage ("You need to specify a FE index when working " + "with hp DoFHandlers")); + Assert (dof_handler.finite_elements != 0, + ExcMessage ("No finite element collection is associated with " + "this DoFHandler")); + Assert (fe_index < dof_handler.finite_elements->size(), + ExcInternalError()); + + // make sure we don't ask on + // unused vertices + Assert (dof_handler.vertex_dofs_offsets[vertex_index] != + numbers::invalid_unsigned_int, + ExcInternalError()); + + // hop along the list of index + // sets and see whether we find + // the given index + const unsigned int starting_offset = dof_handler.vertex_dofs_offsets[vertex_index]; + const types::global_dof_index *pointer = &dof_handler.vertex_dofs[starting_offset]; + + Assert (*pointer != numbers::invalid_unsigned_int, + ExcInternalError()); + + while (true) + { + Assert (pointer <= &dof_handler.vertex_dofs.back(), ExcInternalError()); + + Assert((*pointer)::max(), ExcInternalError()); + const unsigned int this_fe_index = static_cast(*pointer); + + Assert (this_fe_index < dof_handler.finite_elements->size(), + ExcInternalError()); + + if (this_fe_index == numbers::invalid_unsigned_int) + return false; + else if (this_fe_index == fe_index) + return true; + else + pointer += (*dof_handler.finite_elements)[this_fe_index].dofs_per_vertex + 1; + } + } }; } @@ -1448,21 +1447,22 @@ template inline types::global_dof_index DoFAccessor::dof_index (const unsigned int i, - const unsigned int fe_index) const + const unsigned int fe_index) const { - // access the respective DoF + // access the respective DoF return dealii::internal::DoFAccessor::Implementation::get_dof_index (*this->dof_handler, - this->level(), - this->present_index, - fe_index, - i, - dealii::internal::int2type()); + this->level(), + this->present_index, + fe_index, + i, + dealii::internal::int2type()); } template inline -types::global_dof_index DoFAccessor::mg_dof_index (const int level, const unsigned int i) const { +types::global_dof_index DoFAccessor::mg_dof_index (const int level, const unsigned int i) const +{ return this->dof_handler->template get_dof_index (level, this->present_index, 0, i); } @@ -1471,17 +1471,17 @@ template inline void DoFAccessor::set_dof_index (const unsigned int i, - const types::global_dof_index index, - const unsigned int fe_index) const + const types::global_dof_index index, + const unsigned int fe_index) const { - // access the respective DoF + // access the respective DoF dealii::internal::DoFAccessor::Implementation::set_dof_index (*this->dof_handler, - this->level(), - this->present_index, - fe_index, - i, - dealii::internal::int2type(), - index); + this->level(), + this->present_index, + fe_index, + i, + dealii::internal::int2type(), + index); } @@ -1491,13 +1491,13 @@ inline unsigned int DoFAccessor::n_active_fe_indices () const { - // access the respective DoF + // access the respective DoF return dealii::internal::DoFAccessor::Implementation:: n_active_fe_indices (*this->dof_handler, - this->level(), - this->present_index, - dealii::internal::int2type()); + this->level(), + this->present_index, + dealii::internal::int2type()); } @@ -1507,14 +1507,14 @@ inline unsigned int DoFAccessor::nth_active_fe_index (const unsigned int n) const { - // access the respective DoF + // access the respective DoF return dealii::internal::DoFAccessor::Implementation:: nth_active_fe_index (*this->dof_handler, - this->level(), - this->present_index, - n, - dealii::internal::int2type()); + this->level(), + this->present_index, + n, + dealii::internal::int2type()); } @@ -1524,14 +1524,14 @@ inline bool DoFAccessor::fe_index_is_active (const unsigned int fe_index) const { - // access the respective DoF + // access the respective DoF return dealii::internal::DoFAccessor::Implementation:: fe_index_is_active (*this->dof_handler, - this->level(), - this->present_index, - fe_index, - dealii::internal::int2type()); + this->level(), + this->present_index, + fe_index, + dealii::internal::int2type()); } @@ -1540,8 +1540,8 @@ template inline types::global_dof_index DoFAccessor::vertex_dof_index (const unsigned int vertex, - const unsigned int i, - const unsigned int fe_index) const + const unsigned int i, + const unsigned int fe_index) const { return dealii::internal::DoFAccessor::Implementation::get_vertex_dof_index @@ -1555,7 +1555,8 @@ DoFAccessor::vertex_dof_index (const unsigned int vertex, template inline types::global_dof_index -DoFAccessor::mg_vertex_dof_index (const int level, const unsigned int vertex, const unsigned int i, const unsigned int fe_index) const { +DoFAccessor::mg_vertex_dof_index (const int level, const unsigned int vertex, const unsigned int i, const unsigned int fe_index) const +{ Assert (this->dof_handler != 0, ExcInvalidObject ()); Assert (&this->dof_handler->get_fe () != 0, ExcInvalidObject ()); Assert (vertex < GeometryInfo::vertices_per_cell, ExcIndexRange (vertex, 0, GeometryInfo::vertices_per_cell)); @@ -1568,23 +1569,24 @@ template inline void DoFAccessor::set_vertex_dof_index (const unsigned int vertex, - const unsigned int i, - const types::global_dof_index index, - const unsigned int fe_index) const + const unsigned int i, + const types::global_dof_index index, + const unsigned int fe_index) const { dealii::internal::DoFAccessor::Implementation::set_vertex_dof_index - (*this->dof_handler, - this->vertex_index(vertex), - fe_index, - i, - index); + (*this->dof_handler, + this->vertex_index(vertex), + fe_index, + i, + index); } template inline void -DoFAccessor::set_mg_vertex_dof_index (const int level, const unsigned int vertex, const unsigned int i, const types::global_dof_index index, const unsigned int fe_index) const { +DoFAccessor::set_mg_vertex_dof_index (const int level, const unsigned int vertex, const unsigned int i, const types::global_dof_index index, const unsigned int fe_index) const +{ Assert (this->dof_handler != 0, ExcInvalidObject ()); Assert (&this->dof_handler->get_fe () != 0, ExcInvalidObject ()); Assert (vertex < GeometryInfo::vertices_per_cell, ExcIndexRange (vertex, 0, GeometryInfo::vertices_per_cell)); @@ -1596,7 +1598,8 @@ DoFAccessor::set_mg_vertex_dof_index (const int level, const unsi template inline void -DoFAccessor::set_mg_dof_index (const int level, const unsigned int i, const types::global_dof_index index) const { +DoFAccessor::set_mg_dof_index (const int level, const unsigned int i, const types::global_dof_index index) const +{ this->dof_handler->template set_dof_index (level, this->present_index, 0, i, index); } @@ -1609,7 +1612,7 @@ namespace internal inline const FiniteElement & get_fe (const FiniteElement &fe, - const unsigned int) + const unsigned int) { return fe; } @@ -1620,7 +1623,7 @@ namespace internal inline const FiniteElement & get_fe (const dealii::hp::FECollection &fe, - const unsigned int index) + const unsigned int index) { return fe[index]; } @@ -1634,7 +1637,7 @@ const FiniteElement & DoFAccessor::get_fe (const unsigned int fe_index) const { Assert (fe_index_is_active (fe_index) == true, - ExcMessage ("This function can only be called for active fe indices")); + ExcMessage ("This function can only be called for active fe indices")); return dealii::internal::DoFAccessor::get_fe (this->dof_handler->get_fe(), fe_index); } @@ -1647,113 +1650,114 @@ namespace internal { template void get_dof_indices (const dealii::DoFAccessor<1,DH> &accessor, - std::vector &dof_indices, - const unsigned int fe_index) + std::vector &dof_indices, + const unsigned int fe_index) { const unsigned int dofs_per_vertex = accessor.get_fe(fe_index).dofs_per_vertex, - dofs_per_line = accessor.get_fe(fe_index).dofs_per_line; + dofs_per_line = accessor.get_fe(fe_index).dofs_per_line; std::vector::iterator next = dof_indices.begin(); for (unsigned int vertex=0; vertex<2; ++vertex) - for (unsigned int d=0; d void get_dof_indices (const dealii::DoFAccessor<2,DH> &accessor, - std::vector &dof_indices, - const unsigned int fe_index) + std::vector &dof_indices, + const unsigned int fe_index) { const unsigned int dofs_per_vertex = accessor.get_fe(fe_index).dofs_per_vertex, - dofs_per_line = accessor.get_fe(fe_index).dofs_per_line, - dofs_per_quad = accessor.get_fe(fe_index).dofs_per_quad; + dofs_per_line = accessor.get_fe(fe_index).dofs_per_line, + dofs_per_quad = accessor.get_fe(fe_index).dofs_per_quad; std::vector::iterator next = dof_indices.begin(); for (unsigned int vertex=0; vertex<4; ++vertex) - for (unsigned int d=0; ddof_index(accessor.get_fe(fe_index). - adjust_line_dof_index_for_line_orientation(d, - accessor.line_orientation(line)), - fe_index); + for (unsigned int d=0; ddof_index(accessor.get_fe(fe_index). + adjust_line_dof_index_for_line_orientation(d, + accessor.line_orientation(line)), + fe_index); for (unsigned int d=0; d void get_dof_indices (const dealii::DoFAccessor<3,DH> &accessor, - std::vector &dof_indices, - const unsigned int fe_index) + std::vector &dof_indices, + const unsigned int fe_index) { const unsigned int dofs_per_vertex = accessor.get_fe(fe_index).dofs_per_vertex, - dofs_per_line = accessor.get_fe(fe_index).dofs_per_line, - dofs_per_quad = accessor.get_fe(fe_index).dofs_per_quad, - dofs_per_hex = accessor.get_fe(fe_index).dofs_per_hex; + dofs_per_line = accessor.get_fe(fe_index).dofs_per_line, + dofs_per_quad = accessor.get_fe(fe_index).dofs_per_quad, + dofs_per_hex = accessor.get_fe(fe_index).dofs_per_hex; std::vector::iterator next = dof_indices.begin(); for (unsigned int vertex=0; vertex<8; ++vertex) - for (unsigned int d=0; ddof_index(accessor.get_fe(fe_index). - adjust_line_dof_index_for_line_orientation(d, - accessor.line_orientation(line)),fe_index); - // now copy dof numbers from the face. for - // faces with the wrong orientation, we - // have already made sure that we're ok by - // picking the correct lines and vertices - // (this happens automatically in the - // line() and vertex() functions). however, - // if the face is in wrong orientation, we - // look at it in flipped orientation and we - // will have to adjust the shape function - // indices that we see to correspond to the - // correct (cell-local) ordering. The same - // applies, if the face_rotation or - // face_orientation is non-standard + for (unsigned int d=0; ddof_index(accessor.get_fe(fe_index). + adjust_line_dof_index_for_line_orientation(d, + accessor.line_orientation(line)),fe_index); + // now copy dof numbers from the face. for + // faces with the wrong orientation, we + // have already made sure that we're ok by + // picking the correct lines and vertices + // (this happens automatically in the + // line() and vertex() functions). however, + // if the face is in wrong orientation, we + // look at it in flipped orientation and we + // will have to adjust the shape function + // indices that we see to correspond to the + // correct (cell-local) ordering. The same + // applies, if the face_rotation or + // face_orientation is non-standard for (unsigned int quad=0; quad<6; ++quad) - for (unsigned int d=0; ddof_index(accessor.get_fe(fe_index). - adjust_quad_dof_index_for_face_orientation(d, - accessor.face_orientation(quad), - accessor.face_flip(quad), - accessor.face_rotation(quad)), - fe_index); + for (unsigned int d=0; ddof_index(accessor.get_fe(fe_index). + adjust_quad_dof_index_for_face_orientation(d, + accessor.face_orientation(quad), + accessor.face_flip(quad), + accessor.face_rotation(quad)), + fe_index); for (unsigned int d=0; d - void get_mg_dof_indices (const dealii::DoFAccessor<1, DH>& accessor, const int level, std::vector& dof_indices, const unsigned int fe_index) { - const FiniteElement& fe = accessor.get_dof_handler ().get_fe ()[fe_index]; + void get_mg_dof_indices (const dealii::DoFAccessor<1, DH> &accessor, const int level, std::vector &dof_indices, const unsigned int fe_index) + { + const FiniteElement &fe = accessor.get_dof_handler ().get_fe ()[fe_index]; std::vector::iterator next = dof_indices.begin (); for (unsigned int vertex = 0; vertex < GeometryInfo<1>::vertices_per_cell; ++vertex) @@ -1767,8 +1771,9 @@ namespace internal } template - void get_mg_dof_indices (const dealii::DoFAccessor<2, DH>& accessor, const int level, std::vector& dof_indices, const unsigned int fe_index) { - const FiniteElement& fe = accessor.get_dof_handler ().get_fe ()[fe_index]; + void get_mg_dof_indices (const dealii::DoFAccessor<2, DH> &accessor, const int level, std::vector &dof_indices, const unsigned int fe_index) + { + const FiniteElement &fe = accessor.get_dof_handler ().get_fe ()[fe_index]; std::vector::iterator next = dof_indices.begin (); for (unsigned int vertex = 0; vertex < GeometryInfo<2>::vertices_per_cell; ++vertex) @@ -1786,8 +1791,9 @@ namespace internal } template - void get_mg_dof_indices (const dealii::DoFAccessor<3, DH>& accessor, const int level, std::vector& dof_indices, const unsigned int fe_index) { - const FiniteElement& fe = accessor.get_dof_handler ().get_fe ()[fe_index]; + void get_mg_dof_indices (const dealii::DoFAccessor<3, DH> &accessor, const int level, std::vector &dof_indices, const unsigned int fe_index) + { + const FiniteElement &fe = accessor.get_dof_handler ().get_fe ()[fe_index]; std::vector::iterator next = dof_indices.begin (); for (unsigned int vertex = 0; vertex < GeometryInfo<3>::vertices_per_cell; ++vertex) @@ -1815,7 +1821,7 @@ template inline void DoFAccessor::get_dof_indices (std::vector &dof_indices, - const unsigned int fe_index) const + const unsigned int fe_index) const { Assert (static_cast(this->level()) < this->dof_handler->levels.size(), ExcMessage ("DoFHandler not initialized")); @@ -1827,79 +1833,84 @@ DoFAccessor::get_dof_indices (std::vector switch (structdim) { - case 1: - Assert (dof_indices.size() == - (2*this->dof_handler->get_fe()[fe_index].dofs_per_vertex + - this->dof_handler->get_fe()[fe_index].dofs_per_line), - ExcVectorDoesNotMatch()); - break; - case 2: - Assert (dof_indices.size() == - (4*this->dof_handler->get_fe()[fe_index].dofs_per_vertex + - 4*this->dof_handler->get_fe()[fe_index].dofs_per_line + - this->dof_handler->get_fe()[fe_index].dofs_per_quad), - ExcVectorDoesNotMatch()); - break; - case 3: - Assert (dof_indices.size() == - (8*this->dof_handler->get_fe()[fe_index].dofs_per_vertex + - 12*this->dof_handler->get_fe()[fe_index].dofs_per_line + - 6*this->dof_handler->get_fe()[fe_index].dofs_per_quad + - this->dof_handler->get_fe()[fe_index].dofs_per_hex), - ExcVectorDoesNotMatch()); - break; - default: - Assert (false, ExcNotImplemented()); + case 1: + Assert (dof_indices.size() == + (2*this->dof_handler->get_fe()[fe_index].dofs_per_vertex + + this->dof_handler->get_fe()[fe_index].dofs_per_line), + ExcVectorDoesNotMatch()); + break; + case 2: + Assert (dof_indices.size() == + (4*this->dof_handler->get_fe()[fe_index].dofs_per_vertex + + 4*this->dof_handler->get_fe()[fe_index].dofs_per_line + + this->dof_handler->get_fe()[fe_index].dofs_per_quad), + ExcVectorDoesNotMatch()); + break; + case 3: + Assert (dof_indices.size() == + (8*this->dof_handler->get_fe()[fe_index].dofs_per_vertex + + 12*this->dof_handler->get_fe()[fe_index].dofs_per_line + + 6*this->dof_handler->get_fe()[fe_index].dofs_per_quad + + this->dof_handler->get_fe()[fe_index].dofs_per_hex), + ExcVectorDoesNotMatch()); + break; + default: + Assert (false, ExcNotImplemented()); } - // this function really only makes - // sense if either a) there are - // degrees of freedom defined on - // the present object, or b) the - // object is non-active objects but - // all degrees of freedom are - // located on vertices, since - // otherwise there are degrees of - // freedom on sub-objects which are - // not allocated for this - // non-active thing + // this function really only makes + // sense if either a) there are + // degrees of freedom defined on + // the present object, or b) the + // object is non-active objects but + // all degrees of freedom are + // located on vertices, since + // otherwise there are degrees of + // freedom on sub-objects which are + // not allocated for this + // non-active thing Assert (this->fe_index_is_active (fe_index) - || - (this->dof_handler->get_fe()[fe_index].dofs_per_cell == - GeometryInfo::vertices_per_cell * - this->dof_handler->get_fe()[fe_index].dofs_per_vertex), - ExcInternalError()); + || + (this->dof_handler->get_fe()[fe_index].dofs_per_cell == + GeometryInfo::vertices_per_cell * + this->dof_handler->get_fe()[fe_index].dofs_per_vertex), + ExcInternalError()); - // now do the actual work + // now do the actual work dealii::internal::DoFAccessor::get_dof_indices (*this, dof_indices, fe_index); } template inline -void DoFAccessor::get_mg_dof_indices (const int level, std::vector& dof_indices, const unsigned int fe_index) const { +void DoFAccessor::get_mg_dof_indices (const int level, std::vector &dof_indices, const unsigned int fe_index) const +{ Assert (this->dof_handler != 0, ExcInvalidObject ()); Assert (&this->dof_handler->get_fe () != 0, ExcInvalidObject ()); - switch (structdim) { - case 1: { - Assert (dof_indices.size () == 2 * this->dof_handler->get_fe ()[fe_index].dofs_per_vertex + this->dof_handler->get_fe ()[fe_index].dofs_per_line, ExcVectorDoesNotMatch ()); - break; + switch (structdim) + { + case 1: + { + Assert (dof_indices.size () == 2 * this->dof_handler->get_fe ()[fe_index].dofs_per_vertex + this->dof_handler->get_fe ()[fe_index].dofs_per_line, ExcVectorDoesNotMatch ()); + break; } - case 2: { - Assert (dof_indices.size () == 4 * (this->dof_handler->get_fe ()[fe_index].dofs_per_vertex + this->dof_handler->get_fe ()[fe_index].dofs_per_line) + this->dof_handler->get_fe ()[fe_index].dofs_per_quad, ExcVectorDoesNotMatch ()); - break; + case 2: + { + Assert (dof_indices.size () == 4 * (this->dof_handler->get_fe ()[fe_index].dofs_per_vertex + this->dof_handler->get_fe ()[fe_index].dofs_per_line) + this->dof_handler->get_fe ()[fe_index].dofs_per_quad, ExcVectorDoesNotMatch ()); + break; } - case 3: { - Assert (dof_indices.size () == 8 * this->dof_handler->get_fe ()[fe_index].dofs_per_vertex + 12 * this->dof_handler->get_fe ()[fe_index].dofs_per_line + 6 * this->dof_handler->get_fe ()[fe_index].dofs_per_quad + this->dof_handler->get_fe ()[fe_index].dofs_per_hex, ExcVectorDoesNotMatch ()); - break; + case 3: + { + Assert (dof_indices.size () == 8 * this->dof_handler->get_fe ()[fe_index].dofs_per_vertex + 12 * this->dof_handler->get_fe ()[fe_index].dofs_per_line + 6 * this->dof_handler->get_fe ()[fe_index].dofs_per_quad + this->dof_handler->get_fe ()[fe_index].dofs_per_hex, ExcVectorDoesNotMatch ()); + break; } default: - Assert (false, ExcNotImplemented ()); - } + Assert (false, ExcNotImplemented ()); + } internal::DoFAccessor::get_mg_dof_indices (*this, level, dof_indices, fe_index); } @@ -1909,33 +1920,33 @@ inline typename dealii::internal::DoFHandler::Iterators::line_iterator DoFAccessor::line (const unsigned int i) const { - // if we are asking for a - // particular line and this object - // refers to a line, then the only - // valid index is i==0 and we - // should return *this + // if we are asking for a + // particular line and this object + // refers to a line, then the only + // valid index is i==0 and we + // should return *this if (structdim == 1) { Assert (i==0, ExcMessage ("You can only ask for line zero if the " - "current object is a line itself.")); + "current object is a line itself.")); return - typename dealii::internal::DoFHandler::Iterators::cell_iterator - (&this->get_triangulation(), - this->level(), - this->index(), - &this->get_dof_handler()); + typename dealii::internal::DoFHandler::Iterators::cell_iterator + (&this->get_triangulation(), + this->level(), + this->index(), + &this->get_dof_handler()); } - // checking of 'i' happens in - // line_index(i) + // checking of 'i' happens in + // line_index(i) return typename dealii::internal::DoFHandler::Iterators::line_iterator - ( - this->tria, - 0, // only sub-objects are allowed, which have no level - this->line_index(i), - this->dof_handler - ); + ( + this->tria, + 0, // only sub-objects are allowed, which have no level + this->line_index(i), + this->dof_handler + ); } @@ -1944,36 +1955,36 @@ inline typename dealii::internal::DoFHandler::Iterators::quad_iterator DoFAccessor::quad (const unsigned int i) const { - // if we are asking for a - // particular quad and this object - // refers to a quad, then the only - // valid index is i==0 and we - // should return *this + // if we are asking for a + // particular quad and this object + // refers to a quad, then the only + // valid index is i==0 and we + // should return *this if (structdim == 2) { Assert (i==0, ExcMessage ("You can only ask for quad zero if the " - "current object is a quad itself.")); + "current object is a quad itself.")); return - typename dealii::internal::DoFHandler::Iterators::cell_iterator - (&this->get_triangulation(), - this->level(), - this->index(), - &this->get_dof_handler()); + typename dealii::internal::DoFHandler::Iterators::cell_iterator + (&this->get_triangulation(), + this->level(), + this->index(), + &this->get_dof_handler()); } - // otherwise we need to be in - // structdim>=3 + // otherwise we need to be in + // structdim>=3 Assert (structdim > 2, ExcImpossibleInDim(structdim)); - // checking of 'i' happens in - // quad_index(i) + // checking of 'i' happens in + // quad_index(i) return typename dealii::internal::DoFHandler::Iterators::quad_iterator - ( - this->tria, - 0, // only sub-objects are allowed, which have no level - this->quad_index (i), - this->dof_handler - ); + ( + this->tria, + 0, // only sub-objects are allowed, which have no level + this->quad_index (i), + this->dof_handler + ); } @@ -1993,14 +2004,14 @@ template