From a230a59a4811d937dec45eaded356c63aaf98ad7 Mon Sep 17 00:00:00 2001 From: heltai Date: Sat, 25 May 2013 18:05:46 +0000 Subject: [PATCH] Merged from trunk git-svn-id: https://svn.dealii.org/branches/branch_manifold_id@29600 0785d39b-7218-0410-832d-ea1e28bc413d --- deal.II/README | 14 +- deal.II/VERSION | 1 + .../checks/check_01_compiler_features.cmake | 22 +- ...s.cmake => check_02_system_features.cmake} | 10 + ...ugs.cmake => check_03_compiler_bugs.cmake} | 0 deal.II/cmake/config/CMakeLists.txt | 33 +- ...mfpack.cmake => configure_1_umfpack.cmake} | 0 deal.II/cmake/setup_cached_variables.cmake | 9 +- .../cmake/setup_compiler_flags_intel.cmake | 9 +- deal.II/cmake/setup_deal_ii.cmake | 3 +- deal.II/doc/authors.html | 6 + deal.II/doc/doxygen/CMakeLists.txt | 2 + deal.II/doc/doxygen/tutorial/index.html | 3 - deal.II/doc/license.html | 828 ++++-- deal.II/doc/mail.html | 14 +- deal.II/doc/navbar.html | 20 +- deal.II/doc/news/changes.h | 190 +- deal.II/doc/publications/index.html | 27 +- deal.II/doc/readme.html | 81 +- deal.II/examples/step-26/step-26.cc | 36 +- deal.II/examples/step-42/step-42.cc | 2 +- deal.II/examples/step-48/step-48.cc | 13 +- deal.II/examples/step-50/CMakeLists.txt | 52 + deal.II/examples/step-50/step-50.cc | 52 +- .../include/deal.II/base/convergence_table.h | 192 +- deal.II/include/deal.II/base/data_out_base.h | 260 +- deal.II/include/deal.II/base/index_set.h | 30 +- deal.II/include/deal.II/base/mpi.h | 32 +- deal.II/include/deal.II/base/partitioner.h | 4 +- deal.II/include/deal.II/base/point.h | 29 +- deal.II/include/deal.II/base/polynomial.h | 244 +- .../deal.II/base/polynomials_piecewise.h | 224 ++ .../deal.II/base/tensor_product_polynomials.h | 443 +-- .../base/tensor_product_polynomials_const.h | 300 +-- deal.II/include/deal.II/base/vectorization.h | 4 +- deal.II/include/deal.II/dofs/dof_tools.h | 22 +- deal.II/include/deal.II/fe/fe.h | 2367 ++++++----------- deal.II/include/deal.II/fe/fe_face.h | 34 +- deal.II/include/deal.II/fe/fe_poly.h | 253 +- .../include/deal.II/fe/fe_poly.templates.h | 8 +- deal.II/include/deal.II/fe/fe_q.h | 377 +-- deal.II/include/deal.II/fe/fe_q_base.h | 285 ++ deal.II/include/deal.II/fe/fe_q_dg0.h | 403 +-- deal.II/include/deal.II/fe/fe_q_iso_q1.h | 155 ++ deal.II/include/deal.II/fe/fe_system.h | 745 ++---- deal.II/include/deal.II/fe/fe_tools.h | 19 +- deal.II/include/deal.II/fe/mapping_q.h | 2 +- deal.II/include/deal.II/grid/grid_in.h | 34 +- deal.II/include/deal.II/grid/grid_out.h | 61 +- deal.II/include/deal.II/grid/tria_accessor.h | 6 +- .../deal.II/grid/tria_accessor.templates.h | 2 + deal.II/include/deal.II/lac/arpack_solver.h | 4 +- .../include/deal.II/lac/block_matrix_base.h | 18 +- .../include/deal.II/lac/block_vector_base.h | 59 + .../include/deal.II/lac/chunk_sparse_matrix.h | 2019 +++++++++----- .../lac/chunk_sparse_matrix.templates.h | 460 ++-- .../deal.II/lac/chunk_sparsity_pattern.h | 1145 +++++--- .../include/deal.II/lac/constraint_matrix.h | 18 + .../deal.II/lac/constraint_matrix.templates.h | 104 +- .../deal.II/lac/full_matrix.templates.h | 271 +- deal.II/include/deal.II/lac/matrix_lib.h | 4 +- .../deal.II/lac/parallel_block_vector.h | 528 ++-- deal.II/include/deal.II/lac/parallel_vector.h | 1016 +++---- .../deal.II/lac/parallel_vector.templates.h | 75 +- .../deal.II/lac/petsc_parallel_vector.h | 13 + deal.II/include/deal.II/lac/petsc_vector.h | 15 + .../include/deal.II/lac/petsc_vector_base.h | 31 + deal.II/include/deal.II/lac/precondition.h | 446 ++-- deal.II/include/deal.II/lac/slepc_solver.h | 254 +- deal.II/include/deal.II/lac/solver_bicgstab.h | 98 +- deal.II/include/deal.II/lac/solver_gmres.h | 356 ++- .../include/deal.II/lac/sparsity_pattern.h | 1 + .../lac/trilinos_block_sparse_matrix.h | 359 +-- .../deal.II/lac/trilinos_sparse_matrix.h | 655 ++--- deal.II/include/deal.II/lac/trilinos_vector.h | 30 +- .../deal.II/lac/trilinos_vector_base.h | 108 +- deal.II/include/deal.II/lac/vector.h | 34 + .../include/deal.II/lac/vector.templates.h | 32 +- .../include/deal.II/matrix_free/dof_info.h | 7 + .../deal.II/matrix_free/dof_info.templates.h | 2 + .../deal.II/matrix_free/fe_evaluation.h | 38 +- .../include/deal.II/matrix_free/matrix_free.h | 2 +- .../matrix_free/matrix_free.templates.h | 83 +- .../matrix_free/shape_info.templates.h | 14 +- .../include/deal.II/meshworker/vector_info.h | 165 -- .../deal.II/multigrid/mg_block_smoother.h | 2 +- .../include/deal.II/multigrid/mg_transfer.h | 35 +- .../include/deal.II/numerics/matrix_tools.h | 307 ++- .../include/deal.II/numerics/vector_tools.h | 289 +- .../deal.II/numerics/vector_tools.templates.h | 591 ++-- deal.II/source/base/CMakeLists.txt | 1 + deal.II/source/base/convergence_table.cc | 9 +- deal.II/source/base/data_out_base.cc | 1003 ++++++- deal.II/source/base/function.cc | 2 - deal.II/source/base/logstream.cc | 6 +- deal.II/source/base/parameter_handler.cc | 6 +- deal.II/source/base/polynomial.cc | 90 +- deal.II/source/base/polynomials_piecewise.cc | 145 + deal.II/source/base/table_handler.cc | 6 +- .../source/base/tensor_product_polynomials.cc | 209 +- .../base/tensor_product_polynomials_const.cc | 297 +-- deal.II/source/distributed/tria.cc | 18 + deal.II/source/dofs/dof_handler_policy.cc | 8 +- deal.II/source/dofs/dof_tools.cc | 241 +- deal.II/source/fe/CMakeLists.txt | 4 + deal.II/source/fe/fe.cc | 34 +- deal.II/source/fe/fe_poly.cc | 1 + deal.II/source/fe/fe_poly.inst.in | 3 +- deal.II/source/fe/fe_q.cc | 1951 +------------- deal.II/source/fe/fe_q_base.cc | 1426 ++++++++++ deal.II/source/fe/fe_q_base.inst.in | 24 + deal.II/source/fe/fe_q_dg0.cc | 1999 +------------- deal.II/source/fe/fe_q_iso_q1.cc | 111 + deal.II/source/fe/fe_q_iso_q1.inst.in | 22 + deal.II/source/fe/fe_system.cc | 1102 +++----- deal.II/source/fe/fe_tools.cc | 143 +- deal.II/source/fe/fe_tools.inst.in | 66 +- deal.II/source/grid/grid_out.cc | 158 +- deal.II/source/lac/block_sparsity_pattern.cc | 4 +- deal.II/source/lac/chunk_sparsity_pattern.cc | 216 +- deal.II/source/lac/constraint_matrix.cc | 270 +- deal.II/source/lac/constraint_matrix.inst.in | 9 +- deal.II/source/lac/slepc_solver.cc | 38 +- deal.II/source/lac/trilinos_precondition.cc | 1 + deal.II/source/lac/trilinos_sparse_matrix.cc | 6 - deal.II/source/multigrid/mg_tools.cc | 20 +- .../source/multigrid/mg_transfer_prebuilt.cc | 34 +- deal.II/source/numerics/matrix_tools.cc | 92 +- deal.II/source/numerics/matrix_tools.inst.in | 72 +- 129 files changed, 13208 insertions(+), 14289 deletions(-) create mode 100644 deal.II/VERSION rename deal.II/cmake/checks/{check_01_system_features.cmake => check_02_system_features.cmake} (93%) rename deal.II/cmake/checks/{check_02_compiler_bugs.cmake => check_03_compiler_bugs.cmake} (100%) rename deal.II/cmake/configure/{configure_umfpack.cmake => configure_1_umfpack.cmake} (100%) create mode 100644 deal.II/examples/step-50/CMakeLists.txt create mode 100644 deal.II/include/deal.II/base/polynomials_piecewise.h create mode 100644 deal.II/include/deal.II/fe/fe_q_base.h create mode 100644 deal.II/include/deal.II/fe/fe_q_iso_q1.h delete mode 100644 deal.II/include/deal.II/meshworker/vector_info.h create mode 100644 deal.II/source/base/polynomials_piecewise.cc create mode 100644 deal.II/source/fe/fe_q_base.cc create mode 100644 deal.II/source/fe/fe_q_base.inst.in create mode 100644 deal.II/source/fe/fe_q_iso_q1.cc create mode 100644 deal.II/source/fe/fe_q_iso_q1.inst.in diff --git a/deal.II/README b/deal.II/README index 43609fc767..ba6501b3eb 100644 --- a/deal.II/README +++ b/deal.II/README @@ -1,9 +1,9 @@ What is deal.II? - deal.II is a C++ program library targeted at the computational solution of - partial differential equations using adaptive finite elements. It uses - state-of-the-art programming techniques to offer you a modern interface to - the complex data structures and algorithms required. + deal.II is a C++ program library targeted at the computational solution + of partial differential equations using adaptive finite elements. It uses + state-of-the-art programming techniques to offer you a modern interface + to the complex data structures and algorithms required. For the impatient: @@ -27,5 +27,7 @@ License: Please see the file LICENSE for details -For further information have a look at ./doc/readme.html or at -http://www.dealii.org. +Further information: + + For further information have a look at ./doc/readme.html or at + http://www.dealii.org. diff --git a/deal.II/VERSION b/deal.II/VERSION new file mode 100644 index 0000000000..941ceb5b07 --- /dev/null +++ b/deal.II/VERSION @@ -0,0 +1 @@ +8.0.pre diff --git a/deal.II/cmake/checks/check_01_compiler_features.cmake b/deal.II/cmake/checks/check_01_compiler_features.cmake index 8b4ec89a2e..17063b529e 100644 --- a/deal.II/cmake/checks/check_01_compiler_features.cmake +++ b/deal.II/cmake/checks/check_01_compiler_features.cmake @@ -148,15 +148,7 @@ CHECK_CXX_SOURCE_COMPILES( " HAVE_GLIBC_STACKTRACE) -# -# On Mac OS X, -rdynamic is accepted by the compiler (i.e. -# it doesn't produce an error) but we always get a warning -# that it isn't supported. So, only enable -rdynamic on non-Darwin -# platforms. -# -# - Matthias Maier, rewritten 2012 -# -IF(HAVE_GLIBC_STACKTRACE AND NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") +IF(HAVE_GLIBC_STACKTRACE) ENABLE_IF_SUPPORTED(CMAKE_SHARED_LINKER_FLAGS "-rdynamic") ENDIF() @@ -269,10 +261,18 @@ ENDIF() # per email by John Fowkes on the mailing list in Feb 2012, # so don't run the test on cygwin. # -# - Matthias Maier, rewritten 2012 +# Finally, Intel's icpc compiler complains about the flag +# but apparently only if the file to be compiled contains +# particular content. See bug #46 in the Google Code bug +# data base (http://code.google.com/p/dealii/issues/detail?id=46). +# It proved impossible to track down under which circumstances +# this happens, and so it was disabled for icpc. +# +# - Matthias Maier, rewritten 2012, 2013 # IF( (NOT CMAKE_SYSTEM_NAME MATCHES "CYGWIN") AND - (NOT CMAKE_SYSTEM_NAME MATCHES "Windows") ) + (NOT CMAKE_SYSTEM_NAME MATCHES "Windows") AND + (NOT CMAKE_CXX_COMPILER_ID MATCHES "Intel") ) ENABLE_IF_SUPPORTED(DEAL_II_CXX_FLAGS_DEBUG "-Wa,--compress-debug-sections") ENDIF() diff --git a/deal.II/cmake/checks/check_01_system_features.cmake b/deal.II/cmake/checks/check_02_system_features.cmake similarity index 93% rename from deal.II/cmake/checks/check_01_system_features.cmake rename to deal.II/cmake/checks/check_02_system_features.cmake index a85e40fecc..4288bff50b 100644 --- a/deal.II/cmake/checks/check_01_system_features.cmake +++ b/deal.II/cmake/checks/check_02_system_features.cmake @@ -82,9 +82,19 @@ IF(CMAKE_SYSTEM_NAME MATCHES "Darwin") # this flag any more, so check whether we can indeed do this # ENABLE_IF_SUPPORTED(CMAKE_CXX_FLAGS "-Wno-long-double") + + # + # On Mac OS X, -rdynamic is accepted by the compiler (i.e. + # it doesn't produce an error) but we always get a warning + # that it isn't supported. + # + # TODO: MM: Check whether this is still necessary... + # + STRIP_FLAG(CMAKE_SHARED_LINKER_FLAGS "-rdynamic") ENDIF() + ########################################################################### # # # Windows and CYGWIN specific setup: # diff --git a/deal.II/cmake/checks/check_02_compiler_bugs.cmake b/deal.II/cmake/checks/check_03_compiler_bugs.cmake similarity index 100% rename from deal.II/cmake/checks/check_02_compiler_bugs.cmake rename to deal.II/cmake/checks/check_03_compiler_bugs.cmake diff --git a/deal.II/cmake/config/CMakeLists.txt b/deal.II/cmake/config/CMakeLists.txt index 02780e6ed2..206504899c 100644 --- a/deal.II/cmake/config/CMakeLists.txt +++ b/deal.II/cmake/config/CMakeLists.txt @@ -173,7 +173,7 @@ IF(DEAL_II_COMPONENT_COMPAT_FILES) ${DEAL_II_EXTERNAL_LIBRARIES} ${DEAL_II_EXTERNAL_LIBRARIES_${build}} ) - IF(_lib MATCHES "^/") + IF(_lib MATCHES "^\\s*/") LIST(APPEND _libs "${_lib}") ENDIF() ENDFOREACH() @@ -183,22 +183,49 @@ IF(DEAL_II_COMPONENT_COMPAT_FILES) ${_libs} ) + MESSAGE("\n\nDEBUG:") + SET(_paths) FOREACH(_lib $(D)/${DEAL_II_LIBRARY_RELDIR}/${CONFIG_LIBRARY_${build}} ${DEAL_II_EXTERNAL_LIBRARIES} ${DEAL_II_EXTERNAL_LIBRARIES_${build}} ) - LIST(APPEND MAKEFILE_LIBS_${build} ${_lib}) + # - # Extract library paths where possible: + # Strip leading and trailing whitespace # + MESSAGE("--> \"${_lib}\"") + STRING(STRIP "${_lib}" _lib) + MESSAGE(" stripped: \"${_lib}\"") + IF(_lib MATCHES "^(/|\\$\\(D\\))") + # Full path: /path/to/libfoo.so: + + # Extract library paths: GET_FILENAME_COMPONENT(_path ${_lib} PATH) LIST(APPEND _paths ${_path}) + + MESSAGE(" appended: \"${_lib}\"") + LIST(APPEND MAKEFILE_LIBS_${build} ${_lib}) + + ELSEIF(_lib MATCHES "^-") + # Library is of the form "-lflag", don't touch it: + MESSAGE(" appended: \"${_lib}\"") + LIST(APPEND MAKEFILE_LIBS_${build} ${_lib}) + + ELSEIF(NOT "${_lib}" STREQUAL "") + # Well in this case we just assume that we have to append an "-l" + # Note: This won't cover case where a relative library name, such as + # libfoo.so, libfoo.dylib, etc. is specified... + MESSAGE(" appended: \"-l${_lib}\"") + LIST(APPEND MAKEFILE_LIBS_${build} "-l${_lib}") + ENDIF() ENDFOREACH() +# MESSAGE(FATAL_ERROR "bailout") + # # And build up an rpath: # diff --git a/deal.II/cmake/configure/configure_umfpack.cmake b/deal.II/cmake/configure/configure_1_umfpack.cmake similarity index 100% rename from deal.II/cmake/configure/configure_umfpack.cmake rename to deal.II/cmake/configure/configure_1_umfpack.cmake diff --git a/deal.II/cmake/setup_cached_variables.cmake b/deal.II/cmake/setup_cached_variables.cmake index 3e6f447bcc..2216232956 100644 --- a/deal.II/cmake/setup_cached_variables.cmake +++ b/deal.II/cmake/setup_cached_variables.cmake @@ -168,6 +168,8 @@ MARK_AS_ADVANCED(CMAKE_INSTALL_RPATH_USE_LINK_PATH) FOREACH(_flag CXX_FLAGS_RELEASE CXX_FLAGS_DEBUG + SHARED_LINKER_FLAGS_RELEASE + SHARED_LINKER_FLAGS_DEBUG ) IF(NOT "${CMAKE_${_flag}}" STREQUAL "") MESSAGE(FATAL_ERROR @@ -189,6 +191,10 @@ FOREACH(_flag CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_SHARED_LINKER_FLAGS_DEBUG + CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL + CMAKE_SHARED_LINKER_FLAGS_RELEASE + CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO ) # Go away... SET(${_flag} "" CACHE INTERNAL "" FORCE) @@ -230,14 +236,13 @@ FOREACH(_flag ${DEAL_II_USED_FLAGS}) MARK_AS_ADVANCED(${_flag}) ENDFOREACH() - # # Finally, read in CFLAGS, CXXFLAGS and LDFLAGS from environment and # prepend them to the saved variables: # SET(CMAKE_C_FLAGS_SAVED "$ENV{CFLAGS} ${CMAKE_C_FLAGS_SAVED}") SET(CMAKE_CXX_FLAGS_SAVED "$ENV{CXXFLAGS} ${CMAKE_CXX_FLAGS_SAVED}") -SET(CMAKE_SHARED_LINKER_FLAGS_SAVED "$ENV{LDFLAGS} ${CMAKE_SHARED_LINKER_FLAGS}") +SET(CMAKE_SHARED_LINKER_FLAGS_SAVED "$ENV{LDFLAGS} ${CMAKE_SHARED_LINKER_FLAGS_SAVED}") UNSET(ENV{CFLAGS}) UNSET(ENV{CXXFLAGS}) UNSET(ENV{LDFLAGS}) diff --git a/deal.II/cmake/setup_compiler_flags_intel.cmake b/deal.II/cmake/setup_compiler_flags_intel.cmake index 4256a66f99..1c82c3ef5c 100644 --- a/deal.II/cmake/setup_compiler_flags_intel.cmake +++ b/deal.II/cmake/setup_compiler_flags_intel.cmake @@ -1,6 +1,6 @@ ##### ## -## Copyright (C) 2012 by the deal.II authors +## Copyright (C) 2012, 2013 by the deal.II authors ## ## This file is part of the deal.II library. ## @@ -62,6 +62,10 @@ ENABLE_IF_SUPPORTED(CMAKE_CXX_FLAGS "-w2") # -w175 subscript out of range # -w177 declared but not referenced # -w279 controlling expression is constant +# -w383 value copied to temporary, reference to temporary used +# -w981 operands are evaluated in unspecified order +# -w1418 external function definition with no prior declaration +# (happens in boost) # -w1478 deprecation warning # -w1572 floating-point equality and inequality comparisons are unreliable # @@ -69,6 +73,9 @@ ENABLE_IF_SUPPORTED(CMAKE_CXX_FLAGS "-wd68") ENABLE_IF_SUPPORTED(CMAKE_CXX_FLAGS "-wd175") ENABLE_IF_SUPPORTED(CMAKE_CXX_FLAGS "-wd177") ENABLE_IF_SUPPORTED(CMAKE_CXX_FLAGS "-wd279") +ENABLE_IF_SUPPORTED(CMAKE_CXX_FLAGS "-wd383") +ENABLE_IF_SUPPORTED(CMAKE_CXX_FLAGS "-wd981") +ENABLE_IF_SUPPORTED(CMAKE_CXX_FLAGS "-wd1418") ENABLE_IF_SUPPORTED(CMAKE_CXX_FLAGS "-wd1478") ENABLE_IF_SUPPORTED(CMAKE_CXX_FLAGS "-wd1572") diff --git a/deal.II/cmake/setup_deal_ii.cmake b/deal.II/cmake/setup_deal_ii.cmake index c77fa6d7b0..5b82eedd22 100644 --- a/deal.II/cmake/setup_deal_ii.cmake +++ b/deal.II/cmake/setup_deal_ii.cmake @@ -63,7 +63,8 @@ SET_IF_EMPTY(DEAL_II_PACKAGE_NAME "deal.II") -SET_IF_EMPTY(DEAL_II_PACKAGE_VERSION "8.0.pre") # TODO: Get this value from somewhere else +FILE(STRINGS "${CMAKE_SOURCE_DIR}/VERSION" _version LIMIT_COUNT 1) +SET_IF_EMPTY(DEAL_II_PACKAGE_VERSION "${_version}") SET_IF_EMPTY(DEAL_II_PACKAGE_VENDOR "The deal.II Authors " diff --git a/deal.II/doc/authors.html b/deal.II/doc/authors.html index 8791f3008a..033fb196dc 100644 --- a/deal.II/doc/authors.html +++ b/deal.II/doc/authors.html @@ -271,6 +271,12 @@
  • Christophe Trophime: Packaging and configuration issues. +
  • Bruno Turcksin: + Extending deal.ii for 64-bit integer support. + +
  • Kainan Wang: + Extending deal.ii for 64-bit integer support. +
  • Yaqi Wang: The step-28 tutorial program;   diff --git a/deal.II/doc/doxygen/CMakeLists.txt b/deal.II/doc/doxygen/CMakeLists.txt index 5a59b6210d..fcbda0eb42 100644 --- a/deal.II/doc/doxygen/CMakeLists.txt +++ b/deal.II/doc/doxygen/CMakeLists.txt @@ -108,7 +108,9 @@ ADD_CUSTOM_TARGET(doxygen ALL DEPENDS ${CMAKE_BINARY_DIR}/doxygen.log ) + INSTALL(FILES + ${CMAKE_CURRENT_BINARY_DIR}/deal.tag ${CMAKE_CURRENT_SOURCE_DIR}/stylesheet.css DESTINATION ${DEAL_II_DOCHTML_RELDIR}/doxygen COMPONENT documentation diff --git a/deal.II/doc/doxygen/tutorial/index.html b/deal.II/doc/doxygen/tutorial/index.html index 5db27cf44b..8dc9fa5c72 100644 --- a/deal.II/doc/doxygen/tutorial/index.html +++ b/deal.II/doc/doxygen/tutorial/index.html @@ -2,9 +2,6 @@ "http://www.w3.org/TR/REC-html40/frameset.dtd"> - Step-by-Step Examples diff --git a/deal.II/doc/license.html b/deal.II/doc/license.html index 40b3a90805..5470e7573f 100644 --- a/deal.II/doc/license.html +++ b/deal.II/doc/license.html @@ -5,7 +5,7 @@ The deal.II License - + @@ -18,17 +18,27 @@

    The deal.II library has been placed under an Open Source license, in the sense advocated by the Open Source - Initiative. You are thus free to copy and use it, and you have - free access to all source codes. However, deal.II - is not in the public domain, it is property of and copyrighted by - the deal.II authors, and there are restrictions - on its use. We will give some hints on license issues first. The - legally binding license is the Q Public - License (QPL) at the bottom of this page. Furthermore, deal.II - distributions come bundled with some third party software for which - the deal.II authors do not own the copyright. The licenses of these - parts are discussed below. + href="http://www.opensource.org" target="_top">Open Source + Initiative. You are thus free to copy and use it, and you have + free access to all source code. +

    +

    + However, deal.II is not in the public domain, it + is property of and copyrighted by the + deal.II authors, and + there are restrictions on its use: + The legally binding license is the GNU Lesser + General Public License (LGPL) as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) + any later version. +

    + +

    + Furthermore, the deal.II distribution comes bundled with some third + party software for which the deal.II authors do not own the + copyright. The licenses of these parts are discussed + below. +

    In this document, as well as in the program files, the terms @@ -40,247 +50,609 @@ Kanschat and a long list of contributors.

    - - -

    Restrictions on and commercial use of the library

    - -

    - - deal.II is distributed under the Open Source - license called the Q Public License (or "QPL") listed - in full below. Essentially, an interpretation of this license is - that you can use the library free of charge for developing your own - programs. However, if you plan to distribute these programs to others, - certain restrictions are imposed by the QPL. +

    Contributions

    - - In non-legal terms, the license stipulates that if you want - to give your program to someone else in whatever form (including as - a binary executable), you have to make the entire source code of - your own program and all modifications to the deal.II library - available free of charge as well. - + As a contributor to this project, you agree that any of your + contributions be licensed under the same terms and conditions as + the license of the deal.II project granted to you. +

    - - We recognize that giving away the source code may be an - unacceptable restriction in a commercial setting. As the owners of - the sources, we are able to grant you a differently licensed version - of the deal.II library that does not contain this - requirement, and we are very much willing to negotiate the terms of - such a license. Please contact the authors of the library if you are - interested in commercial use of deal.II. - + We, the deal.II authors, do not require + copyright assignments for contributions. This means that the + copyright for code contributions in the deal.II project is held by + its respective contributors who have each agreed to release their + contributed code under the terms of the LGPL v2.1 or later. +

    -

    Referencing the library

    +

    Referencing the library

    - In addition to the terms imposed by the QPL, we ask for the - following courtesy: -

    + In addition to the terms imposed by the LGPL v2.1 or later, we ask + for the following courtesy:
      -
    • Every publication presenting numerical results obtained with - the help of deal.II should state the name of - the library and cite - the canonical deal.II reference listed at the top of the publications page. +
    • Every publication presenting numerical results obtained with the + help of deal.II should state the name of the + library and cite the canonical deal.II reference listed at the top of + the publications page.
    - This is the usual, fair way of giving credit to contributors to a - scientific result. In addition, it helps us justify our effort in - developing deal.II as an academic undertaking. - +

    +

    + This is the usual, fair way of giving credit to contributors to a + scientific result. In addition, it helps us justify our effort in + developing deal.II as an academic undertaking. +

    - We keep a list of publications using deal.II. Feel free to let us know about your publications. - - - -

    Parts of the deal.II distribution under a separate license

    - - - Some of the files in the contrib/ subdirectory are by - other people, and are distributed under separate licenses. These - files are not part of the deal.II library, but - they are part of our distribution. They are generally available for - use in non-commercial academic settings. For commercial use and if - you want to distribute your codes, please consult the licenses of - these parts of the deal.II distribution. We made sure that those parts of the - distribution for which the license is not entirely free for all uses can be - disabled upon configuration of deal.II without - unduly restricting the functionality of the rest of the - library. - -

    - - In particular, these are libraries that we distribute in the - contrib/ subdirectory, courtesy of their authors: -

    - -

    - - deal.II can interface with a number of other packages that you have to install - yourself. They are, of course, covered by their own licenses.

    - -

    About the license

    + +

    Parts of the deal.II distribution under a separate license

    +

    - The QPL was originally developed by - Troll Tech for their - Qt program library, but has since gained some support by other - library authors as well. - For more information on the QPL, please follow - this link. + deal.II comes bundled with copies of + UMFPACK, + Threading Building Blocks, + BOOST and + FunctionParser. + These projects reside in the subdirectory "bundled/". Please note + that these projects are copyrighted by others than the deal.II + authors and are covered by different licenses. For details, + consult the respective webpage.

    - - - -
    - -

    - - THE Q PUBLIC LICENSE - version 1.0 -
    -
    - - Copyright (C) 1999 Troll Tech AS, Norway. -
    - - Everyone is permitted to copy and -
    - distribute this license document. -
    -
    -

    - The intent of this license is to establish freedom to share and change the - software regulated by this license under the open source model. -

    - This license applies to any software containing a notice placed by the - copyright holder saying that it may be distributed under the terms of - the Q Public License version 1.0. Such software is herein referred to as - the Software. This license covers modification and distribution of the - Software, use of third-party application programs based on the Software, - and development of free software which uses the Software. -

    - Granted Rights -

    -

    - 1. You are granted the non-exclusive rights set forth in this license - provided you agree to and comply with any and all conditions in this - license. Whole or partial distribution of the Software, or software - items that link with the Software, in any form signifies acceptance of - this license. -

    - 2. You may copy and distribute the Software in unmodified form provided - that the entire package, including - but not restricted to - copyright, - trademark notices and disclaimers, as released by the initial developer - of the Software, is distributed. -

    - 3. You may make modifications to the Software and distribute your - modifications, in a form that is separate from the Software, such as - patches. The following restrictions apply to modifications: -

    -

    - a. Modifications must not alter or remove any copyright notices in - the Software. -

    - b. When modifications to the Software are released under this - license, a non-exclusive royalty-free right is granted to the - initial developer of the Software to distribute your modification - in future versions of the Software provided such versions remain - available under these terms in addition to any other license(s) of - the initial developer. -

    -

    - 4. You may distribute machine-executable forms of the Software or - machine-executable forms of modified versions of the Software, provided - that you meet these restrictions: -

    -

    - a. You must include this license document in the distribution. -

    - b. You must ensure that all recipients of the machine-executable forms - are also able to receive the complete machine-readable source code - to the distributed Software, including all modifications, without - any charge beyond the costs of data transfer, and place prominent - notices in the distribution explaining this. -

    - c. You must ensure that all modifications included in the - machine-executable forms are available under the terms of this - license. -

    -

    - 5. You may use the original or modified versions of the Software to - compile, link and run application programs legally developed by you - or by others. -

    - 6. You may develop application programs, reusable components and other - software items that link with the original or modified versions of the - Software. These items, when distributed, are subject to the following - requirements: -

    -

    - a. You must ensure that all recipients of machine-executable forms of - these items are also able to receive and use the complete - machine-readable source code to the items without any charge - beyond the costs of data transfer. -

    - b. You must explicitly license all recipients of your items to use - and re-distribute original and modified versions of the items in - both machine-executable and source code forms. The recipients must - be able to do so without any charges whatsoever, and they must be - able to re-distribute to anyone they choose. -

    - c. If the items are not available to the general public, and the - initial developer of the Software requests a copy of the items, - then you must supply one. -

    -

    - Limitations of Liability -

    - In no event shall the initial developers or copyright holders be liable - for any damages whatsoever, including - but not restricted to - lost - revenue or profits or other direct, indirect, special, incidental or - consequential damages, even if they have been advised of the possibility - of such damages, except to the extent invariable law, if any, provides - otherwise. -

    - No Warranty -

    + The libraries listed above are all open source and as such place few + restrictions on their use. However, if you want to distribute + software developed with deal.II (in source or binary form) and you + are using the packages above (with source code in + bundled/), then they may impose different terms. Please + consult the licenses of these packages for more information. +

    - The Software and this license document are provided AS IS with NO WARRANTY - OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE. -

    - Choice of Law -

    + Alternatively, the configuration process of deal.II allows you to + disable the use of all of these bundled libraries. In that case, you + can not use their functionality but you also are not restricted by + their license.

    - This license is governed by the Laws of Germany. Disputes shall be settled - by Amtsgericht Heidelberg. + +

    Contact

    +

    + For further questions regarding licensing and commercial use please + contact the deal.II maintainers directly via one of the following mail + addresses: +

      +
    • + TODO +
    • +
    • + Wolfgang Bangerth < bangerth@gmail.com > +
    • +
    • + Timo Heister < heister@math.tamu.edu > +
    • +
    • + Guido Kanschat < dr.guido.kanschat@gmail.com > +
    • +

    +

    Full license text

    + +
    +
    +                      GNU LESSER GENERAL PUBLIC LICENSE
    +                           Version 2.1, February 1999
    +
    +     Copyright (C) 1991, 1999 Free Software Foundation, Inc.
    +     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    +     Everyone is permitted to copy and distribute verbatim copies
    +     of this license document, but changing it is not allowed.
    +
    +    [This is the first released version of the Lesser GPL.  It also counts
    +     as the successor of the GNU Library Public License, version 2, hence
    +     the version number 2.1.]
    +
    +                                Preamble
    +
    +      The licenses for most software are designed to take away your
    +    freedom to share and change it.  By contrast, the GNU General Public
    +    Licenses are intended to guarantee your freedom to share and change
    +    free software--to make sure the software is free for all its users.
    +
    +      This license, the Lesser General Public License, applies to some
    +    specially designated software packages--typically libraries--of the
    +    Free Software Foundation and other authors who decide to use it.  You
    +    can use it too, but we suggest you first think carefully about whether
    +    this license or the ordinary General Public License is the better
    +    strategy to use in any particular case, based on the explanations below.
    +
    +      When we speak of free software, we are referring to freedom of use,
    +    not price.  Our General Public Licenses are designed to make sure that
    +    you have the freedom to distribute copies of free software (and charge
    +    for this service if you wish); that you receive source code or can get
    +    it if you want it; that you can change the software and use pieces of
    +    it in new free programs; and that you are informed that you can do
    +    these things.
    +
    +      To protect your rights, we need to make restrictions that forbid
    +    distributors to deny you these rights or to ask you to surrender these
    +    rights.  These restrictions translate to certain responsibilities for
    +    you if you distribute copies of the library or if you modify it.
    +
    +      For example, if you distribute copies of the library, whether gratis
    +    or for a fee, you must give the recipients all the rights that we gave
    +    you.  You must make sure that they, too, receive or can get the source
    +    code.  If you link other code with the library, you must provide
    +    complete object files to the recipients, so that they can relink them
    +    with the library after making changes to the library and recompiling
    +    it.  And you must show them these terms so they know their rights.
    +
    +      We protect your rights with a two-step method: (1) we copyright the
    +    library, and (2) we offer you this license, which gives you legal
    +    permission to copy, distribute and/or modify the library.
    +
    +      To protect each distributor, we want to make it very clear that
    +    there is no warranty for the free library.  Also, if the library is
    +    modified by someone else and passed on, the recipients should know
    +    that what they have is not the original version, so that the original
    +    author's reputation will not be affected by problems that might be
    +    introduced by others.
    +
    +      Finally, software patents pose a constant threat to the existence of
    +    any free program.  We wish to make sure that a company cannot
    +    effectively restrict the users of a free program by obtaining a
    +    restrictive license from a patent holder.  Therefore, we insist that
    +    any patent license obtained for a version of the library must be
    +    consistent with the full freedom of use specified in this license.
    +
    +      Most GNU software, including some libraries, is covered by the
    +    ordinary GNU General Public License.  This license, the GNU Lesser
    +    General Public License, applies to certain designated libraries, and
    +    is quite different from the ordinary General Public License.  We use
    +    this license for certain libraries in order to permit linking those
    +    libraries into non-free programs.
    +
    +      When a program is linked with a library, whether statically or using
    +    a shared library, the combination of the two is legally speaking a
    +    combined work, a derivative of the original library.  The ordinary
    +    General Public License therefore permits such linking only if the
    +    entire combination fits its criteria of freedom.  The Lesser General
    +    Public License permits more lax criteria for linking other code with
    +    the library.
    +
    +      We call this license the "Lesser" General Public License because it
    +    does Less to protect the user's freedom than the ordinary General
    +    Public License.  It also provides other free software developers Less
    +    of an advantage over competing non-free programs.  These disadvantages
    +    are the reason we use the ordinary General Public License for many
    +    libraries.  However, the Lesser license provides advantages in certain
    +    special circumstances.
    +
    +      For example, on rare occasions, there may be a special need to
    +    encourage the widest possible use of a certain library, so that it becomes
    +    a de-facto standard.  To achieve this, non-free programs must be
    +    allowed to use the library.  A more frequent case is that a free
    +    library does the same job as widely used non-free libraries.  In this
    +    case, there is little to gain by limiting the free library to free
    +    software only, so we use the Lesser General Public License.
    +
    +      In other cases, permission to use a particular library in non-free
    +    programs enables a greater number of people to use a large body of
    +    free software.  For example, permission to use the GNU C Library in
    +    non-free programs enables many more people to use the whole GNU
    +    operating system, as well as its variant, the GNU/Linux operating
    +    system.
    +
    +      Although the Lesser General Public License is Less protective of the
    +    users' freedom, it does ensure that the user of a program that is
    +    linked with the Library has the freedom and the wherewithal to run
    +    that program using a modified version of the Library.
    +
    +      The precise terms and conditions for copying, distribution and
    +    modification follow.  Pay close attention to the difference between a
    +    "work based on the library" and a "work that uses the library".  The
    +    former contains code derived from the library, whereas the latter must
    +    be combined with the library in order to run.
    +
    +                      GNU LESSER GENERAL PUBLIC LICENSE
    +       TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
    +
    +      0. This License Agreement applies to any software library or other
    +    program which contains a notice placed by the copyright holder or
    +    other authorized party saying it may be distributed under the terms of
    +    this Lesser General Public License (also called "this License").
    +    Each licensee is addressed as "you".
    +
    +      A "library" means a collection of software functions and/or data
    +    prepared so as to be conveniently linked with application programs
    +    (which use some of those functions and data) to form executables.
    +
    +      The "Library", below, refers to any such software library or work
    +    which has been distributed under these terms.  A "work based on the
    +    Library" means either the Library or any derivative work under
    +    copyright law: that is to say, a work containing the Library or a
    +    portion of it, either verbatim or with modifications and/or translated
    +    straightforwardly into another language.  (Hereinafter, translation is
    +    included without limitation in the term "modification".)
    +
    +      "Source code" for a work means the preferred form of the work for
    +    making modifications to it.  For a library, complete source code means
    +    all the source code for all modules it contains, plus any associated
    +    interface definition files, plus the scripts used to control compilation
    +    and installation of the library.
    +
    +      Activities other than copying, distribution and modification are not
    +    covered by this License; they are outside its scope.  The act of
    +    running a program using the Library is not restricted, and output from
    +    such a program is covered only if its contents constitute a work based
    +    on the Library (independent of the use of the Library in a tool for
    +    writing it).  Whether that is true depends on what the Library does
    +    and what the program that uses the Library does.
    +
    +      1. You may copy and distribute verbatim copies of the Library's
    +    complete source code as you receive it, in any medium, provided that
    +    you conspicuously and appropriately publish on each copy an
    +    appropriate copyright notice and disclaimer of warranty; keep intact
    +    all the notices that refer to this License and to the absence of any
    +    warranty; and distribute a copy of this License along with the
    +    Library.
    +
    +      You may charge a fee for the physical act of transferring a copy,
    +    and you may at your option offer warranty protection in exchange for a
    +    fee.
    +
    +      2. You may modify your copy or copies of the Library or any portion
    +    of it, thus forming a work based on the Library, and copy and
    +    distribute such modifications or work under the terms of Section 1
    +    above, provided that you also meet all of these conditions:
    +
    +        a) The modified work must itself be a software library.
    +
    +        b) You must cause the files modified to carry prominent notices
    +        stating that you changed the files and the date of any change.
    +
    +        c) You must cause the whole of the work to be licensed at no
    +        charge to all third parties under the terms of this License.
    +
    +        d) If a facility in the modified Library refers to a function or a
    +        table of data to be supplied by an application program that uses
    +        the facility, other than as an argument passed when the facility
    +        is invoked, then you must make a good faith effort to ensure that,
    +        in the event an application does not supply such function or
    +        table, the facility still operates, and performs whatever part of
    +        its purpose remains meaningful.
    +
    +        (For example, a function in a library to compute square roots has
    +        a purpose that is entirely well-defined independent of the
    +        application.  Therefore, Subsection 2d requires that any
    +        application-supplied function or table used by this function must
    +        be optional: if the application does not supply it, the square
    +        root function must still compute square roots.)
    +
    +    These requirements apply to the modified work as a whole.  If
    +    identifiable sections of that work are not derived from the Library,
    +    and can be reasonably considered independent and separate works in
    +    themselves, then this License, and its terms, do not apply to those
    +    sections when you distribute them as separate works.  But when you
    +    distribute the same sections as part of a whole which is a work based
    +    on the Library, the distribution of the whole must be on the terms of
    +    this License, whose permissions for other licensees extend to the
    +    entire whole, and thus to each and every part regardless of who wrote
    +    it.
    +
    +    Thus, it is not the intent of this section to claim rights or contest
    +    your rights to work written entirely by you; rather, the intent is to
    +    exercise the right to control the distribution of derivative or
    +    collective works based on the Library.
    +
    +    In addition, mere aggregation of another work not based on the Library
    +    with the Library (or with a work based on the Library) on a volume of
    +    a storage or distribution medium does not bring the other work under
    +    the scope of this License.
    +
    +      3. You may opt to apply the terms of the ordinary GNU General Public
    +    License instead of this License to a given copy of the Library.  To do
    +    this, you must alter all the notices that refer to this License, so
    +    that they refer to the ordinary GNU General Public License, version 2,
    +    instead of to this License.  (If a newer version than version 2 of the
    +    ordinary GNU General Public License has appeared, then you can specify
    +    that version instead if you wish.)  Do not make any other change in
    +    these notices.
    +
    +      Once this change is made in a given copy, it is irreversible for
    +    that copy, so the ordinary GNU General Public License applies to all
    +    subsequent copies and derivative works made from that copy.
    +
    +      This option is useful when you wish to copy part of the code of
    +    the Library into a program that is not a library.
    +
    +      4. You may copy and distribute the Library (or a portion or
    +    derivative of it, under Section 2) in object code or executable form
    +    under the terms of Sections 1 and 2 above provided that you accompany
    +    it with the complete corresponding machine-readable source code, which
    +    must be distributed under the terms of Sections 1 and 2 above on a
    +    medium customarily used for software interchange.
    +
    +      If distribution of object code is made by offering access to copy
    +    from a designated place, then offering equivalent access to copy the
    +    source code from the same place satisfies the requirement to
    +    distribute the source code, even though third parties are not
    +    compelled to copy the source along with the object code.
    +
    +      5. A program that contains no derivative of any portion of the
    +    Library, but is designed to work with the Library by being compiled or
    +    linked with it, is called a "work that uses the Library".  Such a
    +    work, in isolation, is not a derivative work of the Library, and
    +    therefore falls outside the scope of this License.
    +
    +      However, linking a "work that uses the Library" with the Library
    +    creates an executable that is a derivative of the Library (because it
    +    contains portions of the Library), rather than a "work that uses the
    +    library".  The executable is therefore covered by this License.
    +    Section 6 states terms for distribution of such executables.
    +
    +      When a "work that uses the Library" uses material from a header file
    +    that is part of the Library, the object code for the work may be a
    +    derivative work of the Library even though the source code is not.
    +    Whether this is true is especially significant if the work can be
    +    linked without the Library, or if the work is itself a library.  The
    +    threshold for this to be true is not precisely defined by law.
    +
    +      If such an object file uses only numerical parameters, data
    +    structure layouts and accessors, and small macros and small inline
    +    functions (ten lines or less in length), then the use of the object
    +    file is unrestricted, regardless of whether it is legally a derivative
    +    work.  (Executables containing this object code plus portions of the
    +    Library will still fall under Section 6.)
    +
    +      Otherwise, if the work is a derivative of the Library, you may
    +    distribute the object code for the work under the terms of Section 6.
    +    Any executables containing that work also fall under Section 6,
    +    whether or not they are linked directly with the Library itself.
    +
    +      6. As an exception to the Sections above, you may also combine or
    +    link a "work that uses the Library" with the Library to produce a
    +    work containing portions of the Library, and distribute that work
    +    under terms of your choice, provided that the terms permit
    +    modification of the work for the customer's own use and reverse
    +    engineering for debugging such modifications.
    +
    +      You must give prominent notice with each copy of the work that the
    +    Library is used in it and that the Library and its use are covered by
    +    this License.  You must supply a copy of this License.  If the work
    +    during execution displays copyright notices, you must include the
    +    copyright notice for the Library among them, as well as a reference
    +    directing the user to the copy of this License.  Also, you must do one
    +    of these things:
    +
    +        a) Accompany the work with the complete corresponding
    +        machine-readable source code for the Library including whatever
    +        changes were used in the work (which must be distributed under
    +        Sections 1 and 2 above); and, if the work is an executable linked
    +        with the Library, with the complete machine-readable "work that
    +        uses the Library", as object code and/or source code, so that the
    +        user can modify the Library and then relink to produce a modified
    +        executable containing the modified Library.  (It is understood
    +        that the user who changes the contents of definitions files in the
    +        Library will not necessarily be able to recompile the application
    +        to use the modified definitions.)
    +
    +        b) Use a suitable shared library mechanism for linking with the
    +        Library.  A suitable mechanism is one that (1) uses at run time a
    +        copy of the library already present on the user's computer system,
    +        rather than copying library functions into the executable, and (2)
    +        will operate properly with a modified version of the library, if
    +        the user installs one, as long as the modified version is
    +        interface-compatible with the version that the work was made with.
    +
    +        c) Accompany the work with a written offer, valid for at
    +        least three years, to give the same user the materials
    +        specified in Subsection 6a, above, for a charge no more
    +        than the cost of performing this distribution.
    +
    +        d) If distribution of the work is made by offering access to copy
    +        from a designated place, offer equivalent access to copy the above
    +        specified materials from the same place.
    +
    +        e) Verify that the user has already received a copy of these
    +        materials or that you have already sent this user a copy.
    +
    +      For an executable, the required form of the "work that uses the
    +    Library" must include any data and utility programs needed for
    +    reproducing the executable from it.  However, as a special exception,
    +    the materials to be distributed need not include anything that is
    +    normally distributed (in either source or binary form) with the major
    +    components (compiler, kernel, and so on) of the operating system on
    +    which the executable runs, unless that component itself accompanies
    +    the executable.
    +
    +      It may happen that this requirement contradicts the license
    +    restrictions of other proprietary libraries that do not normally
    +    accompany the operating system.  Such a contradiction means you cannot
    +    use both them and the Library together in an executable that you
    +    distribute.
    +
    +      7. You may place library facilities that are a work based on the
    +    Library side-by-side in a single library together with other library
    +    facilities not covered by this License, and distribute such a combined
    +    library, provided that the separate distribution of the work based on
    +    the Library and of the other library facilities is otherwise
    +    permitted, and provided that you do these two things:
    +
    +        a) Accompany the combined library with a copy of the same work
    +        based on the Library, uncombined with any other library
    +        facilities.  This must be distributed under the terms of the
    +        Sections above.
    +
    +        b) Give prominent notice with the combined library of the fact
    +        that part of it is a work based on the Library, and explaining
    +        where to find the accompanying uncombined form of the same work.
    +
    +      8. You may not copy, modify, sublicense, link with, or distribute
    +    the Library except as expressly provided under this License.  Any
    +    attempt otherwise to copy, modify, sublicense, link with, or
    +    distribute the Library is void, and will automatically terminate your
    +    rights under this License.  However, parties who have received copies,
    +    or rights, from you under this License will not have their licenses
    +    terminated so long as such parties remain in full compliance.
    +
    +      9. You are not required to accept this License, since you have not
    +    signed it.  However, nothing else grants you permission to modify or
    +    distribute the Library or its derivative works.  These actions are
    +    prohibited by law if you do not accept this License.  Therefore, by
    +    modifying or distributing the Library (or any work based on the
    +    Library), you indicate your acceptance of this License to do so, and
    +    all its terms and conditions for copying, distributing or modifying
    +    the Library or works based on it.
    +
    +      10. Each time you redistribute the Library (or any work based on the
    +    Library), the recipient automatically receives a license from the
    +    original licensor to copy, distribute, link with or modify the Library
    +    subject to these terms and conditions.  You may not impose any further
    +    restrictions on the recipients' exercise of the rights granted herein.
    +    You are not responsible for enforcing compliance by third parties with
    +    this License.
    +
    +      11. If, as a consequence of a court judgment or allegation of patent
    +    infringement or for any other reason (not limited to patent issues),
    +    conditions are imposed on you (whether by court order, agreement or
    +    otherwise) that contradict the conditions of this License, they do not
    +    excuse you from the conditions of this License.  If you cannot
    +    distribute so as to satisfy simultaneously your obligations under this
    +    License and any other pertinent obligations, then as a consequence you
    +    may not distribute the Library at all.  For example, if a patent
    +    license would not permit royalty-free redistribution of the Library by
    +    all those who receive copies directly or indirectly through you, then
    +    the only way you could satisfy both it and this License would be to
    +    refrain entirely from distribution of the Library.
    +
    +    If any portion of this section is held invalid or unenforceable under any
    +    particular circumstance, the balance of the section is intended to apply,
    +    and the section as a whole is intended to apply in other circumstances.
    +
    +    It is not the purpose of this section to induce you to infringe any
    +    patents or other property right claims or to contest validity of any
    +    such claims; this section has the sole purpose of protecting the
    +    integrity of the free software distribution system which is
    +    implemented by public license practices.  Many people have made
    +    generous contributions to the wide range of software distributed
    +    through that system in reliance on consistent application of that
    +    system; it is up to the author/donor to decide if he or she is willing
    +    to distribute software through any other system and a licensee cannot
    +    impose that choice.
    +
    +    This section is intended to make thoroughly clear what is believed to
    +    be a consequence of the rest of this License.
    +
    +      12. If the distribution and/or use of the Library is restricted in
    +    certain countries either by patents or by copyrighted interfaces, the
    +    original copyright holder who places the Library under this License may add
    +    an explicit geographical distribution limitation excluding those countries,
    +    so that distribution is permitted only in or among countries not thus
    +    excluded.  In such case, this License incorporates the limitation as if
    +    written in the body of this License.
    +
    +      13. The Free Software Foundation may publish revised and/or new
    +    versions of the Lesser General Public License from time to time.
    +    Such new versions will be similar in spirit to the present version,
    +    but may differ in detail to address new problems or concerns.
    +
    +    Each version is given a distinguishing version number.  If the Library
    +    specifies a version number of this License which applies to it and
    +    "any later version", you have the option of following the terms and
    +    conditions either of that version or of any later version published by
    +    the Free Software Foundation.  If the Library does not specify a
    +    license version number, you may choose any version ever published by
    +    the Free Software Foundation.
    +
    +      14. If you wish to incorporate parts of the Library into other free
    +    programs whose distribution conditions are incompatible with these,
    +    write to the author to ask for permission.  For software which is
    +    copyrighted by the Free Software Foundation, write to the Free
    +    Software Foundation; we sometimes make exceptions for this.  Our
    +    decision will be guided by the two goals of preserving the free status
    +    of all derivatives of our free software and of promoting the sharing
    +    and reuse of software generally.
    +
    +                                NO WARRANTY
    +
    +      15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
    +    WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
    +    EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
    +    OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
    +    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
    +    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    +    PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
    +    LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
    +    THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
    +
    +      16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
    +    WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
    +    AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
    +    FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
    +    CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
    +    LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
    +    RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
    +    FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
    +    SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
    +    DAMAGES.
    +
    +                         END OF TERMS AND CONDITIONS
    +
    +               How to Apply These Terms to Your New Libraries
    +
    +      If you develop a new library, and you want it to be of the greatest
    +    possible use to the public, we recommend making it free software that
    +    everyone can redistribute and change.  You can do so by permitting
    +    redistribution under these terms (or, alternatively, under the terms of the
    +    ordinary General Public License).
    +
    +      To apply these terms, attach the following notices to the library.  It is
    +    safest to attach them to the start of each source file to most effectively
    +    convey the exclusion of warranty; and each file should have at least the
    +    "copyright" line and a pointer to where the full notice is found.
    +
    +        <one line to give the library's name and a brief idea of what it does.>
    +        Copyright (C) <year>  <name of author>
    +
    +        This library is free software; you can redistribute it and/or
    +        modify it under the terms of the GNU Lesser General Public
    +        License as published by the Free Software Foundation; either
    +        version 2.1 of the License, or (at your option) any later version.
    +
    +        This library is distributed in the hope that it will be useful,
    +        but WITHOUT ANY WARRANTY; without even the implied warranty of
    +        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    +        Lesser General Public License for more details.
    +
    +        You should have received a copy of the GNU Lesser General Public
    +        License along with this library; if not, write to the Free Software
    +        Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    +
    +    Also add information on how to contact you by electronic and paper mail.
    +
    +    You should also get your employer (if you work as a programmer) or your
    +    school, if any, to sign a "copyright disclaimer" for the library, if
    +    necessary.  Here is a sample; alter the names:
    +
    +      Yoyodyne, Inc., hereby disclaims all copyright interest in the
    +      library `Frob' (a library for tweaking knobs) written by James Random Hacker.
    +
    +      <signature of Ty Coon>, 1 April 1990
    +      Ty Coon, President of Vice
    +
    +    That's all there is to it!
    +    
    +
    diff --git a/deal.II/doc/mail.html b/deal.II/doc/mail.html index 60c423c198..66574112c1 100644 --- a/deal.II/doc/mail.html +++ b/deal.II/doc/mail.html @@ -5,7 +5,7 @@ deal.II Communications - + @@ -27,7 +27,7 @@

    All deal.II users (and everybody interested) are encouraged to join the deal.II - Discussion group on Google Groups. + Discussion Group on Google Groups.

    • @@ -44,7 +44,7 @@ the the deal.II Discussion Group page and click on the corresponding - button (preferred), or you can send email + button, or you can send email to dealii@googlegroups.com. Or just post from the window below. Note that you have to be a group member with your specific sender address in order to be @@ -71,16 +71,16 @@ flaming them on missing items) should be sent to one of
      • - authors at dealii.org + TODO
      • - Wolfgang.Bangerth at dealii.org + Wolfgang Bangerth < bangerth@gmail.com >
      • - Timo.Heister at dealii.org + Timo Heister < heister@math.tamu.edu >
      • - Guido.Kanschat at dealii.org + Guido Kanschat < dr.guido.kanschat@gmail.com >
      Everything, that is not directly related to the library is to be considered diff --git a/deal.II/doc/navbar.html b/deal.II/doc/navbar.html index 64b987fe61..fcc7b54ec1 100644 --- a/deal.II/doc/navbar.html +++ b/deal.II/doc/navbar.html @@ -13,13 +13,22 @@

      Home
      - dealii.org
      + dealii.org
      + README
      +

      + +
      + +

      + Tutorial
      + Manual
      + Video lectures


      - README
      FAQ
      News
      Download
      @@ -30,13 +39,6 @@


      -

      - Tutorial
      - Manual
      -

      - -
      -

      Development
      Publications
      diff --git a/deal.II/doc/news/changes.h b/deal.II/doc/news/changes.h index 399aac68cd..a29684ee60 100644 --- a/deal.II/doc/news/changes.h +++ b/deal.II/doc/news/changes.h @@ -64,6 +64,26 @@ this function.

        + +
      1. New: All vector classes now have a member function + locally_owned_elements that returns an index + set indicating which elements of this vector the current + processor owns. +
        + (Wolfgang Bangerth, 2013/05/24) +
      2. + + +
      3. New: A new element FE_Q_iso_Q1 has been implemented that is defined by + a subdivision of the element into smaller Q1 elements. An element of order + @p p is similar to FE_Q of degree @p p with the same numbering of degrees of + freedom. The element is useful e.g. for defining a sparser preconditioner + matrix for AMG at higher order FE_Q elements or for representing a component + of a system of PDEs where higher resolution is preferred over high order. +
        + (Martin Kronbichler, 2013/05/14) +
      4. +
      5. New: The step-49 tutorial program now also has a discussion on what to do once you have a coarse mesh and want to refine it.
        @@ -110,6 +130,152 @@ this function.

        Specific improvements

          +
        1. New: ConstraintMatrix::distribute is now also implemented for +arguments of type PETScWrappers::MPI::BlockVector. +
          +(Wolfgang Bangerth, 2013/05/25) +
        2. + +
        3. Fixed: IndexSet::operator== returned the wrong results +in some cases. +
          +(Wolfgang Bangerth, 2013/05/25) +
        4. + +
        5. New: The global function complete_index_set() +creates and returns an index set of given size that contains +every single index with this range. +
          +(Wolfgang Bangerth, 2013/05/24) +
        6. + +
        7. New: All vector classes now have a static member variable +supports_distributed_data that indicates whether the +vector class supports data that is distributed across multiple +processors. This variable is provided as a traits variable +to allow generic algorithms working on general vector types to +query the capabilities of the vector class at compile time. +
          +(Wolfgang Bangerth, 2013/05/23) +
        8. + +
        9. Fixed: FETools::back_interpolate has been revised to work correctly +also with parallel::distributed::Vector. +
          +(Martin Steigemann, 2013/05/23) +
        10. + +
        11. Removed: The file mesh_worker/vector_info.h was unused and +untested. It has thus been removed. +
          +(Wolfgang Bangerth, Guido Kanschat, 2013/05/21) +
        12. + +
        13. Fixed: The method parallel::distributed::Vector::compress +(VectorOperation::insert) previously set the elements of ghost elements +unconditionally on the owning processor, even if they had not been touched. +This led to a problem in certain library functions where vector entries became +zero in a spurious way. This is now fixed by discarding the elements in ghost +entries for the VectorOperation::insert operation. This is legitimate since we +assume consistency of set elements across processors, so the owning processor +sets the element already. +
          +(Martin Kronbichler, 2013/05/21) +
        14. + +
        15. Improved: DoFTools::make_periodicity_constraints now also works +for meshes where the refinement level of the two sides of the domain +is not the same, i.e., one side is more refined than the other. +
          +(Wolfgang Bangerth, 2013/05/20) +
        16. + +
        17. Improved: Through the fields DataOutBase::VtkFlags::time and +DataOutBase::VtkFlags::cycle, it is now possible to encode the time and/or +cycle within a nonlinear or other iteration in VTK and VTU files written +via DataOutBase::write_vtk and DataOutBase::write_vtu. +
          +(Wolfgang Bangerth, 2013/05/12) +
        18. + +
        19. Fixed: The method ConvergenceTable::evaluate_convergence_rates with + reference column did not take the dimension of the reference column into + account, leading to wrong logarithmic rates for dim!=2. This can now be fixed + by specifying the dimension as a last argument. +
          +(Martin Kronbichler, 2013/05/10) +
        20. + +
        21. Improved: The functions MatrixTools::create_mass_matrix and +MatrixTools::create_laplace_matrix take now an optional ConstraintMatrix +argument that allows to directly apply the constraints. This also helps +VectorTools::project. Note that not providing constraints remains the default +and recommended way to ensure consistency when several matrices are combined. +
          +(Martin Kronbichler, 2013/05/08) +
        22. + +
        23. New: The classes TrilinosWrappers::SparseMatrix and +TrilinosWrappers::BlockSparseMatrix now fully implement vmult and Tvmult with +deal.II's own vector classes Vector and +parallel::distributed::Vector. +
          +(Martin Kronbichler, 2013/05/08) +
        24. + +
        25. Improved: The matrix-vector product ChunkSparseMatrix::vmult now runs in +parallel in shared memory. +
          +(Martin Kronbichler, 2013/05/07) +
        26. + +
        27. New: The class ChunkSparseMatrix and the associated +ChunkSparsityPattern now offer iterator classes to iterate over rows of the +matrix in an STL-like way. +
          +(Martin Kronbichler, 2013/05/07) +
        28. + +
        29. Fixed: The stopping criterion for early exit in SolverBicgstab did not +work properly for systems with large values, leading to premature exit. This +is now fixed. +
          +(Martin Kronbichler, 2013/05/07) +
        30. + +
        31. Changed: The SolverGMRES implementation previously applied two +iterations of the modified Gram–Schmidt algorithm for +orthogonalization. In many situations one iteration is enough. The algorithm +can now detect loss of orthogonality and enables re-orthogonalization only if +necessary. The second iteration (and, hence, old behavior) can be forced by +the flag SolverGMRES::AdditionalData::force_re_orthogonalization. +
          +(Martin Kronbichler, 2013/05/06) +
        32. + +
        33. Changed: FETools::interpolate is now instantiated for all +vector types, not just dealii::Vector and dealii::BlockVector. +
          +(Wolfgang Bangerth, 2013/05/06) +
        34. + +
        35. Fixed: setting values in TrilinosWrappers::SparseMatrix +in parallel was adding the values instead. +
          +(Bruno Turcksin, Timo Heister, 2013/05/03) +
        36. + +
        37. Fixed: Generate an error if the user tries to refine a cell +that is already on the maximum level in a distributed triangulation. +
          +(Timo Heister, 2013/05/01) +
        38. + +
        39. Fixed: The version of ParameterHandler::set that takes a boolean +as second argument was broken and did not work. This is now fixed. +
          +(Ashkan Dorostkar, Wolfgang Bangerth, 2013/04/30) +
        40. Fixed: PETScWrappers::VectorBase::print now saves and restores the precision @@ -261,13 +427,17 @@ sense. (Guido Kanschat, 2013/03/21)
        41. -
        42. Added GridOut::write_svg to allow for the output of two-dimensional -triangulations in two space dimensions in the SVG format (Scalable Vector -Graphics, an XML-based vector image format recommended by the World -Wide Web Consortium W3C). This function also provides cell coloring -and cell labeling for the visualization of basic cell properties. +
        43. Added GridOut::write_svg() to allow for the output of +two-dimensional triangulations in two space dimensions in the SVG +format (Scalable Vector Graphics, an generic XML-based vector image +format developed and maintained by the World Wide Web Consortium W3C). +This function also provides cell coloring and cell labeling for the +visualization of basic cell properties. Pespective view is further +possible and the cell level number may be converted into altitude, +revealing the inactive cells lying below.
          (Christian Wülker, 2013/03/21) +
        44. Added TimerOutput::reset to remove the collected information so far and added a new frequency TimerOutput::never to only output information if @@ -292,6 +462,16 @@ This is now fixed. (Timo Heister, 2013/03/01)
        45. +
        46. Added DataOutBase::write_svg() to allow for the output of a given +list of patches in two space dimensions in the SVG format (Scalable Vector +Graphics, an generic XML-based vector image format developed and maintained +by the World Wide Web Consortium W3C). An additional dimension (z-direction) +is employed for the visualization of data values taken from a data vector. +This function also provides patch coloring for the visual enhancement. +
          +(Christian Wülker, 2013/05/10) +
        47. +
        diff --git a/deal.II/doc/publications/index.html b/deal.II/doc/publications/index.html index 346587c363..77d0a3a83b 100644 --- a/deal.II/doc/publications/index.html +++ b/deal.II/doc/publications/index.html @@ -124,7 +124,7 @@ @Manual{DealIIReference, title = {{\tt deal.{I}{I}} Differential Equations Analysis Library, Technical Reference}, - author = {W. Bangerth and T. Heister and G. Kanschat}, + author = {W. Bangerth and T. Heister and G. Kanschat and others}, note = {\texttt{http://www.dealii.org}}, url = {http://www.dealii.org} } @@ -297,6 +297,15 @@
      6. +
      7. K. Dugan +
        + Dynamic adaptive multimesh refinement for coupled + physics applicable to nuclear engineering + +
        + MSc thesis, Texas A&M University, 2013. +
      8. +
      9. Y. Efendiev, J. Galvis, S. Pauletti
        Multiscale finite element methods for flows on rough @@ -373,6 +382,13 @@ Series, pp. 79-86, 2013.
      10. +
      11. J. Reinhardt, F. Weysser, J. M. Brader +
        + Density functional approach to nonlinear rheology +
        + Europhysics Letters, vol. 102, article 28011, 2013. +
      12. +
      13. S. Rudraraju, K. L. Mills, R. Kemkemer, K. Garikipati
        Multiphysics Modeling of Reactions, Mass Transport and Mechanics of Tumor Growth @@ -382,6 +398,15 @@ (eds.), Spinger Verlag, pp. 293-303, 2013.
      14. +
      15. N. Vakulin, R. Shaw, T. Livingston +
        + ELEC 490, final report: High performance computing with GPUs + +
        + Semester project for ELEC 490, final report. Queen's University, + 2013 +
      16. +
      17. S. Yoon, Y. T. Kang
        Mass transfer enhancement in non-Brownian particle diff --git a/deal.II/doc/readme.html b/deal.II/doc/readme.html index 6663f289e0..e80ebdac83 100644 --- a/deal.II/doc/readme.html +++ b/deal.II/doc/readme.html @@ -785,50 +785,59 @@

        License

        - We have placed deal.II under an Open Source - license, which allows you to use the library free of charge. You - are guaranteed full access to the source code and are encouraged - to help in the further development of the library. Follow this link to read the full - text of the license, + The deal.II library is free software; you can use it, redistribute + it, and/or modify it under the terms of the + GNU Lesser General + Public License (LGPL) as published by the Free Software + Foundation; either version 2.1 of the License, or (at your option) + any later version. +

        +

        + This allows you to use the library free of charge for private, + academic, or commercial use (under the terms of the LGPL v2.1 or + later). You are guaranteed full access to the source code and are + encouraged to help in the further development of the library. Follow + this link for detailed + information.

        -

        - The basics of the license are in short: + Please note:

        • - The library comes free of charge for all users, but - we keep a copyright in it. We will grant everyone access to - the library and its source code, provided they keep to the - license. - + Detailed license information can be found following + this link.
        • - The libraries may not be stripped of our copyright. We would - appreciate being contacted if you want to use the library - commercially. - + As a contributor to this project, you agree that any of your + contributions be licensed under the same terms and conditions as + the license of the deal.II project granted to you. +
        • + We, the deal.II authors, do not require + copyright assignments for contributions. This means that the + copyright for code contributions in the deal.II project is held by + its respective contributors who have each agreed to release their + contributed code under the terms of the LGPL v2.1 or later. +
        • + In addition to the terms imposed by the LGPL (version 2.1 or + later), we ask for the courtesy that scientific publications + presenting results obtained with this libraries acknowledge its + use. Please cite one of the papers referenced + here.
        • - We ask that scientific publications presenting results obtained with this - libraries acknowledge its use. Please cite one of the papers referenced - here. + deal.II can interface with a number of other packages that you either + have to install yourself. They are, of course, covered by their + own licenses. In addition, deal.II comes bundled with copies of + UMFPACK, + Threading Building Blocks, + BOOST and + FunctionParser, courtesy of their authors. + These are also covered by their own licenses; please refer to + their webpages for more information.
        -

        - deal.II can interface with a number of - other packages that you have to install - yourself. They are, of course, covered by their own licenses. In addition, - deal.II comes bundled with copies of - UMFPACK, - Threading Building Blocks, - BOOST - and - FunctionParser, - courtesy of their authors. These are also covered by their - own licenses; please refer to their webpages for more information. -


        diff --git a/deal.II/examples/step-26/step-26.cc b/deal.II/examples/step-26/step-26.cc index a4528ae963..0d4da01968 100644 --- a/deal.II/examples/step-26/step-26.cc +++ b/deal.II/examples/step-26/step-26.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -172,8 +173,6 @@ namespace Step26 template void HeatEquation::setup_system() { -//TODO: we need to take care of constraints here! :-) - dof_handler.distribute_dofs(fe); std::cout << std::endl @@ -185,12 +184,17 @@ namespace Step26 << std::endl << std::endl; - sparsity_pattern.reinit(dof_handler.n_dofs(), - dof_handler.n_dofs(), - dof_handler.max_couplings_between_dofs()); + constraints.clear (); + DoFTools::make_hanging_node_constraints (dof_handler, + constraints); + constraints.close(); + + CompressedSparsityPattern c_sparsity(dof_handler.n_dofs()); DoFTools::make_sparsity_pattern(dof_handler, - sparsity_pattern); - sparsity_pattern.compress(); + c_sparsity, + constraints, + /*keep_constrained_dofs = */ true); + sparsity_pattern.copy_from(c_sparsity); mass_matrix.reinit(sparsity_pattern); laplace_matrix.reinit(sparsity_pattern); @@ -198,16 +202,18 @@ namespace Step26 MatrixCreator::create_mass_matrix(dof_handler, QGauss(fe.degree+1), - mass_matrix); + mass_matrix, + (const Function*)0, + constraints); MatrixCreator::create_laplace_matrix(dof_handler, QGauss(fe.degree+1), - laplace_matrix); + laplace_matrix, + (const Function*)0, + constraints); solution.reinit(dof_handler.n_dofs()); old_solution.reinit(dof_handler.n_dofs()); system_rhs.reinit(dof_handler.n_dofs()); - - constraints.close(); } @@ -223,6 +229,8 @@ namespace Step26 cg.solve(system_matrix, solution, system_rhs, preconditioner); + constraints.distribute(solution); + std::cout << " " << solver_control.last_step() << " CG iterations." << std::endl; } @@ -362,8 +370,8 @@ namespace Step26 template void HeatEquation::run() { - const unsigned int initial_global_refinement = (dim == 2 ? 4 : 2); - const unsigned int n_adaptive_pre_refinement_steps = 3; + const unsigned int initial_global_refinement = (dim == 2 ? 1 : 2); + const unsigned int n_adaptive_pre_refinement_steps = 1; GridGenerator::hyper_L (triangulation); triangulation.refine_global (initial_global_refinement); @@ -440,6 +448,8 @@ start_time_iteration: system_rhs); } + constraints.condense (system_rhs); + solve_time_step(); output_results(); diff --git a/deal.II/examples/step-42/step-42.cc b/deal.II/examples/step-42/step-42.cc index de28338a59..54de0ab8f4 100644 --- a/deal.II/examples/step-42/step-42.cc +++ b/deal.II/examples/step-42/step-42.cc @@ -1551,7 +1551,7 @@ namespace Step42 { pcout << "Read the obstacle from a file." << std::endl; input_obstacle.reset (new Input("obstacle_file.pbm")); - pcout << "Ostacle is available now." << std::endl; + pcout << "Obstacle is available now." << std::endl; } const unsigned int n_cycles = 6; diff --git a/deal.II/examples/step-48/step-48.cc b/deal.II/examples/step-48/step-48.cc index e5e71e7d20..79250490ed 100644 --- a/deal.II/examples/step-48/step-48.cc +++ b/deal.II/examples/step-48/step-48.cc @@ -3,7 +3,7 @@ /* $Id$ */ /* */ -/* Copyright (C) 2011-2012 by the deal.II authors */ +/* Copyright (C) 2011-2013 by the deal.II authors */ /* */ /* This file is subject to QPL and may not be distributed */ /* without copyright and license information. Please refer */ @@ -603,13 +603,20 @@ namespace Step48 // @sect3{The main function} -// This is as in all other programs: +// As in step-40, we initialize MPI at the start of the program. Since we will +// in general mix MPI parallelization with threads, we also set the third +// argument in MPI_InitFinalize that controls the number of threads to an +// invalid number, which means that the TBB library chooses the number of +// threads automatically, typically to the number of available cores in the +// system. As an alternative, you can also set this number manually if you +// want to set a specific number of threads (e.g. when MPI-only is required). int main (int argc, char **argv) { using namespace Step48; using namespace dealii; - Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv); + Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv, + numbers::invalid_unsigned_int); try { diff --git a/deal.II/examples/step-50/CMakeLists.txt b/deal.II/examples/step-50/CMakeLists.txt new file mode 100644 index 0000000000..babcd817e0 --- /dev/null +++ b/deal.II/examples/step-50/CMakeLists.txt @@ -0,0 +1,52 @@ +## +# CMake script for the step-32 tutorial program: +## + +# Set the name of the project and target: +SET(TARGET "step-50") + +# Declare all source files the target consists of: +SET(TARGET_SRC + ${TARGET}.cc + # You can specify additional files here! + ) + +# A custom command line to run the program +SET(TARGET_RUN mpirun -np 2 ${TARGET}) + +# Define the output that should be cleaned: +SET(CLEAN_UP_FILES *.vtu *.pvtu *.visit) + +# Usually, you will not need to modify anything beyond this point... + +CMAKE_MINIMUM_REQUIRED(VERSION 2.8.8) + +FIND_PACKAGE(deal.II 8.0 QUIET + HINTS ${deal.II_DIR} ${DEAL_II_DIR} ../ ../../ $ENV{DEAL_II_DIR} + ) +IF(NOT ${deal.II_FOUND}) + MESSAGE(FATAL_ERROR "\n" + "*** Could not locate deal.II. ***\n\n" + "You may want to either pass a flag -DDEAL_II_DIR=/path/to/deal.II to cmake\n" + "or set an environment variable \"DEAL_II_DIR\" that contains this path." + ) +ENDIF() + +# +# Are all dependencies fullfilled? +# +IF( NOT DEAL_II_WITH_MPI OR + NOT DEAL_II_WITH_P4EST OR + NOT DEAL_II_WITH_TRILINOS ) + MESSAGE(FATAL_ERROR " +Error! The deal.II library found at ${DEAL_II_PATH} was not configured with + DEAL_II_WITH_MPI = ON + DEAL_II_WITH_P4EST = ON + DEAL_II_WITH_TRILINOS = ON +which is required for this tutorial step." + ) +ENDIF() + +DEAL_II_INITIALIZE_CACHED_VARIABLES() +PROJECT(${TARGET}) +DEAL_II_INVOKE_AUTOPILOT() diff --git a/deal.II/examples/step-50/step-50.cc b/deal.II/examples/step-50/step-50.cc index 640c4b38be..abca04f105 100644 --- a/deal.II/examples/step-50/step-50.cc +++ b/deal.II/examples/step-50/step-50.cc @@ -11,6 +11,8 @@ /* to the file deal.II/doc/license.html for the text and */ /* further information on this license. */ +// parallel geometric multi-grid. work in progress! + // As discussed in the introduction, most of // this program is copied almost verbatim // from step-6, which itself is only a slight @@ -47,6 +49,7 @@ #include #include #include +#include #include #include @@ -132,7 +135,6 @@ namespace Step50 typedef TrilinosWrappers::SparseMatrix matrix_t; typedef TrilinosWrappers::MPI::Vector vector_t; - SparsityPattern sparsity_pattern; matrix_t system_matrix; // We need an additional object for the @@ -188,7 +190,6 @@ namespace Step50 // 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; @@ -279,7 +280,8 @@ namespace Step50 LaplaceProblem::LaplaceProblem (const unsigned int degree) : triangulation (MPI_COMM_WORLD,Triangulation:: - limit_level_difference_at_vertices), + limit_level_difference_at_vertices, + parallel::distributed::Triangulation::construct_multigrid_hierarchy), fe (degree), mg_dof_handler (triangulation), degree(degree) @@ -296,6 +298,21 @@ namespace Step50 template void LaplaceProblem::setup_system () { + std::string filename = "grid-" + + Utilities::int_to_string(triangulation.n_levels(), 2) + + "-" + + Utilities::int_to_string (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD), 3) + + ".svg"; + + std::ofstream out (filename.c_str()); + GridOut grid_out; + GridOutFlags::Svg svg_flags; + svg_flags.polar_angle = 60; + svg_flags.label_level_subdomain_id = true; + svg_flags.convert_level_number_to_height = true; + grid_out.set_flags(svg_flags); + grid_out.write_svg (triangulation, out); + mg_dof_handler.distribute_dofs (fe); mg_dof_handler.distribute_mg_dofs (fe); @@ -312,11 +329,6 @@ namespace Step50 << mg_dof_handler.n_dofs(l); deallog << std::endl; - sparsity_pattern.reinit (mg_dof_handler.n_dofs(), - mg_dof_handler.n_dofs(), - mg_dof_handler.max_couplings_between_dofs()); - DoFTools::make_sparsity_pattern (mg_dof_handler, sparsity_pattern); - //solution.reinit (mg_dof_handler.n_dofs()); //system_rhs.reinit (mg_dof_handler.n_dofs()); @@ -356,9 +368,10 @@ namespace Step50 constraints); constraints.close (); hanging_node_constraints.close (); - constraints.condense (sparsity_pattern); - sparsity_pattern.compress(); - system_matrix.reinit (sparsity_pattern); + + CompressedSimpleSparsityPattern csp(mg_dof_handler.n_dofs(), mg_dof_handler.n_dofs()); + DoFTools::make_sparsity_pattern (mg_dof_handler, csp, constraints); + system_matrix.reinit (mg_dof_handler.locally_owned_dofs(), csp, MPI_COMM_WORLD, true); // The multigrid constraints have to be // initialized. They need to know about @@ -390,7 +403,6 @@ namespace Step50 mg_interface_matrices.clear (); mg_matrices.resize(0, n_levels-1); 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 @@ -425,10 +437,10 @@ namespace Step50 mg_dof_handler.n_dofs(level)); MGTools::make_sparsity_pattern(mg_dof_handler, csp, level); - mg_sparsity_patterns[level].copy_from (csp); - - mg_matrices[level].reinit(mg_sparsity_patterns[level]); - mg_interface_matrices[level].reinit(mg_sparsity_patterns[level]); + mg_matrices[level].reinit(mg_dof_handler.locally_owned_mg_dofs(level), + mg_dof_handler.locally_owned_mg_dofs(level), + csp, + MPI_COMM_WORLD, true); } } @@ -971,12 +983,12 @@ namespace Step50 if (cycle == 0) { - GridGenerator::hyper_ball (triangulation); + GridGenerator::hyper_cube (triangulation); - static const HyperBallBoundary boundary; - triangulation.set_boundary (0, boundary); + // static const HyperBallBoundary boundary; + // triangulation.set_boundary (0, boundary); - triangulation.refine_global (1); + triangulation.refine_global (2); } else refine_grid (); diff --git a/deal.II/include/deal.II/base/convergence_table.h b/deal.II/include/deal.II/base/convergence_table.h index df3bed1a50..a262a6be3b 100644 --- a/deal.II/include/deal.II/base/convergence_table.h +++ b/deal.II/include/deal.II/base/convergence_table.h @@ -73,175 +73,127 @@ public: */ none, /** - * Quotient of values in - * the previous row and in - * this row. + * 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. + * 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. + * 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. + * As this class has no information on the space dimension upon which the + * reference column vs. the value column is based upon, it needs to be + * passed as last argument to this method. The default dimension for the + * reference column is 2, which is appropriate for the number of cells + * in 2D. If you work in 3D, set the number to 3. If the reference column is + * $1/h$, remember to set the dimension to 1 also when working in 3D to get + * correct rates. * - * This method behaves in the following way: + * 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. * - * If RateMode is reduction_rate, then the computed - * output is - * $ \frac{e_{n-1}/k_{n-1}}{e_n/k_n} $. + * This method behaves in the following way: * - * Where $k$ is the reference column. + * 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 (no dimension dependence!). * - * If RateMode is reduction_rate_log2, then the - * computed output is + * 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}|} + * dim \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. + * This is useful, for example, if we use as reference key the number of + * degrees of freedom or better, the number of cells. Assuming that the + * error is proportional to $ C (1/\sqrt{k})^r $ in 2D, then this method + * will produce the rate $r$ as a result. For general dimension, as described + * by the last parameter of this function, the formula needs to be + * $ C (1/\sqrt[dim]{k})^r $. * - * @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. + * @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); + const RateMode rate_mode, + const unsigned int dim = 2); /** - * 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. + * 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. + * 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. + * @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). + * 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 + * 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. + * 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(). + * 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. + * 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(). + * 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); 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 a8124528fc..0ce000d902 100644 --- a/deal.II/include/deal.II/base/data_out_base.h +++ b/deal.II/include/deal.II/base/data_out_base.h @@ -21,6 +21,7 @@ #include #include +#include #include @@ -1186,34 +1187,54 @@ public: /** * Flags controlling the details * of output in VTK - * format. At present no flags - * are implemented. + * format. * * @ingroup output */ struct VtkFlags { - private: + public: /** - * 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). + * The time of the time step if this file is part of a + * time dependent simulation. + * + * The value of this variable is written into the output file according + * to the instructions provided in + * http://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files + * unless it is at its default value of std::numeric_limits::min(). */ - int dummy; + double time; + + /** + * The number of the time step if this file is part of a + * time dependent simulation, or the cycle within a nonlinear or other + * iteration. + * + * The value of this variable is written into the output file according + * to the instructions provided in + * http://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files + * unless it is at its default value of std::numeric_limits::min(). + */ + unsigned int cycle; - public: /** * Default constructor. */ - VtkFlags (); + VtkFlags (const double time = std::numeric_limits::min(), + const unsigned int cycle = std::numeric_limits::min()); /** - * Declare all flags with name + * Declare the flags with name * and type as offered by this * class, for use in input files. + * + * Unlike the flags in many of the other classes similar to this one, we do + * not actually declare parameters for the #cycle and #time member variables + * of this class. The reason is that there wouldn't appear to be a case where + * one would want to declare these parameters in an input file. Rather, these + * are typically values that change during the course of a simulation and + * can only reasonably be set as part of the execution of a program, rather + * than a priori by a user who runs this program. */ static void declare_parameters (ParameterHandler &prm); @@ -1246,6 +1267,77 @@ public: */ std::size_t memory_consumption () const; }; + + + /** + * Flags for SVG output. + */ + struct SvgFlags + { + public: + /** + * 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; + + /* + * Angles for the perspective view + */ + int azimuth_angle, polar_angle; + + unsigned int line_thickness; + + /* + * Draw a margin of 5% around the plotted area + */ + bool margin; + + /* + * Draw a colorbar encoding the cell coloring + */ + bool draw_colorbar; + + /* + * Constructor. + */ + SvgFlags(const unsigned int height_vector = 0, + const int azimuth_angle = 37, + const int polar_angle = 45, + const unsigned int line_thickness = 1, + const bool margin = true, + const bool draw_colorbar = true); + + /** + * 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; + + private: + + }; + + /** * Flags controlling the details * of output in deal.II @@ -1398,6 +1490,12 @@ public: */ vtu, + /** + * Output in + * SVG format. + */ + svg, + /** * Output in deal.II * intermediate format. @@ -1693,6 +1791,13 @@ public: * 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. + * + * @note VTK is a legacy format and has largely been supplanted by the VTU + * format (an XML-structured version of VTK). In particular, VTU allows for + * the compression of data and consequently leads to much smaller file + * sizes that equivalent VTK files for large files. Since all visualization + * programs that support VTK also support VTU, you should consider using the + * latter file format instead, by using the write_vtu() function. */ template static void write_vtk (const std::vector > &patches, @@ -1703,7 +1808,7 @@ public: /** - * Write the given list of patches to the output stream in VTK + * Write the given list of patches to the output stream in VTU * format. The data is written in the XML-based VTK format as opposed to the * traditional format that write_vtk() produces. * @@ -1760,7 +1865,35 @@ public: const std::vector > &vector_data_ranges, const VtkFlags &flags, std::ostream &out); - + + /** + * Write the given list of patches to the output stream in SVG format. + * + * SVG (Scalable Vector Graphics) is an XML-based vector image format + * developed and maintained by the World Wide Web Consortium (W3C). + * This function conforms to the latest specification SVG 1.1, + * released on August 16, 2011. Controlling the graphic output is + * possible by setting or clearing the respective flags (see the + * SvgFlags struct). At present, this format only supports output + * for two-dimensional data, with values in the third direction + * taken from a data vector. + * + * For the output, each patch is subdivided into four triangles + * which are then written as polygons and filled with a linear + * color gradient. The arising coloring of the patches visualizes + * the data values at the vertices taken from the specified data + * vector. A colorbar can be drawn to encode the coloring. + * + * @note Yet only implemented for two dimensions with an additional + * dimension reserved for data information. + */ + template + static void write_svg (const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const SvgFlags &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 @@ -1909,6 +2042,7 @@ public: *
      18. tecplot_binary: .plt *
      19. vtk: .vtk *
      20. vtu: .vtu + *
      21. svg: .svg *
      22. deal_II_intermediate: .d2. *
    */ @@ -1988,6 +2122,72 @@ private: const bool double_precision, STREAM &out); + + /** + * This function projects a three-dimensional point (Point<3> point) + * onto a two-dimensional image plane, specified by the position of + * the camera viewing system (Point<3> camera_position), camera + * direction (Point<3> camera_position), camera horizontal (Point<3> + * camera_horizontal, necessary for the correct alignment of the + * later images), and the focus of the camera (float camera_focus). + * + * For SVG output. + */ + static Point<2> svg_project_point(Point<3> point, + Point<3> camera_position, + Point<3> camera_direction, + Point<3> camera_horizontal, + float camera_focus); + /** + * Function to compute the gradient parameters for + * a triangle with given values for the vertices. + * + * Used for svg output. + */ + static Point<6> svg_get_gradient_parameters(Point<3> points[]); + + /** + * 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 SvgCell + { + public: + + // Center of the cell (three-dimensional) + Point<3> center; + + /** + * Vector of vertices of this cell (three-dimensional) + */ + Point<3> vertices[4]; + + /** + * 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; + + /** + * Vector of vertices of this cell (projected, two-dimensional). + */ + Point<2> projected_vertices[4]; + + // Center of the cell (projected, two-dimensional) + Point<2> projected_center; + + /** + * Comparison operator for + * sorting. + */ + bool operator < (const SvgCell &) const; + }; + + /** * Class holding the data of one * cell of a patch in two space @@ -2290,6 +2490,13 @@ public: * and write it to out * in Vtk format. See * DataOutBase::write_vtk. + * + * @note VTK is a legacy format and has largely been supplanted by the VTU + * format (an XML-structured version of VTK). In particular, VTU allows for + * the compression of data and consequently leads to much smaller file + * sizes that equivalent VTK files for large files. Since all visualization + * programs that support VTK also support VTU, you should consider using the + * latter file format instead, by using the write_vtu() function. */ void write_vtk (std::ostream &out) const; @@ -2463,6 +2670,14 @@ public: void write_visit_record (std::ostream &out, const std::vector &piece_names) const; + /** + * Obtain data through get_patches() + * and write it to out + * in SVG format. See + * DataOutBase::write_svg. + */ + void write_svg(std::ostream &out) const; + /** * Obtain data through get_patches() * and write it to out @@ -2568,6 +2783,12 @@ public: */ void set_flags (const VtkFlags &vtk_flags); + /** + * Set the flags to be used for + * output in SVG format. + */ + void set_flags (const SvgFlags &svg_flags); + /** * Set the flags to be used for output in * deal.II intermediate format. @@ -2806,6 +3027,15 @@ private: */ VtkFlags vtk_flags; + /** + * Flags to be used upon output + * of svg data in one space + * dimension. Can be changed by + * using the set_flags + * function. + */ + SvgFlags svg_flags; + /** * Flags to be used upon output of * deal.II intermediate data in one space diff --git a/deal.II/include/deal.II/base/index_set.h b/deal.II/include/deal.II/base/index_set.h index c0cf5a1ddd..aff873cba7 100644 --- a/deal.II/include/deal.II/base/index_set.h +++ b/deal.II/include/deal.II/base/index_set.h @@ -490,8 +490,8 @@ private: const Range &range_2) { return ((range_1.begin == range_2.begin) - || - (range_1.begin == range_2.begin)); + && + (range_1.end == range_2.end)); } std::size_t memory_consumption () const @@ -571,6 +571,32 @@ private: }; +/** + * Create and return an index set of size $N$ that contains every + * single index within this range. In essence, this function + * returns an index set created by + * @code + * IndexSet is (N); + * is.add_range(0, N); + * @endcode + * This function exists so that one can create and initialize + * index sets that are complete in one step, or so one can write + * code like + * @code + * if (my_index_set == complete_index_set(my_index_set.size()) + * ... + * @endcode + * + * @relates IndexSet + */ +inline +IndexSet complete_index_set (const unsigned int N) +{ + IndexSet is (N); + is.add_range(0, N); + return is; +} + /* ------------------ inline functions ------------------ */ inline diff --git a/deal.II/include/deal.II/base/mpi.h b/deal.II/include/deal.II/base/mpi.h index 821ad1e4d3..4887776510 100644 --- a/deal.II/include/deal.II/base/mpi.h +++ b/deal.II/include/deal.II/base/mpi.h @@ -151,6 +151,14 @@ namespace Utilities * MPI_Allreduce function, i.e. all processors receive * the result of this operation. * + * @note Sometimes, not all processors need a results and in that case + * one would call the MPI_Reduce function instead of the + * MPI_Allreduce function. The latter is at most twice as + * expensive, so if you are concerned about performance, it may be + * worthwhile investigating whether your algorithm indeed needs the + * result everywhere or whether you could get away with calling the + * current function and getting the result everywhere. + * * @note This function is only implemented for certain template * arguments T, namely float, double, int, * unsigned int. @@ -198,6 +206,14 @@ namespace Utilities * MPI_Allreduce function, i.e. all processors receive * the result of this operation. * + * @note Sometimes, not all processors need a results and in that case + * one would call the MPI_Reduce function instead of the + * MPI_Allreduce function. The latter is at most twice as + * expensive, so if you are concerned about performance, it may be + * worthwhile investigating whether your algorithm indeed needs the + * result everywhere or whether you could get away with calling the + * current function and getting the result everywhere. + * * @note This function is only implemented for certain template * arguments T, namely float, double, int, * unsigned int. @@ -255,12 +271,20 @@ namespace Utilities * Returns sum, average, minimum, * maximum, processor id of minimum and * maximum as a collective operation of - * on the given MPI communicator @param + * on the given MPI communicator @p * 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 + * value is given in @p my_value and + * the result will be returned. + * The result is available on all * machines. + * + * @note Sometimes, not all processors need a results and in that case + * one would call the MPI_Reduce function instead of the + * MPI_Allreduce function. The latter is at most twice as + * expensive, so if you are concerned about performance, it may be + * worthwhile investigating whether your algorithm indeed needs the + * result everywhere or whether you could get away with calling the + * current function and getting the result everywhere. */ MinMaxAvg min_max_avg (const double my_value, diff --git a/deal.II/include/deal.II/base/partitioner.h b/deal.II/include/deal.II/base/partitioner.h index fff826924b..68d4c4d720 100644 --- a/deal.II/include/deal.II/base/partitioner.h +++ b/deal.II/include/deal.II/base/partitioner.h @@ -272,7 +272,7 @@ namespace Utilities * Returns the MPI communicator underlying the * partitioner object. */ - MPI_Comm get_communicator() const; + const MPI_Comm& get_communicator() const; /** * Computes the memory consumption of this @@ -565,7 +565,7 @@ namespace Utilities inline - MPI_Comm + const MPI_Comm& Partitioner::get_communicator() const { return communicator; diff --git a/deal.II/include/deal.II/base/point.h b/deal.II/include/deal.II/base/point.h index b91947b89f..c33ea938c8 100644 --- a/deal.II/include/deal.II/base/point.h +++ b/deal.II/include/deal.II/base/point.h @@ -53,21 +53,22 @@ class Point : public Tensor<1,dim,Number> public: /** * Standard constructor. Creates - * an origin. + * an object that corresponds to the origin, i.e., all coordinates + * are set to zero. */ Point (); /** * Constructor. Initialize all * entries to zero if - * initialize==true. + * initialize==true (in which case it is equivalent to the default + * constructor) or leaves the elements uninitialized if + * initialize==false. */ explicit Point (const bool initialize); /** - * Convert a tensor to a point. Since no - * additional data is inside a point, - * this is ok. + * Convert a tensor to a point. */ Point (const Tensor<1,dim,Number> &); @@ -76,7 +77,8 @@ public: * points. This function is only * implemented for dim==1 since * the usage is considered unsafe for - * points with dim!=1. + * points with dim!=1 as it would leave some components + * of the point coordinates uninitialized. */ explicit Point (const Number x); @@ -85,18 +87,25 @@ public: * points. This function is only * implemented for dim==2 since * the usage is considered unsafe for - * points with dim!=2. + * points with dim!=2 as it would leave some components + * of the point coordinates uninitialized (if dim>2) or would + * not use some arguments (if dim<2). */ - Point (const Number x, const Number y); + 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. + * points with dim!=3 as it would leave some components + * of the point coordinates uninitialized (if dim>3) or would + * not use some arguments (if dim<3). */ - Point (const Number x, const Number y, const Number z); + Point (const Number x, + const Number y, + const Number z); /** * Return a unit vector in diff --git a/deal.II/include/deal.II/base/polynomial.h b/deal.II/include/deal.II/base/polynomial.h index 8897467e04..ec33ec38a8 100644 --- a/deal.II/include/deal.II/base/polynomial.h +++ b/deal.II/include/deal.II/base/polynomial.h @@ -30,8 +30,8 @@ DEAL_II_NAMESPACE_OPEN */ /** - * A namespace in which classes relating to the description of - * 1d polynomial spaces are declared. + * A namespace in which classes relating to the description of 1d polynomial + * spaces are declared. */ namespace Polynomials { @@ -50,145 +50,97 @@ namespace Polynomials { 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. + * 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. + * 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). + * 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. + * Default constructor creating an illegal object. */ Polynomial (); /** - * Return the value of this - * polynomial at the given point. + * Return the value of this polynomial at the given point. * - * This function uses the Horner - * scheme for numerical stability - * of the evaluation. + * 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. + * 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. + * 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. + * 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). + * 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. + * 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). + * 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 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. + * 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. + * 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. + * Compute the primitive of a polynomial. the coefficient of the zero + * order term of the polynomial is zero. */ Polynomial primitive () const; @@ -213,8 +165,8 @@ namespace Polynomials Polynomial &operator -= (const Polynomial &p); /** - * Test for equality of two polynomials. - */ + * Test for equality of two polynomials. + */ bool operator == (const Polynomial &p) const; /** @@ -223,8 +175,8 @@ namespace Polynomials void print(std::ostream &out) const; /** - * Write or read the data of this object to or - * from a stream for the purpose of serialization. + * 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); @@ -232,15 +184,13 @@ namespace Polynomials protected: /** - * This function performs the - * actual scaling. + * This function performs the actual scaling. */ static void scale (std::vector &coefficients, const number factor); /** - * This function performs the - * actual shift + * This function performs the actual shift */ template static void shift (std::vector &coefficients, @@ -253,49 +203,37 @@ namespace Polynomials 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. + * 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 + * 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. + * 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. + * 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$. + * 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. + * 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; }; @@ -367,32 +305,20 @@ namespace Polynomials { 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. + * 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. + * 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 > @@ -401,12 +327,8 @@ namespace Polynomials private: /** - * Computes the coefficients - * of the base class - * Polynomial. This function - * is static to allow to be - * called in the - * constructor. + * Computes the coefficients of the base class Polynomial. This + * function is static to allow to be called in the constructor. */ static void @@ -416,15 +338,12 @@ namespace Polynomials }; + /** - * 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); @@ -801,8 +720,7 @@ namespace Polynomials void Polynomial::serialize (Archive &ar, const unsigned int) { - // forward to serialization - // function in the base class. + // forward to serialization function in the base class. ar &static_cast(*this); ar &coefficients; ar &in_lagrange_product_form; diff --git a/deal.II/include/deal.II/base/polynomials_piecewise.h b/deal.II/include/deal.II/base/polynomials_piecewise.h new file mode 100644 index 0000000000..c9e1dbbcaa --- /dev/null +++ b/deal.II/include/deal.II/base/polynomials_piecewise.h @@ -0,0 +1,224 @@ +//--------------------------------------------------------------------------- +// $Id$ +// +// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010, 2011, 2012 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//--------------------------------------------------------------------------- +#ifndef __deal2__polynomials_piecewise_h +#define __deal2__polynomials_piecewise_h + + + +#include +#include +#include +#include +#include + +#include + +DEAL_II_NAMESPACE_OPEN + +/** + * @addtogroup Polynomials + * @{ + */ + +/** + * A namespace in which classes relating to the description of 1d polynomial + * spaces are declared. + */ +namespace Polynomials +{ + + /** + * Definition of piecewise 1D polynomials for the unit interval. This space + * allows the description of interpolating polynomials on parts of the unit + * interval, similarly to the definition of finite element basis functions + * on the subdivided elements. This primary purpose of this class is to + * allow constructing FE_Q_iso_Q1 elements that put additional degrees of + * freedom into an equivalent of a refined mesh instead of higher order + * polynomials, which is useful when using mixed finite elements. + * + * @author Martin Kronbichler, 2013 + */ + template + class PiecewisePolynomial : public Subscriptor + { + public: + /** + * Constructor for Lagrange polynomial on an interval that is a subset of + * the unit interval. It uses a polynomial description that is scaled to + * the size of the subinterval compared to the unit interval, the total + * number of intervals (subdivisions), the current index of the interval + * as well as if the polynomial spans onto the next interval (e.g., if it + * lives on two neighboring intervals). + * + * If the number of intervals is one, the piecewise polynomial behaves + * exactly like a usual polynomial. + */ + PiecewisePolynomial (const Polynomial &coefficients_on_interval, + const unsigned int n_intervals, + const unsigned int interval, + const bool spans_next_interval); + + /** + * Return the value of this polynomial at the given point, evaluating the + * underlying polynomial. The polynomial evaluates to zero when outside of + * the given interval (and possible the next one to the right when it + * spans over that range). + */ + 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. + * + * Note that all the derivatives evaluate to zero at the border between + * intervals (assuming exact arithmetics) in the interior of the unit + * interval, as there is no unique gradient value in that case for a + * piecewise polynomial. This is not always desired (e.g., when evaluating + * jumps of gradients on the element boundary), but it is the user's + * responsibility to avoid evaluation at these points when it does not + * make sense. + */ + void value (const number x, + std::vector &values) const; + + /** + * Degree of the polynomial. This is the degree of the underlying base + * polynomial. + */ + unsigned int degree () 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: + + /** + * Underlying polynomial object that is scaled to a subinterval and + * concatenated accordingly. + */ + Polynomial polynomial; + + /** + * Stores the number of intervals that the unit interval is divided into. + */ + unsigned int n_intervals; + + /** + * Stores the index of the current polynomial in the range of + * intervals. + */ + unsigned int interval; + + /** + * Store if the polynomial spans over two adjacent intervals, i.e., the + * one given in subinterval and the next one. + */ + bool spans_two_intervals; + }; + + + + /** + * Generates a complete Lagrange basis on a subdivision of the unit interval + * in smaller intervals for a given degree on the subintervals and number of + * intervals. + */ + std::vector > + generate_complete_Lagrange_basis_on_subdivisions (const unsigned int n_subdivisions, + const unsigned int base_degree); + +} + + +/** @} */ + +/* -------------------------- inline functions --------------------- */ + +namespace Polynomials +{ + template + inline + unsigned int + PiecewisePolynomial::degree () const + { + return polynomial.degree(); + } + + + + template + inline + number + PiecewisePolynomial::value (const number x) const + { + AssertIndexRange (interval, n_intervals); + number y = x; + // shift polynomial if necessary + if (n_intervals > 1) + { + const number step = 1./n_intervals; + + // polynomial spans over two intervals + if (spans_two_intervals == true) + { + const number offset = step * interval; + if (xoffset+step+step) + return 0; + else if (xoffset+step) + return 0; + else + y = x-offset; + } + + return polynomial.value(y); + } + else + return polynomial.value(x); + } + + + + template + template + inline + void + PiecewisePolynomial::serialize (Archive &ar, const unsigned int) + { + // forward to serialization function in the base class. + ar &static_cast(*this); + ar &polynomial; + ar &n_intervals; + ar &interval; + ar &spans_two_intervals; + } + +} + +DEAL_II_NAMESPACE_CLOSE + +#endif 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 972f0ab0dd..3babe00729 100644 --- a/deal.II/include/deal.II/base/tensor_product_polynomials.h +++ b/deal.II/include/deal.II/base/tensor_product_polynomials.h @@ -58,80 +58,57 @@ DEAL_II_NAMESPACE_OPEN * * @author Ralf Hartmann, 2000, 2004, Guido Kanschat, 2000, Wolfgang Bangerth 2003 */ -template +template > class TensorProductPolynomials { public: /** - * Access to the dimension of - * this object, for checking and - * automatic setting of dimension - * in other classes. + * 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. + * Constructor. pols is a vector of objects that should be derived + * or otherwise convertible to one-dimensional polynomial objects of type @p + * POLY (template argument of class). 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. + * 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. + * 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. + * Gives read access to the renumber vector. */ const std::vector &get_numbering() const; /** - * Gives read access to the - * inverse renumber vector. + * 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. + * 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. + * 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. + * 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, @@ -139,157 +116,89 @@ public: std::vector > &grad_grads) const; /** - * Computes the value of the - * ith tensor product - * polynomial at - * unit_point. Here i is - * given in tensor product + * 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. + * 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 + * 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. + * 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. + * 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. + * 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. + * Returns the number of tensor product polynomials. For n 1d + * polynomials this is ndim. */ unsigned int n () const; -private: +protected: /** - * Copy of the vector pols of - * polynomials given to the - * constructor. + * Copy of the vector pols of polynomials given to the constructor. */ - std::vector > polynomials; + std::vector polynomials; /** - * Number of tensor product - * polynomials. See n(). + * Number of tensor product polynomials. See n(). */ unsigned int n_tensor_pols; /** - * Index map for reordering the - * polynomials. + * Index map for reordering the polynomials. */ std::vector index_map; /** - * Index map for reordering the - * polynomials. + * 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. + * 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 + // fix to avoid compiler warnings about zero length arrays void compute_index (const unsigned int i, unsigned int (&indices)[(dim>0?dim:1)]) const; }; -#ifndef DOXYGEN - -template -inline -const std::vector & -TensorProductPolynomials::get_numbering() const -{ - return index_map; -} - - -template -inline -const std::vector & -TensorProductPolynomials::get_numbering_inverse() const -{ - return index_map_inverse; -} - - -#endif // DOXYGEN /** @@ -316,44 +225,26 @@ 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. + * 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. + * 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. + * 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. + * 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, @@ -361,130 +252,79 @@ public: std::vector > &grad_grads) const; /** - * Computes the value of the - * ith tensor product - * polynomial at - * unit_point. Here i is - * given in tensor product + * 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. + * 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 + * 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. + * 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. + * 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. + * 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. + * 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. + * 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. + * 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. + * 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. + * Given the input to the constructor, compute n_tensor_pols. */ static unsigned int @@ -495,28 +335,14 @@ private: #ifndef DOXYGEN -/* -------------- declaration of explicit specializations --- */ - -template <> -void -TensorProductPolynomials<1>::compute_index(const unsigned int n, - unsigned int (&index)[1]) const; -template <> -void -TensorProductPolynomials<2>::compute_index(const unsigned int n, - unsigned int (&index)[2]) const; -template <> -void -TensorProductPolynomials<3>::compute_index(const unsigned int n, - unsigned int (&index)[3]) const; - /* ---------------- template and inline functions ---------- */ -template +template template -TensorProductPolynomials:: +inline +TensorProductPolynomials:: TensorProductPolynomials(const std::vector &pols) : polynomials (pols.begin(), pols.end()), @@ -524,10 +350,8 @@ TensorProductPolynomials(const std::vector &pols) 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; i &pols) -template <> -void -AnisotropicPolynomials<1>::compute_index(const unsigned int n, - unsigned int (&index)[1]) const; -template <> -void -AnisotropicPolynomials<2>::compute_index(const unsigned int n, - unsigned int (&index)[2]) const; -template <> -void -AnisotropicPolynomials<3>::compute_index(const unsigned int n, - unsigned int (&index)[3]) const; +template +inline +unsigned int +TensorProductPolynomials::n() const +{ + if (dim == 0) + return numbers::invalid_unsigned_int; + else + return n_tensor_pols; +} + + + +template +inline +const std::vector & +TensorProductPolynomials::get_numbering() const +{ + return index_map; +} + + +template +inline +const std::vector & +TensorProductPolynomials::get_numbering_inverse() const +{ + return index_map_inverse; +} + + #endif // DOXYGEN DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/include/deal.II/base/tensor_product_polynomials_const.h b/deal.II/include/deal.II/base/tensor_product_polynomials_const.h index 9d47b28d72..82fa269e5f 100644 --- a/deal.II/include/deal.II/base/tensor_product_polynomials_const.h +++ b/deal.II/include/deal.II/base/tensor_product_polynomials_const.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -25,45 +26,21 @@ DEAL_II_NAMESPACE_OPEN -//TODO: This class is an almost exact copy of the TensorProductPolynomials class. They should be unified. A simple way to do that would be to derive this one from the other and in the implemented member functions simply defer to the corresponding function in the base class whenever appropriate. - /** * @addtogroup Polynomials * @{ */ /** - * Tensor product of given polynomials. - * - * Given a vector of n one-dimensional polynomials - * P1 to Pn, this class generates - * ndim polynomials of the form - * Qijk(x,y,z) = - * Pi(x)Pj(y)Pk(z)and a locally - * constant function. If the base polynomials are mutually orthogonal - * on the interval [-1,1] or [0,1], then the tensor product - * polynomials are orthogonal on [-1,1]dim or [0,1] - * dim, respectively. - * - * Indexing is as follows: the order of dim-dimensional polynomials is - * x-coordinates running fastest, then y-coordinate, etc. The first - * few polynomials are thus P1(x)P1(y), - * P2(x)P1(y), P3(x)P1(y), - * ..., P1(x)P2(y), - * P2(x)P2(y), P3(x)P2(y), - * ... and likewise in 3d. The locally constant function has the last index - * - * The output_indices() function prints the ordering of the - * dim-dimensional polynomials, i.e. for each polynomial in the - * polynomial space it gives the indices i,j,k of the one-dimensional - * polynomials in x,y and z direction. The ordering of the - * dim-dimensional polynomials can be changed by using the - * set_numbering() function. + * Tensor product of given polynomials and a locally constant function. This + * class inherits most of its functionality from TensorProductPolynomials. It + * works similarly to that class but adds a constant function for the last + * index. * - * @author Ralf Hartmann, 2000, 2004, Guido Kanschat, 2000, Wolfgang Bangerth 2003 + * @author Timo Heister, 2012 */ template -class TensorProductPolynomialsConst +class TensorProductPolynomialsConst : public TensorProductPolynomials { public: /** @@ -75,67 +52,24 @@ public: 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. + * 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 TensorProductPolynomialsConst (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. + * 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. + * 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. + * 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, @@ -143,202 +77,98 @@ public: std::vector > &grad_grads) const; /** - * Computes the value of the - * ith tensor product - * polynomial at - * unit_point. Here i is - * given in tensor product + * 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. + * 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 + * 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. + * 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. + * 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. + * 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 plus the constant - * function. For n 1d polynomials - * this is ndim+1. + * Returns the number of tensor product polynomials plus the constant + * function. For n 1d polynomials this is ndim+1. */ 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; }; /** @} */ + +/* ---------------- template and inline functions ---------- */ + #ifndef DOXYGEN template +template inline -const std::vector & -TensorProductPolynomialsConst::get_numbering() const +TensorProductPolynomialsConst:: +TensorProductPolynomialsConst(const std::vector &pols) + : + TensorProductPolynomials(pols) { - return index_map; + // append index for renumbering + this->index_map.push_back(this->n_tensor_pols); + this->index_map_inverse.push_back(this->n_tensor_pols); } + template inline -const std::vector & -TensorProductPolynomialsConst::get_numbering_inverse() const +unsigned int +TensorProductPolynomialsConst::n() const { - return index_map_inverse; + return this->n_tensor_pols+1; } -#endif // DOXYGEN - -#ifndef DOXYGEN -/* -------------- declaration of explicit specializations --- */ - -template <> -void -TensorProductPolynomialsConst<1>::compute_index(const unsigned int n, - unsigned int (&index)[1]) const; template <> -void -TensorProductPolynomialsConst<2>::compute_index(const unsigned int n, - unsigned int (&index)[2]) const; -template <> -void -TensorProductPolynomialsConst<3>::compute_index(const unsigned int n, - unsigned int (&index)[3]) const; - - -/* ---------------- template and inline functions ---------- */ - -template -template -TensorProductPolynomialsConst:: -TensorProductPolynomialsConst(const std::vector &pols) - : - polynomials (pols.begin(), pols.end()), - n_tensor_pols(Utilities::fixed_power(pols.size())+1), - index_map(n_tensor_pols), - index_map_inverse(n_tensor_pols) +inline +unsigned int +TensorProductPolynomialsConst<0>::n() const { - // 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(&data)+comp);; + return *(reinterpret_cast(&data)+comp); } /** diff --git a/deal.II/include/deal.II/dofs/dof_tools.h b/deal.II/include/deal.II/dofs/dof_tools.h index 765bf03568..dc30e7863a 100644 --- a/deal.II/include/deal.II/dofs/dof_tools.h +++ b/deal.II/include/deal.II/dofs/dof_tools.h @@ -954,12 +954,14 @@ namespace DoFTools * * Otherwise, if @p face_1 and @p face_2 are not active faces, this * function loops recursively over the children of @p face_1 and @p face_2. - * - * For this to work @p face_1 and @p face_2 must have the same - * refinement history, i.e. either @p face_1 and @p face_2 must be active - * faces or must be isotropically refined and have the same number of - * child faces that recursively obey this rule. (The anisotropic case - * is not yet implemented.) + * If only one of the two faces is active, then we recursively iterate + * over the children of the non-active ones and make sure that the + * solution function on the refined side equals that on the non-refined + * face in much the same way as we enforce hanging node constraints + * at places where differently refined cells come together. (However, + * unlike hanging nodes, we do not enforce the requirement that there + * be only a difference of one refinement level between the two sides + * of the domain you would like to be periodic). * * This routine only constrains DoFs that are not already constrained. * If this routine encounters a DoF that already is constrained (for @@ -967,9 +969,6 @@ namespace DoFTools * constraint (dofs the entry is constrained to, inhomogeneities) is * kept and nothing happens. * - * Furthermore, no DoFs belonging to (or belonging to any descendant - * of) @p face_2 get constrained or get marked as being constrained. - * * The flags in the @p component_mask (see @ref GlossComponentMask) * denote which components of the finite element space shall be * constrained with periodic boundary conditions. If it is left as @@ -1107,7 +1106,7 @@ namespace DoFTools * boundary with faces belonging to the second boundary with the help * of @p orthogonal_equality. * - * If this matching is successfull it constrains all DoFs associated + * If this matching is successful it constrains all DoFs associated * with the 'first' boundary to the respective DoFs of the 'second' * boundary respecting the relative orientation of the two faces. * @@ -1117,9 +1116,6 @@ namespace DoFTools * constraint (dofs the entry is constrained to, inhomogeneities) is * kept and nothing happens. * - * Furthermore, no DoFs belonging to the 'second' boundary get - * constrained or get marked as being constrained. - * * The flags in the last parameter, @p component_mask (see @ref * GlossComponentMask) denote which components of the finite element space * shall be constrained with periodic boundary conditions. If it is left diff --git a/deal.II/include/deal.II/fe/fe.h b/deal.II/include/deal.II/fe/fe.h index 3059dbcc02..fbf458b9fe 100644 --- a/deal.II/include/deal.II/fe/fe.h +++ b/deal.II/include/deal.II/fe/fe.h @@ -343,14 +343,11 @@ class FiniteElement : public Subscriptor, { public: /** - * Base class for internal data. - * Adds data for second derivatives to + * Base class for internal data. Adds data for second derivatives to * Mapping::InternalDataBase() * - * For information about the - * general purpose of this class, - * see the documentation of the - * base class. + * For information about the general purpose of this class, see the + * documentation of the base class. * * @author Guido Kanschat, 2001 */ @@ -358,35 +355,24 @@ public: { public: /** - * Destructor. Needed to - * avoid memory leaks with - * difference quotients. + * Destructor. Needed to avoid memory leaks with difference quotients. */ virtual ~InternalDataBase (); /** - * Initialize some pointers - * used in the computation of - * second derivatives by - * finite differencing of - * gradients. + * Initialize some pointers used in the computation of second derivatives + * by finite differencing of gradients. */ void initialize_2nd (const FiniteElement *element, const Mapping &mapping, const Quadrature &quadrature); /** - * Storage for FEValues - * objects needed to - * approximate second - * derivatives. + * Storage for FEValues objects needed to approximate second derivatives. * - * The ordering is p+hx, - * p+hy, p+hz, - * p-hx, p-hy, - * p-hz, where unused - * entries in lower dimensions - * are missing. + * The ordering is p+hx, p+hy, p+hz, p-hx, + * p-hy, p-hz, where unused entries in lower dimensions are + * missing. */ std::vector*> differences; }; @@ -400,74 +386,43 @@ public: const std::vector &nonzero_components); /** - * Virtual destructor. Makes sure - * that pointers to this class - * are deleted properly. + * Virtual destructor. Makes sure that pointers to this class are deleted + * properly. */ virtual ~FiniteElement (); /** - * Return a string that uniquely - * identifies a finite - * element. The general - * convention is that this is the - * class name, followed by the - * dimension in angle - * brackets, and the polynomial - * degree and whatever else is - * necessary in parentheses. For - * example, FE_Q<2>(3) is the - * value returned for a cubic - * element in 2d. + * Return a string that uniquely identifies a finite element. The general + * convention is that this is the class name, followed by the dimension in + * angle brackets, and the polynomial degree and whatever else is necessary + * in parentheses. For example, FE_Q<2>(3) is the value returned + * for a cubic element in 2d. * - * Systems of elements have their - * own naming convention, see the - * FESystem class. + * Systems of elements have their own naming convention, see the FESystem + * class. */ virtual std::string get_name () const = 0; /** - * This operator returns a - * reference to the present - * object if the argument given - * equals to zero. While this - * does not seem particularly - * useful, it is helpful in - * writing code that works with - * both ::DoFHandler and the hp - * version hp::DoFHandler, since - * one can then write code like - * this: + * This operator returns a reference to the present object if the argument + * given equals to zero. While this does not seem particularly useful, it is + * helpful in writing code that works with both ::DoFHandler and the hp + * version hp::DoFHandler, since one can then write code like this: * @code * dofs_per_cell * = dof_handler->get_fe()[cell->active_fe_index()].dofs_per_cell; * @endcode * - * This code doesn't work in both - * situations without the present - * operator because - * DoFHandler::get_fe() returns a - * finite element, whereas - * hp::DoFHandler::get_fe() - * returns a collection of finite - * elements that doesn't offer a - * dofs_per_cell - * member variable: one first has - * to select which finite element - * to work on, which is done - * using the - * operator[]. Fortunately, - * cell-@>active_fe_index() - * also works for non-hp classes - * and simply returns zero in - * that case. The present - * operator[] accepts this zero - * argument, by returning the - * finite element with index zero - * within its collection (that, - * of course, consists only of - * the present finite element - * anyway). + * This code doesn't work in both situations without the present operator + * because DoFHandler::get_fe() returns a finite element, whereas + * hp::DoFHandler::get_fe() returns a collection of finite elements that + * doesn't offer a dofs_per_cell member variable: one first has + * to select which finite element to work on, which is done using the + * operator[]. Fortunately, cell-@>active_fe_index() also works + * for non-hp classes and simply returns zero in that case. The present + * operator[] accepts this zero argument, by returning the finite element + * with index zero within its collection (that, of course, consists only of + * the present finite element anyway). */ const FiniteElement &operator[] (const unsigned int fe_index) const; @@ -477,139 +432,75 @@ public: */ /** - * Return the value of the - * @p ith shape function at the - * point @p p. @p p is a point - * on the reference element. If - * the finite element is - * vector-valued, then return the - * value of the only non-zero - * component of the vector value - * of this shape function. If the - * shape function has more than - * one non-zero component (which - * we refer to with the term - * non-primitive), then derived - * classes implementing this - * function should throw an - * exception of type - * ExcShapeFunctionNotPrimitive. In - * that case, use the - * shape_value_component() - * function. + * Return the value of the @p ith shape function at the point @p p. @p p is + * a point on the reference element. If the finite element is vector-valued, + * then return the value of the only non-zero component of the vector value + * of this shape function. If the shape function has more than one non-zero + * component (which we refer to with the term non-primitive), then derived + * classes implementing this function should throw an exception of type + * ExcShapeFunctionNotPrimitive. In that case, use the + * shape_value_component() function. * - * An - * ExcUnitShapeValuesDoNotExist - * is thrown if the shape values - * of the FiniteElement under - * consideration depends on the - * shape of the cell in real - * space. + * An ExcUnitShapeValuesDoNotExist is thrown if the shape values of the + * FiniteElement under consideration depends on the shape of the cell in + * real space. */ virtual double shape_value (const unsigned int i, const Point &p) const; /** - * Just like for shape_value(), - * but this function will be - * called when the shape function - * has more than one non-zero - * vector component. In that - * case, this function should - * return the value of the - * @p component-th vector - * component of the @p ith shape - * function at point @p p. + * Just like for shape_value(), but this function will be called when the + * shape function has more than one non-zero vector component. In that case, + * this function should return the value of the @p component-th vector + * component of the @p ith shape function at point @p p. */ virtual double shape_value_component (const unsigned int i, const Point &p, const unsigned int component) const; /** - * Return the gradient of the - * @p ith shape function at the - * point @p p. @p p is a point - * on the reference element, and - * likewise the gradient is the - * gradient on the unit cell with - * respect to unit cell - * coordinates. If - * the finite element is - * vector-valued, then return the - * value of the only non-zero - * component of the vector value - * of this shape function. If the - * shape function has more than - * one non-zero component (which - * we refer to with the term - * non-primitive), then derived - * classes implementing this - * function should throw an - * exception of type - * ExcShapeFunctionNotPrimitive. In - * that case, use the - * shape_grad_component() - * function. + * Return the gradient of the @p ith shape function at the point @p p. @p p + * is a point on the reference element, and likewise the gradient is the + * gradient on the unit cell with respect to unit cell coordinates. If the + * finite element is vector-valued, then return the value of the only + * non-zero component of the vector value of this shape function. If the + * shape function has more than one non-zero component (which we refer to + * with the term non-primitive), then derived classes implementing this + * function should throw an exception of type + * ExcShapeFunctionNotPrimitive. In that case, use the + * shape_grad_component() function. * - * An - * ExcUnitShapeValuesDoNotExist - * is thrown if the shape values - * of the FiniteElement under - * consideration depends on the - * shape of the cell in real - * space. + * An ExcUnitShapeValuesDoNotExist is thrown if the shape values of the + * FiniteElement under consideration depends on the shape of the cell in + * real space. */ virtual Tensor<1,dim> shape_grad (const unsigned int i, const Point &p) const; /** - * Just like for shape_grad(), - * but this function will be - * called when the shape function - * has more than one non-zero - * vector component. In that - * case, this function should - * return the gradient of the - * @p component-th vector - * component of the @p ith shape - * function at point @p p. + * Just like for shape_grad(), but this function will be called when the + * shape function has more than one non-zero vector component. In that case, + * this function should return the gradient of the @p component-th vector + * component of the @p ith shape function at point @p p. */ virtual Tensor<1,dim> shape_grad_component (const unsigned int i, const Point &p, const unsigned int component) const; /** - * Return the tensor of second - * derivatives of the @p ith - * shape function at point @p p - * on the unit cell. The - * derivatives are derivatives on - * the unit cell with respect to - * unit cell coordinates. If - * the finite element is - * vector-valued, then return the - * value of the only non-zero - * component of the vector value - * of this shape function. If the - * shape function has more than - * one non-zero component (which - * we refer to with the term - * non-primitive), then derived - * classes implementing this - * function should throw an - * exception of type - * ExcShapeFunctionNotPrimitive. In - * that case, use the - * shape_grad_grad_component() - * function. + * Return the tensor of second derivatives of the @p ith shape function at + * point @p p on the unit cell. The derivatives are derivatives on the unit + * cell with respect to unit cell coordinates. If the finite element is + * vector-valued, then return the value of the only non-zero component of + * the vector value of this shape function. If the shape function has more + * than one non-zero component (which we refer to with the term + * non-primitive), then derived classes implementing this function should + * throw an exception of type ExcShapeFunctionNotPrimitive. In that case, + * use the shape_grad_grad_component() function. * - * An - * ExcUnitShapeValuesDoNotExist - * is thrown if the shape values - * of the FiniteElement under - * consideration depends on the - * shape of the cell in real - * space. + * An ExcUnitShapeValuesDoNotExist is thrown if the shape values of the + * FiniteElement under consideration depends on the shape of the cell in + * real space. */ virtual Tensor<2,dim> shape_grad_grad (const unsigned int i, const Point &p) const; @@ -630,21 +521,14 @@ public: const Point &p, const unsigned int component) const; /** - * Check for non-zero values on a - * face in order to optimize out - * matrix elements. + * Check for non-zero values on a face in order to optimize out matrix + * elements. * - * This function returns - * @p true, if the shape - * function @p shape_index has - * non-zero values on the face - * @p face_index. + * This function returns @p true, if the shape function @p shape_index has + * non-zero values on the face @p face_index. * - * A default implementation is - * provided in this basis class - * which always returns @p - * true. This is the safe way to - * go. + * A default implementation is provided in this basis class which always + * returns @p true. This is the safe way to go. */ virtual bool has_support_on_face (const unsigned int shape_index, const unsigned int face_index) const; @@ -656,374 +540,214 @@ public: */ /** - * Projection from a fine grid - * space onto a coarse grid - * space. If this projection - * operator is associated with a - * matrix @p P, then the - * restriction of this matrix - * @p P_i to a single child cell - * is returned here. - * - * The matrix @p P is the - * concatenation or the sum of - * the cell matrices @p P_i, - * depending on the - * #restriction_is_additive_flags. This - * distinguishes interpolation - * (concatenation) and projection - * with respect to scalar + * Projection from a fine grid space onto a coarse grid space. If this + * projection operator is associated with a matrix @p P, then the + * restriction of this matrix @p P_i to a single child cell is returned + * here. + * + * The matrix @p P is the concatenation or the sum of the cell matrices @p + * P_i, depending on the #restriction_is_additive_flags. This distinguishes + * interpolation (concatenation) and projection with respect to scalar * products (summation). * - * Row and column indices are - * related to coarse grid and - * fine grid spaces, - * respectively, consistent with - * the definition of the - * associated operator. - * - * If projection matrices are not - * implemented in the derived - * finite element class, this - * function aborts with - * ExcProjectionVoid. You can - * check whether this is the case - * by calling the - * restriction_is_implemented() - * or the - * isotropic_restriction_is_implemented() - * function. + * Row and column indices are related to coarse grid and fine grid spaces, + * respectively, consistent with the definition of the associated operator. + * + * If projection matrices are not implemented in the derived finite element + * class, this function aborts with ExcProjectionVoid. You can check whether + * this is the case by calling the restriction_is_implemented() or the + * isotropic_restriction_is_implemented() function. */ - const FullMatrix & + virtual const FullMatrix & get_restriction_matrix (const unsigned int child, const RefinementCase &refinement_case=RefinementCase::isotropic_refinement) const; /** * Embedding matrix between grids. * - * The identity operator from a - * coarse grid space into a fine - * grid space is associated with - * a matrix @p P. The - * restriction of this matrix @p P_i to - * a single child cell is - * returned here. - * - * The matrix @p P is the - * concatenation, not the sum of - * the cell matrices - * @p P_i. That is, if the same - * non-zero entry j,k exists - * in in two different child - * matrices @p P_i, the value - * should be the same in both - * matrices and it is copied into - * the matrix @p P only once. - * - * Row and column indices are - * related to fine grid and - * coarse grid spaces, - * respectively, consistent with - * the definition of the - * associated operator. - * - * These matrices are used by - * routines assembling the - * prolongation matrix for - * multi-level methods. Upon - * assembling the transfer matrix - * between cells using this - * matrix array, zero elements in - * the prolongation matrix are - * discarded and will not fill up - * the transfer matrix. - * - * If projection matrices are not - * implemented in the derived - * finite element class, this - * function aborts with - * ExcEmbeddingVoid. You can - * check whether this is the case - * by calling the - * prolongation_is_implemented() - * or the - * isotropic_prolongation_is_implemented() - * function. + * The identity operator from a coarse grid space into a fine grid space is + * associated with a matrix @p P. The restriction of this matrix @p P_i to a + * single child cell is returned here. + * + * The matrix @p P is the concatenation, not the sum of the cell matrices @p + * P_i. That is, if the same non-zero entry j,k exists in in two + * different child matrices @p P_i, the value should be the same in both + * matrices and it is copied into the matrix @p P only once. + * + * Row and column indices are related to fine grid and coarse grid spaces, + * respectively, consistent with the definition of the associated operator. + * + * These matrices are used by routines assembling the prolongation matrix + * for multi-level methods. Upon assembling the transfer matrix between + * cells using this matrix array, zero elements in the prolongation matrix + * are discarded and will not fill up the transfer matrix. + * + * If projection matrices are not implemented in the derived finite element + * class, this function aborts with ExcEmbeddingVoid. You can check whether + * this is the case by calling the prolongation_is_implemented() or the + * isotropic_prolongation_is_implemented() function. */ - const FullMatrix & + virtual const FullMatrix & get_prolongation_matrix (const unsigned int child, const RefinementCase &refinement_case=RefinementCase::isotropic_refinement) const; /** - * Return whether this element implements - * its prolongation matrices. The return - * value also indicates whether a call to - * the get_prolongation_matrix() - * function will generate an error or - * not. - * - * Note, that this function - * returns true only - * if the prolongation matrices of - * the isotropic and all - * anisotropic refinement cases - * are implemented. If you are - * interested in the prolongation - * matrices for isotropic - * refinement only, use the - * isotropic_prolongation_is_implemented - * function instead. - * - * This function is mostly here in order - * to allow us to write more efficient - * test programs which we run on all - * kinds of weird elements, and for which - * we simply need to exclude certain - * tests in case something is not - * implemented. It will in general - * probably not be a great help in - * applications, since there is not much - * one can do if one needs these features - * and they are not implemented. This - * function could be used to check - * whether a call to - * get_prolongation_matrix() will - * succeed; however, one then still needs - * to cope with the lack of information - * this just expresses. + * Return whether this element implements its prolongation matrices. The + * return value also indicates whether a call to the + * get_prolongation_matrix() function will generate an error or not. + * + * Note, that this function returns true only if the + * prolongation matrices of the isotropic and all anisotropic refinement + * cases are implemented. If you are interested in the prolongation matrices + * for isotropic refinement only, use the + * isotropic_prolongation_is_implemented function instead. + * + * This function is mostly here in order to allow us to write more efficient + * test programs which we run on all kinds of weird elements, and for which + * we simply need to exclude certain tests in case something is not + * implemented. It will in general probably not be a great help in + * applications, since there is not much one can do if one needs these + * features and they are not implemented. This function could be used to + * check whether a call to get_prolongation_matrix() will succeed; + * however, one then still needs to cope with the lack of information this + * just expresses. */ bool prolongation_is_implemented () const; /** - * Return whether this element implements - * its prolongation matrices for isotropic - * children. The return value also - * indicates whether a call to the @p - * get_prolongation_matrix function will - * generate an error or not. - * - * This function is mostly here in order - * to allow us to write more efficient - * test programs which we run on all - * kinds of weird elements, and for which - * we simply need to exclude certain - * tests in case something is not - * implemented. It will in general - * probably not be a great help in - * applications, since there is not much - * one can do if one needs these features - * and they are not implemented. This - * function could be used to check - * whether a call to - * get_prolongation_matrix() will - * succeed; however, one then still needs - * to cope with the lack of information - * this just expresses. + * Return whether this element implements its prolongation matrices for + * isotropic children. The return value also indicates whether a call to the + * @p get_prolongation_matrix function will generate an error or not. + * + * This function is mostly here in order to allow us to write more efficient + * test programs which we run on all kinds of weird elements, and for which + * we simply need to exclude certain tests in case something is not + * implemented. It will in general probably not be a great help in + * applications, since there is not much one can do if one needs these + * features and they are not implemented. This function could be used to + * check whether a call to get_prolongation_matrix() will succeed; + * however, one then still needs to cope with the lack of information this + * just expresses. */ bool isotropic_prolongation_is_implemented () const; /** - * Return whether this element implements - * its restriction matrices. The return - * value also indicates whether a call to - * the get_restriction_matrix() - * function will generate an error or - * not. - * - * Note, that this function - * returns true only - * if the restriction matrices of - * the isotropic and all - * anisotropic refinement cases - * are implemented. If you are - * interested in the restriction - * matrices for isotropic - * refinement only, use the - * isotropic_restriction_is_implemented - * function instead. - * - * This function is mostly here in order - * to allow us to write more efficient - * test programs which we run on all - * kinds of weird elements, and for which - * we simply need to exclude certain - * tests in case something is not - * implemented. It will in general - * probably not be a great help in - * applications, since there is not much - * one can do if one needs these features - * and they are not implemented. This - * function could be used to check - * whether a call to - * get_restriction_matrix() will - * succeed; however, one then still needs - * to cope with the lack of information - * this just expresses. + * Return whether this element implements its restriction matrices. The + * return value also indicates whether a call to the + * get_restriction_matrix() function will generate an error or not. + * + * Note, that this function returns true only if the + * restriction matrices of the isotropic and all anisotropic refinement + * cases are implemented. If you are interested in the restriction matrices + * for isotropic refinement only, use the + * isotropic_restriction_is_implemented function instead. + * + * This function is mostly here in order to allow us to write more efficient + * test programs which we run on all kinds of weird elements, and for which + * we simply need to exclude certain tests in case something is not + * implemented. It will in general probably not be a great help in + * applications, since there is not much one can do if one needs these + * features and they are not implemented. This function could be used to + * check whether a call to get_restriction_matrix() will succeed; + * however, one then still needs to cope with the lack of information this + * just expresses. */ bool restriction_is_implemented () const; /** - * Return whether this element implements - * its restriction matrices for isotropic - * children. The return value also - * indicates whether a call to the @p - * get_restriction_matrix function will - * generate an error or not. - * - * This function is mostly here in order - * to allow us to write more efficient - * test programs which we run on all - * kinds of weird elements, and for which - * we simply need to exclude certain - * tests in case something is not - * implemented. It will in general - * probably not be a great help in - * applications, since there is not much - * one can do if one needs these features - * and they are not implemented. This - * function could be used to check - * whether a call to - * get_restriction_matrix() will - * succeed; however, one then still needs - * to cope with the lack of information - * this just expresses. + * Return whether this element implements its restriction matrices for + * isotropic children. The return value also indicates whether a call to the + * @p get_restriction_matrix function will generate an error or not. + * + * This function is mostly here in order to allow us to write more efficient + * test programs which we run on all kinds of weird elements, and for which + * we simply need to exclude certain tests in case something is not + * implemented. It will in general probably not be a great help in + * applications, since there is not much one can do if one needs these + * features and they are not implemented. This function could be used to + * check whether a call to get_restriction_matrix() will succeed; + * however, one then still needs to cope with the lack of information this + * just expresses. */ bool isotropic_restriction_is_implemented () const; /** - * Access the - * #restriction_is_additive_flags - * field. See there for more + * Access the #restriction_is_additive_flags field. See there for more * information on its contents. * - * The index must be between zero - * and the number of shape - * functions of this element. + * The index must be between zero and the number of shape functions of this + * element. */ bool restriction_is_additive (const unsigned int index) const; /** - * Return a readonly reference to - * the matrix which describes the - * constraints at the interface - * between a refined and an - * unrefined cell. - * - * The matrix is obviously empty - * in only one dimension, - * since there are no constraints - * then. - * - * Note that some finite elements - * do not (yet) implement hanging - * node constraints. If this is - * the case, then this function - * will generate an exception, - * since no useful return value - * can be generated. If you - * should have a way to live with - * this, then you might want to - * use the - * constraints_are_implemented() - * function to check up front - * whethehr this function will - * succeed or generate the - * exception. + * Return a read only reference to the matrix which describes the + * constraints at the interface between a refined and an unrefined cell. + * + * The matrix is obviously empty in only one dimension, since there are no + * constraints then. + * + * Note that some finite elements do not (yet) implement hanging node + * constraints. If this is the case, then this function will generate an + * exception, since no useful return value can be generated. If you should + * have a way to live with this, then you might want to use the + * constraints_are_implemented() function to check up front whether this + * function will succeed or generate the exception. */ const FullMatrix &constraints (const dealii::internal::SubfaceCase &subface_case=dealii::internal::SubfaceCase::case_isotropic) const; /** - * Return whether this element - * implements its hanging node - * constraints. The return value - * also indicates whether a call - * to the constraints() function + * Return whether this element implements its hanging node constraints. The + * return value also indicates whether a call to the constraints() function * will generate an error or not. * - * This function is mostly here - * in order to allow us to write - * more efficient test programs - * which we run on all kinds of - * weird elements, and for which - * we simply need to exclude - * certain tests in case hanging - * node constraints are not - * implemented. It will in - * general probably not be a - * great help in applications, - * since there is not much one - * can do if one needs hanging - * node constraints and they are - * not implemented. This function - * could be used to check whether - * a call to constraints() - * will succeed; however, one - * then still needs to cope with - * the lack of information this + * This function is mostly here in order to allow us to write more efficient + * test programs which we run on all kinds of weird elements, and for which + * we simply need to exclude certain tests in case hanging node constraints + * are not implemented. It will in general probably not be a great help in + * applications, since there is not much one can do if one needs hanging + * node constraints and they are not implemented. This function could be + * used to check whether a call to constraints() will succeed; + * however, one then still needs to cope with the lack of information this * just expresses. */ bool constraints_are_implemented (const dealii::internal::SubfaceCase &subface_case=dealii::internal::SubfaceCase::case_isotropic) const; /** - * Return whether this element - * implements its hanging node - * constraints in the new way, - * which has to be used to make - * elements "hp compatible". - * That means, the element properly - * implements the - * get_face_interpolation_matrix - * and get_subface_interpolation_matrix - * methods. Therefore the return - * value also indicates whether a call - * to the get_face_interpolation_matrix() - * method and the get_subface_interpolation_matrix() - * method will generate an error or not. - * - * Currently the main purpose of this - * function is to allow the - * make_hanging_node_constraints method - * to decide whether the new procedures, - * which are supposed to work in the hp - * framework can be used, or if the old - * well verified but not hp capable - * functions should be used. Once the - * transition to the new scheme for - * computing the interface constraints is - * complete, this function will be - * superfluous and will probably go away. - * - * Derived classes should implement this - * function accordingly. The default - * assumption is that a finite element - * does not provide hp capable face - * interpolation, and the default - * implementation therefore returns @p - * false. + * Return whether this element implements its hanging node constraints in + * the new way, which has to be used to make elements "hp compatible". That + * means, the element properly implements the get_face_interpolation_matrix + * and get_subface_interpolation_matrix methods. Therefore the return value + * also indicates whether a call to the get_face_interpolation_matrix() + * method and the get_subface_interpolation_matrix() method will generate an + * error or not. + * + * Currently the main purpose of this function is to allow the + * make_hanging_node_constraints method to decide whether the new + * procedures, which are supposed to work in the hp framework can be used, + * or if the old well verified but not hp capable functions should be used. + * Once the transition to the new scheme for computing the interface + * constraints is complete, this function will be superfluous and will + * probably go away. + * + * Derived classes should implement this function accordingly. The default + * assumption is that a finite element does not provide hp capable face + * interpolation, and the default implementation therefore returns @p false. */ virtual bool hp_constraints_are_implemented () const; /** - * Return the matrix - * interpolating from the given - * finite element to the present - * one. The size of the matrix is - * then #dofs_per_cell times + * Return the matrix interpolating from the given finite element to the + * present one. The size of the matrix is then #dofs_per_cell times * source.#dofs_per_cell. * - * Derived elements will have to - * implement this function. They - * may only provide interpolation - * matrices for certain source - * finite elements, for example - * those from the same family. If - * they don't implement - * interpolation from a given - * element, then they must throw - * an exception of type + * Derived elements will have to implement this function. They may only + * provide interpolation matrices for certain source finite elements, for + * example those from the same family. If they don't implement interpolation + * from a given element, then they must throw an exception of type * ExcInterpolationNotImplemented. */ virtual void @@ -1038,24 +762,14 @@ public: /** - * Return the matrix - * interpolating from a face of - * of one element to the face of - * the neighboring element. - * The size of the matrix is - * then source.#dofs_per_face times - * this->#dofs_per_face. + * Return the matrix interpolating from a face of of one element to the face + * of the neighboring element. The size of the matrix is then + * source.#dofs_per_face times this->#dofs_per_face. * - * Derived elements will have to - * implement this function. They - * may only provide interpolation - * matrices for certain source - * finite elements, for example - * those from the same family. If - * they don't implement - * interpolation from a given - * element, then they must throw - * an exception of type + * Derived elements will have to implement this function. They may only + * provide interpolation matrices for certain source finite elements, for + * example those from the same family. If they don't implement interpolation + * from a given element, then they must throw an exception of type * ExcInterpolationNotImplemented. */ virtual void @@ -1064,24 +778,14 @@ public: /** - * Return the matrix - * interpolating from a face of - * of one element to the subface of - * the neighboring element. - * The size of the matrix is - * then source.#dofs_per_face times - * this->#dofs_per_face. - * - * Derived elements will have to - * implement this function. They - * may only provide interpolation - * matrices for certain source - * finite elements, for example - * those from the same family. If - * they don't implement - * interpolation from a given - * element, then they must throw - * an exception of type + * Return the matrix interpolating from a face of of one element to the + * subface of the neighboring element. The size of the matrix is then + * source.#dofs_per_face times this->#dofs_per_face. + * + * Derived elements will have to implement this function. They may only + * provide interpolation matrices for certain source finite elements, for + * example those from the same family. If they don't implement interpolation + * from a given element, then they must throw an exception of type * ExcInterpolationNotImplemented. */ virtual void @@ -1134,15 +838,11 @@ public: hp_quad_dof_identities (const FiniteElement &fe_other) const; /** - * Return whether this element dominates - * the one given as argument when they - * meet at a common face, - * whether it is the other way around, - * whether neither dominates, or if - * either could dominate. + * Return whether this element dominates the one given as argument when they + * meet at a common face, whether it is the other way around, whether + * neither dominates, or if either could dominate. * - * For a definition of domination, see - * FiniteElementBase::Domination and in + * For a definition of domination, see FiniteElementBase::Domination and in * particular the @ref hp_paper "hp paper". */ virtual @@ -1152,21 +852,13 @@ public: //@} /** - * Comparison operator. We also - * check for equality of the - * constraint matrix, which is - * quite an expensive operation. - * Do therefore use this function - * with care, if possible only - * for debugging purposes. + * Comparison operator. We also check for equality of the constraint matrix, + * which is quite an expensive operation. Do therefore use this function + * with care, if possible only for debugging purposes. * - * Since this function is not - * that important, we avoid an - * implementational question - * about comparing arrays and do - * not compare the matrix arrays - * #restriction and - * #prolongation. + * Since this function is not that important, we avoid an implementational + * question about comparing arrays and do not compare the matrix arrays + * #restriction and #prolongation. */ bool operator == (const FiniteElement &) const; @@ -1175,116 +867,65 @@ public: * @{ */ /** - * Compute vector component and - * index of this shape function - * within the shape functions - * corresponding to this - * component from the index of a - * shape function within this - * finite element. + * Compute vector component and index of this shape function within the + * shape functions corresponding to this component from the index of a shape + * function within this finite element. * - * If the element is scalar, then - * the component is always zero, - * and the index within this - * component is equal to the - * overall index. - * - * If the shape function - * referenced has more than one - * non-zero component, then it - * cannot be associated with one - * vector component, and an - * exception of type - * ExcShapeFunctionNotPrimitive - * will be raised. - * - * Note that if the element is - * composed of other (base) - * elements, and a base element - * has more than one component - * but all its shape functions - * are primitive (i.e. are - * non-zero in only one - * component), then this mapping - * contains valid - * information. However, the - * index of a shape function of - * this element within one - * component (i.e. the second - * number of the respective entry - * of this array) does not - * indicate the index of the - * respective shape function - * within the base element (since - * that has more than one - * vector-component). For this - * information, refer to the - * #system_to_base_table field - * and the - * system_to_base_index() - * function. + * If the element is scalar, then the component is always zero, and the + * index within this component is equal to the overall index. + * + * If the shape function referenced has more than one non-zero component, + * then it cannot be associated with one vector component, and an exception + * of type ExcShapeFunctionNotPrimitive will be raised. + * + * Note that if the element is composed of other (base) elements, and a base + * element has more than one component but all its shape functions are + * primitive (i.e. are non-zero in only one component), then this mapping + * contains valid information. However, the index of a shape function of + * this element within one component (i.e. the second number of the + * respective entry of this array) does not indicate the index of the + * respective shape function within the base element (since that has more + * than one vector-component). For this information, refer to the + * #system_to_base_table field and the system_to_base_index() function. * - * The use of this function is - * explained extensively in the - * step-8 and @ref - * step_20 "step-20" tutorial - * programs as well as in the - * @ref vector_valued module. + * The use of this function is explained extensively in the step-8 and @ref + * step_20 "step-20" tutorial programs as well as in the @ref vector_valued + * module. */ std::pair system_to_component_index (const unsigned int index) const; /** - * Compute the shape function for - * the given vector component and - * index. + * Compute the shape function for the given vector component and index. * - * If the element is scalar, then - * the component must be zero, - * and the index within this - * component is equal to the - * overall index. + * If the element is scalar, then the component must be zero, and the index + * within this component is equal to the overall index. * - * This is the opposite operation - * from the system_to_component_index() + * This is the opposite operation from the system_to_component_index() * function. */ unsigned int component_to_system_index(const unsigned int component, const unsigned int index) const; /** - * Same as - * system_to_component_index(), - * but do it for shape functions - * and their indices on a - * face. The range of allowed - * indices is therefore + * Same as system_to_component_index(), but do it for shape functions and + * their indices on a face. The range of allowed indices is therefore * 0..#dofs_per_face. * - * You will rarely need this - * function in application - * programs, since almost all - * application codes only need to - * deal with cell indices, not - * face indices. The function is - * mainly there for use inside - * the library. + * You will rarely need this function in application programs, since almost + * all application codes only need to deal with cell indices, not face + * indices. The function is mainly there for use inside the library. */ std::pair face_system_to_component_index (const unsigned int index) const; /** - * For faces with non-standard - * face_orientation in 3D, the dofs on - * faces (quads) have to be permuted in - * order to be combined with the correct - * shape functions. Given a local dof @p - * index on a quad, return the local index, - * if the face has non-standard - * face_orientation, face_flip or - * face_rotation. In 2D and 1D there is no - * need for permutation and consequently - * an exception is thrown. + * For faces with non-standard face_orientation in 3D, the dofs on faces + * (quads) have to be permuted in order to be combined with the correct + * shape functions. Given a local dof @p index on a quad, return the local + * index, if the face has non-standard face_orientation, face_flip or + * face_rotation. In 2D and 1D there is no need for permutation and + * consequently an exception is thrown. */ unsigned int adjust_quad_dof_index_for_face_orientation (const unsigned int index, const bool face_orientation, @@ -1292,268 +933,166 @@ public: const bool face_rotation) const; /** - * For lines with non-standard - * line_orientation in 3D, the dofs on - * lines have to be permuted in order to be - * combined with the correct shape - * functions. Given a local dof @p index on - * a line, return the local index, if the - * line has non-standard - * line_orientation. In 2D and 1D there is - * no need for permutation, so the given - * index is simply returned. + * For lines with non-standard line_orientation in 3D, the dofs on lines + * have to be permuted in order to be combined with the correct shape + * functions. Given a local dof @p index on a line, return the local index, + * if the line has non-standard line_orientation. In 2D and 1D there is no + * need for permutation, so the given index is simply returned. */ unsigned int adjust_line_dof_index_for_line_orientation (const unsigned int index, const bool line_orientation) const; /** - * Return in which of the vector - * components of this finite - * element the @p ith shape - * function is non-zero. The - * length of the returned array - * is equal to the number of - * vector components of this - * element. + * Return in which of the vector components of this finite element the @p + * ith shape function is non-zero. The length of the returned array is equal + * to the number of vector components of this element. + * + * For most finite element spaces, the result of this function will be a + * vector with exactly one element being @p true, since for most spaces the + * individual vector components are independent. In that case, the component + * with the single zero is also the first element of what + * system_to_component_index() returns. * - * For most finite element - * spaces, the result of this - * function will be a vector with - * exactly one element being - * @p true, since for most - * spaces the individual vector - * components are independent. In - * that case, the component with - * the single zero is also the - * first element of what - * system_to_component_index() - * returns. - * - * Only for those spaces that couple the - * components, for example to make a - * shape function divergence free, will - * there be more than one @p true entry. - * Elements for which this is true are - * called non-primitive (see - * @ref GlossPrimitive). + * Only for those spaces that couple the components, for example to make a + * shape function divergence free, will there be more than one @p true + * entry. Elements for which this is true are called non-primitive (see + * @ref GlossPrimitive). */ const ComponentMask & get_nonzero_components (const unsigned int i) const; /** - * Return in how many vector - * components the @p ith shape - * function is non-zero. This - * value equals the number of - * entries equal to @p true in - * the result of the - * get_nonzero_components() - * function. + * Return in how many vector components the @p ith shape function is + * non-zero. This value equals the number of entries equal to @p true in the + * result of the get_nonzero_components() function. * - * For most finite element - * spaces, the result will be - * equal to one. It is not equal - * to one only for those ansatz - * spaces for which vector-valued - * shape functions couple the - * individual components, for - * example in order to make them - * divergence-free. + * For most finite element spaces, the result will be equal to one. It is + * not equal to one only for those ansatz spaces for which vector-valued + * shape functions couple the individual components, for example in order to + * make them divergence-free. */ unsigned int n_nonzero_components (const unsigned int i) const; /** - * Return whether the @p ith - * shape function is primitive in - * the sense that the shape - * function is non-zero in only - * one vector - * component. Non-primitive shape - * functions would then, for - * example, be those of - * divergence free ansatz spaces, - * in which the individual vector + * Return whether the @p ith shape function is primitive in the sense that + * the shape function is non-zero in only one vector + * component. Non-primitive shape functions would then, for example, be + * those of divergence free ansatz spaces, in which the individual vector * components are coupled. * - * The result of the function is - * @p true if and only if the - * result of - * n_nonzero_components(i) is - * equal to one. + * The result of the function is @p true if and only if the result of + * n_nonzero_components(i) is equal to one. */ bool is_primitive (const unsigned int i) const; /** - * Import function that is overloaded - * by the one above and would otherwise + * Import function that is overloaded by the one above and would otherwise * be hidden. */ using FiniteElementData::is_primitive; /** - * Number of base elements in a - * mixed discretization. - * - * Note that even for vector - * valued finite elements, the - * number of components needs not - * coincide with the number of - * base elements, since they may - * be reused. For example, if you - * create a FESystem with - * three identical finite element - * classes by using the - * constructor that takes one - * finite element and a - * multiplicity, then the number - * of base elements is still one, - * although the number of - * components of the finite - * element is equal to the - * multiplicity. + * Number of base elements in a mixed discretization. + * + * Note that even for vector valued finite elements, the number of + * components needs not coincide with the number of base elements, since + * they may be reused. For example, if you create a FESystem with three + * identical finite element classes by using the constructor that takes one + * finite element and a multiplicity, then the number of base elements is + * still one, although the number of components of the finite element is + * equal to the multiplicity. */ unsigned int n_base_elements () const; /** - * Access to base element - * objects. If the element is - * atomic, then - * base_element(0) is - * @p this. + * Access to base element objects. If the element is atomic, then + * base_element(0) is @p this. */ virtual const FiniteElement & base_element (const unsigned int index) const; /** - * This index denotes how often - * the base element @p index is - * used in a composed element. If - * the element is atomic, then - * the result is always equal to - * one. See the documentation for - * the n_base_elements() - * function for more details. + * This index denotes how often the base element @p index is used in a + * composed element. If the element is atomic, then the result is always + * equal to one. See the documentation for the n_base_elements() function + * for more details. */ unsigned int element_multiplicity (const unsigned int index) const; /** - * Return for shape function - * @p index the base element it - * belongs to, the number of the - * copy of this base element - * (which is between zero and the - * multiplicity of this element), - * and the index of this shape - * function within this base - * element. + * Return for shape function @p index the base element it belongs to, the + * number of the copy of this base element (which is between zero and the + * multiplicity of this element), and the index of this shape function + * within this base element. * - * If the element is not composed of - * others, then base and instance - * are always zero, and the index - * is equal to the number of the - * shape function. If the element - * is composed of single - * instances of other elements - * (i.e. all with multiplicity - * one) all of which are scalar, - * then base values and dof - * indices within this element - * are equal to the - * #system_to_component_table. It - * differs only in case the - * element is composed of other - * elements and at least one of - * them is vector-valued itself. - * - * This function returns valid - * values also in the case of - * vector-valued - * (i.e. non-primitive) shape - * functions, in contrast to the - * system_to_component_index() - * function. + * If the element is not composed of others, then base and instance are + * always zero, and the index is equal to the number of the shape + * function. If the element is composed of single instances of other + * elements (i.e. all with multiplicity one) all of which are scalar, then + * base values and dof indices within this element are equal to the + * #system_to_component_table. It differs only in case the element is + * composed of other elements and at least one of them is vector-valued + * itself. + * + * This function returns valid values also in the case of vector-valued + * (i.e. non-primitive) shape functions, in contrast to the + * system_to_component_index() function. */ std::pair, unsigned int> system_to_base_index (const unsigned int index) const; /** - * Same as - * system_to_base_index(), but - * for degrees of freedom located - * on a face. The range of allowed - * indices is therefore - * 0..#dofs_per_face. + * Same as system_to_base_index(), but for degrees of freedom located on a + * face. The range of allowed indices is therefore 0..#dofs_per_face. * - * You will rarely need this - * function in application - * programs, since almost all - * application codes only need to - * deal with cell indices, not - * face indices. The function is - * mainly there for use inside - * the library. + * You will rarely need this function in application programs, since almost + * all application codes only need to deal with cell indices, not face + * indices. The function is mainly there for use inside the library. */ std::pair, unsigned int> face_system_to_base_index (const unsigned int index) const; /** - * Given a base element number, - * return the first block of a - * BlockVector it would generate. + * Given a base element number, return the first block of a BlockVector it + * would generate. */ unsigned int first_block_of_base (const unsigned int b) const; /** - * For each vector component, - * return which base - * element implements this - * component and which vector - * component in this base element - * this is. This information is - * only of interest for - * vector-valued finite elements - * which are composed of several - * sub-elements. In that case, - * one may want to obtain - * information about the element - * implementing a certain vector - * component, which can be done - * using this function and the - * FESystem::base_element() - * function. + * For each vector component, return which base element implements this + * component and which vector component in this base element this is. This + * information is only of interest for vector-valued finite elements which + * are composed of several sub-elements. In that case, one may want to + * obtain information about the element implementing a certain vector + * component, which can be done using this function and the + * FESystem::base_element() function. * - * If this is a scalar finite - * element, then the return value - * is always equal to a pair of - * zeros. + * If this is a scalar finite element, then the return value is always equal + * to a pair of zeros. */ std::pair component_to_base_index (const unsigned int component) const; /** - * Return the base element for - * this block and the number of - * the copy of the base element. + * Return the base element for this block and the number of the copy of the + * base element. */ std::pair block_to_base_index (const unsigned int block) const; /** - * The vector block and the index - * inside the block for this - * shape function. + * The vector block and the index inside the block for this shape function. */ std::pair system_to_block_index (const unsigned int component) const; /** - * The vector block for this - * component. + * The vector block for this component. */ unsigned int component_to_block_index (const unsigned int component) const; @@ -1566,57 +1105,53 @@ public: */ /** - * Return a component mask with as many elements as this - * object has vector components and of which exactly the - * one component is true that corresponds to the given - * argument. See @ref GlossComponentMask "the glossary" - * for more information. + * Return a component mask with as many elements as this object has vector + * components and of which exactly the one component is true that + * corresponds to the given argument. See @ref GlossComponentMask "the + * glossary" for more information. * - * @param scalar An object that represents a single scalar - * vector component of this finite element. - * @return A component mask that is false in all components - * except for the one that corresponds to the argument. + * @param scalar An object that represents a single scalar vector component + * of this finite element. + * @return A component mask that is false in all components except for the + * one that corresponds to the argument. */ ComponentMask component_mask (const FEValuesExtractors::Scalar &scalar) const; /** - * Return a component mask with as many elements as this - * object has vector components and of which exactly the - * dim components are true that correspond to the given - * argument. See @ref GlossComponentMask "the glossary" - * for more information. + * Return a component mask with as many elements as this object has vector + * components and of which exactly the dim components are true + * that correspond to the given argument. See @ref GlossComponentMask "the + * glossary" for more information. * - * @param vector An object that represents dim - * vector components of this finite element. - * @return A component mask that is false in all components - * except for the ones that corresponds to the argument. + * @param vector An object that represents dim vector components of this + * finite element. + * @return A component mask that is false in all components except for the + * ones that corresponds to the argument. */ ComponentMask component_mask (const FEValuesExtractors::Vector &vector) const; /** - * Return a component mask with as many elements as this - * object has vector components and of which exactly the - * dim*(dim+1)/2 components are true that - * correspond to the given argument. See @ref GlossComponentMask "the glossary" - * for more information. + * Return a component mask with as many elements as this object has vector + * components and of which exactly the dim*(dim+1)/2 components + * are true that correspond to the given argument. See @ref + * GlossComponentMask "the glossary" for more information. * - * @param sym_tensor An object that represents dim*(dim+1)/2 - * components of this finite element that are jointly to be - * interpreted as forming a symmetric tensor. - * @return A component mask that is false in all components - * except for the ones that corresponds to the argument. + * @param sym_tensor An object that represents dim*(dim+1)/2 components of + * this finite element that are jointly to be interpreted as forming a + * symmetric tensor. @return A component mask that is false in all + * components except for the ones that corresponds to the argument. */ ComponentMask component_mask (const FEValuesExtractors::SymmetricTensor<2> &sym_tensor) const; /** * Given a block mask (see @ref GlossBlockMask "this glossary entry"), - * produce a component mask (see @ref GlossComponentMask "this glossary entry") - * that represents the components that correspond to the blocks selected in - * the input argument. This is essentially a conversion operator from - * BlockMask to ComponentMask. + * produce a component mask (see @ref GlossComponentMask "this glossary + * entry") that represents the components that correspond to the blocks + * selected in the input argument. This is essentially a conversion operator + * from BlockMask to ComponentMask. * * @param block_mask The mask that selects individual blocks of the finite * element @@ -1627,84 +1162,80 @@ public: component_mask (const BlockMask &block_mask) const; /** - * Return a block mask with as many elements as this - * object has blocks and of which exactly the - * one component is true that corresponds to the given - * argument. See @ref GlossBlockMask "the glossary" - * for more information. + * Return a block mask with as many elements as this object has blocks and + * of which exactly the one component is true that corresponds to the given + * argument. See @ref GlossBlockMask "the glossary" for more information. * - * @note This function will only succeed if the scalar referenced - * by the argument encompasses a complete block. In other words, - * if, for example, you pass an extractor for the single - * $x$ velocity and this object represents an FE_RaviartThomas - * object, then the single scalar object you selected is part - * of a larger block and consequently there is no block mask that - * would represent it. The function will then produce an exception. + * @note This function will only succeed if the scalar referenced by the + * argument encompasses a complete block. In other words, if, for example, + * you pass an extractor for the single $x$ velocity and this object + * represents an FE_RaviartThomas object, then the single scalar object you + * selected is part of a larger block and consequently there is no block + * mask that would represent it. The function will then produce an + * exception. * - * @param scalar An object that represents a single scalar - * vector component of this finite element. - * @return A component mask that is false in all components - * except for the one that corresponds to the argument. + * @param scalar An object that represents a single scalar vector component + * of this finite element. + * @return A component mask that is false in all components except for the + * one that corresponds to the argument. */ BlockMask block_mask (const FEValuesExtractors::Scalar &scalar) const; /** - * Return a component mask with as many elements as this - * object has vector components and of which exactly the - * dim components are true that correspond to the given - * argument. See @ref GlossBlockMask "the glossary" - * for more information. + * Return a component mask with as many elements as this object has vector + * components and of which exactly the dim components are true + * that correspond to the given argument. See @ref GlossBlockMask "the + * glossary" for more information. * * @note The same caveat applies as to the version of the function above: - * The extractor object passed as argument must be so that it corresponds - * to full blocks and does not split blocks of this element. + * The extractor object passed as argument must be so that it corresponds to + * full blocks and does not split blocks of this element. * - * @param vector An object that represents dim - * vector components of this finite element. - * @return A component mask that is false in all components - * except for the ones that corresponds to the argument. + * @param vector An object that represents dim vector components of this + * finite element. + * @return A component mask that is false in all components except for the + * ones that corresponds to the argument. */ BlockMask block_mask (const FEValuesExtractors::Vector &vector) const; /** - * Return a component mask with as many elements as this - * object has vector components and of which exactly the - * dim*(dim+1)/2 components are true that - * correspond to the given argument. See @ref GlossBlockMask "the glossary" - * for more information. + * Return a component mask with as many elements as this object has vector + * components and of which exactly the dim*(dim+1)/2 components + * are true that correspond to the given argument. See @ref GlossBlockMask + * "the glossary" for more information. * * @note The same caveat applies as to the version of the function above: - * The extractor object passed as argument must be so that it corresponds - * to full blocks and does not split blocks of this element. + * The extractor object passed as argument must be so that it corresponds to + * full blocks and does not split blocks of this element. * - * @param sym_tensor An object that represents dim*(dim+1)/2 - * components of this finite element that are jointly to be - * interpreted as forming a symmetric tensor. - * @return A component mask that is false in all components - * except for the ones that corresponds to the argument. + * @param sym_tensor An object that represents dim*(dim+1)/2 components of + * this finite element that are jointly to be interpreted as forming a + * symmetric tensor. + * @return A component mask that is false in all components except for the + * ones that corresponds to the argument. */ BlockMask block_mask (const FEValuesExtractors::SymmetricTensor<2> &sym_tensor) const; /** - * Given a component mask (see @ref GlossComponentMask "this glossary entry"), - * produce a block mask (see @ref GlossBlockMask "this glossary entry") - * that represents the blocks that correspond to the components selected in - * the input argument. This is essentially a conversion operator from - * ComponentMask to BlockMask. + * Given a component mask (see @ref GlossComponentMask "this glossary + * entry"), produce a block mask (see @ref GlossBlockMask "this glossary + * entry") that represents the blocks that correspond to the components + * selected in the input argument. This is essentially a conversion operator + * from ComponentMask to BlockMask. * - * @note This function will only succeed if the components referenced - * by the argument encompasses complete blocks. In other words, - * if, for example, you pass an component mask for the single - * $x$ velocity and this object represents an FE_RaviartThomas - * object, then the single component you selected is part - * of a larger block and consequently there is no block mask that - * would represent it. The function will then produce an exception. + * @note This function will only succeed if the components referenced by the + * argument encompasses complete blocks. In other words, if, for example, + * you pass an component mask for the single $x$ velocity and this object + * represents an FE_RaviartThomas object, then the single component you + * selected is part of a larger block and consequently there is no block + * mask that would represent it. The function will then produce an + * exception. * - * @param component_mask The mask that selects individual components of the finite - * element + * @param component_mask The mask that selects individual components of the + * finite element * @return A mask that selects those blocks corresponding to the selected * blocks of the input argument. */ @@ -1719,199 +1250,118 @@ public: */ /** - * Return the support points of - * the trial functions on the - * unit cell, if the derived - * finite element defines some. - * Finite elements that allow - * some kind of interpolation - * operation usually have support - * points. On the other hand, - * elements that define their - * degrees of freedom by, for - * example, moments on faces, or - * as derivatives, don't have - * support points. In that case, - * the returned field is empty. - * - * If the finite element defines - * support points, then their - * number equals the number of - * degrees of freedom of the - * element. The order of points - * in the array matches that - * returned by the - * cell->get_dof_indices + * Return the support points of the trial functions on the unit cell, if the + * derived finite element defines some. Finite elements that allow some + * kind of interpolation operation usually have support points. On the other + * hand, elements that define their degrees of freedom by, for example, + * moments on faces, or as derivatives, don't have support points. In that + * case, the returned field is empty. + * + * If the finite element defines support points, then their number equals + * the number of degrees of freedom of the element. The order of points in + * the array matches that returned by the cell->get_dof_indices * function. * - * See the class documentation - * for details on support points. + * See the class documentation for details on support points. */ const std::vector > & get_unit_support_points () const; /** - * Return whether a finite - * element has defined support - * points. If the result is true, - * then a call to the - * get_unit_support_points() - * yields a non-empty array. + * Return whether a finite element has defined support points. If the result + * is true, then a call to the get_unit_support_points() yields a non-empty + * array. * - * The result may be false if an - * element is not defined by - * interpolating shape functions, - * for example by P-elements on - * quadrilaterals. It will - * usually only be true if the - * element constructs its shape - * functions by the requirement - * that they be one at a certain - * point and zero at all the - * points associated with the - * other shape functions. - * - * In composed elements (i.e. for - * the FESystem class, the - * result will be true if all all - * the base elements have defined - * support points. + * The result may be false if an element is not defined by interpolating + * shape functions, for example by P-elements on quadrilaterals. It will + * usually only be true if the element constructs its shape functions by the + * requirement that they be one at a certain point and zero at all the + * points associated with the other shape functions. + * + * In composed elements (i.e. for the FESystem class, the result will be + * true if all all the base elements have defined support points. */ bool has_support_points () const; /** - * Return the position of the - * support point of the - * @p indexth shape function. If - * it does not exist, raise an - * exception. + * Return the position of the support point of the @p indexth shape + * function. If it does not exist, raise an exception. * - * The default implementation - * simply returns the respective - * element from the array you get - * from - * get_unit_support_points(), - * but derived elements may - * overload this function. In - * particular, note that the - * FESystem class overloads - * it so that it can return the - * support points of individual - * base elements, if not all the - * base elements define support - * points. In this way, you can - * still ask for certain support - * points, even if - * get_unit_support_points() - * only returns an empty array. + * The default implementation simply returns the respective element from the + * array you get from get_unit_support_points(), but derived elements may + * overload this function. In particular, note that the FESystem class + * overloads it so that it can return the support points of individual base + * elements, if not all the base elements define support points. In this + * way, you can still ask for certain support points, even if + * get_unit_support_points() only returns an empty array. */ virtual Point unit_support_point (const unsigned int index) const; /** - * Return the support points of - * the trial functions on the - * unit face, if the derived - * finite element defines some. - * Finite elements that allow - * some kind of interpolation - * operation usually have support - * points. On the other hand, - * elements that define their - * degrees of freedom by, for - * example, moments on faces, or - * as derivatives, don't have - * support points. In that case, - * the returned field is empty - * - * Note that elements that have - * support points need not - * necessarily have some on the - * faces, even if the - * interpolation points are - * located physically on a - * face. For example, the - * discontinuous elements have - * interpolation points on the - * vertices, and for higher - * degree elements also on the - * faces, but they are not - * defined to be on faces since - * in that case degrees of - * freedom from both sides of a - * face (or from all adjacent - * elements to a vertex) would be - * identified with each other, - * which is not what we would - * like to have). Logically, - * these degrees of freedom are - * therefore defined to belong to - * the cell, rather than the face - * or vertex. In that case, the - * returned element would - * therefore have length zero. - * - * If the finite element defines - * support points, then their - * number equals the number of - * degrees of freedom on the face - * (#dofs_per_face). The order - * of points in the array matches - * that returned by the - * cell->get_dof_indices - * function. - * - * See the class documentation - * for details on support points. + * Return the support points of the trial functions on the unit face, if the + * derived finite element defines some. Finite elements that allow some + * kind of interpolation operation usually have support points. On the other + * hand, elements that define their degrees of freedom by, for example, + * moments on faces, or as derivatives, don't have support points. In that + * case, the returned field is empty + * + * Note that elements that have support points need not necessarily have + * some on the faces, even if the interpolation points are located + * physically on a face. For example, the discontinuous elements have + * interpolation points on the vertices, and for higher degree elements also + * on the faces, but they are not defined to be on faces since in that case + * degrees of freedom from both sides of a face (or from all adjacent + * elements to a vertex) would be identified with each other, which is not + * what we would like to have). Logically, these degrees of freedom are + * therefore defined to belong to the cell, rather than the face or + * vertex. In that case, the returned element would therefore have length + * zero. + * + * If the finite element defines support points, then their number equals + * the number of degrees of freedom on the face (#dofs_per_face). The order + * of points in the array matches that returned by the + * cell->get_dof_indices function. + * + * See the class documentation for details on support points. */ const std::vector > & get_unit_face_support_points () const; /** - * Return whether a finite - * element has defined support - * points on faces. If the result - * is true, then a call to the - * get_unit_face_support_points() + * Return whether a finite element has defined support points on faces. If + * the result is true, then a call to the get_unit_face_support_points() * yields a non-empty array. * - * For more information, see the - * documentation for the - * has_support_points() + * For more information, see the documentation for the has_support_points() * function. */ bool has_face_support_points () const; /** - * The function corresponding to - * the unit_support_point() - * function, but for faces. See - * there for more information. + * The function corresponding to the unit_support_point() function, but for + * faces. See there for more information. */ virtual Point unit_face_support_point (const unsigned int index) const; /** - * Return a support point vector - * for generalized interpolation. - * - * See the @ref GlossGeneralizedSupport "glossary entry on generalized support points" - * for more information. + * Return a support point vector for generalized interpolation. + * + * See the @ref GlossGeneralizedSupport "glossary entry on generalized + * support points" for more information. */ const std::vector > & get_generalized_support_points () const; /** - * Returns true if the - * class provides nonempty - * vectors either from - * get_unit_support_points() or - * get_generalized_support_points(). - * - * See the @ref GlossGeneralizedSupport "glossary entry on generalized support points" - * for more information. + * Returns true if the class provides nonempty vectors either from + * get_unit_support_points() or get_generalized_support_points(). + * + * See the @ref GlossGeneralizedSupport "glossary entry on generalized + * support points" for more information. */ bool has_generalized_support_points () const; @@ -1922,61 +1372,43 @@ public: get_generalized_face_support_points () const; /** - * Return whether a finite - * element has defined - * generalized support - * points on faces. If the result - * is true, then a call to the - * get_generalized_face_support_points - * yields a non-empty array. + * Return whether a finite element has defined generalized support points on + * faces. If the result is true, then a call to the + * get_generalized_face_support_points yields a non-empty array. * - * For more information, see the - * documentation for the - * has_support_points() + * For more information, see the documentation for the has_support_points() * function. */ bool has_generalized_face_support_points () const; /** - * Interpolate a set of scalar - * values, computed in the - * generalized support points. + * Interpolate a set of scalar values, computed in the generalized support + * points. * - * @note This function is - * implemented in - * FiniteElement for the case - * that the element has support - * points. In this case, the - * resulting coefficients are - * just the values in the suport - * points. All other elements - * must reimplement it. + * @note This function is implemented in FiniteElement for the case that the + * element has support points. In this case, the resulting coefficients are + * just the values in the suport points. All other elements must reimplement + * it. */ virtual void interpolate(std::vector &local_dofs, const std::vector &values) const; /** - * Interpolate a set of vector - * values, computed in the - * generalized support points. + * Interpolate a set of vector values, computed in the generalized support + * points. * - * Since a finite element often - * only interpolates part of a - * vector, offset is - * used to determine the first - * component of the vector to be - * interpolated. Maybe consider - * changing your data structures - * to use the next function. + * Since a finite element often only interpolates part of a vector, + * offset is used to determine the first component of the vector to + * be interpolated. Maybe consider changing your data structures to use the + * next function. */ virtual void interpolate(std::vector &local_dofs, const std::vector > &values, unsigned int offset = 0) const; /** - * Interpolate a set of vector - * values, computed in the - * generalized support points. + * Interpolate a set of vector values, computed in the generalized support + * points. */ virtual void interpolate( std::vector &local_dofs, @@ -1985,15 +1417,12 @@ public: //@} /** - * Determine an estimate for the - * memory consumption (in bytes) - * of this object. + * Determine an estimate for the memory consumption (in bytes) of this + * object. * - * This function is made virtual, - * since finite element objects - * are usually accessed through - * pointers to their base class, - * rather than the class itself. + * This function is made virtual, since finite element objects are usually + * accessed through pointers to their base class, rather than the class + * itself. */ virtual std::size_t memory_consumption () const; /** @@ -2023,29 +1452,24 @@ public: DeclException0 (ExcUnitShapeValuesDoNotExist); /** - * Attempt to access support - * points of a finite element - * which is not Lagrangian. + * Attempt to access support points of a finite element which is not + * Lagrangian. * * @ingroup Exceptions */ DeclException0 (ExcFEHasNoSupportPoints); /** - * Attempt to access embedding - * matrices of a finite element - * which did not implement these - * matrices. + * Attempt to access embedding matrices of a finite element which did not + * implement these matrices. * * @ingroup Exceptions */ DeclException0 (ExcEmbeddingVoid); /** - * Attempt to access restriction - * matrices of a finite element - * which did not implement these - * matrices. + * Attempt to access restriction matrices of a finite element which did not + * implement these matrices. * * Exception * @ingroup Exceptions @@ -2053,10 +1477,8 @@ public: DeclException0 (ExcProjectionVoid); /** - * Attempt to access constraint - * matrices of a finite element - * which did not implement these - * matrices. + * Attempt to access constraint matrices of a finite element which did not + * implement these matrices. * * Exception * @ingroup Exceptions @@ -2102,235 +1524,140 @@ public: protected: /** - * Reinit the vectors of - * restriction and prolongation - * matrices to the right sizes: - * For every refinement case, - * except for - * RefinementCase::no_refinement, - * and for every child of that - * refinement case the space of - * one restriction and - * prolongation matrix is - * allocated, see the - * documentation of the - * restriction and prolongation - * vectors for more detail on the - * actual vector sizes. - * - * @param - * isotropic_restriction_only - * only the restriction matrices - * required for isotropic - * refinement are reinited to the - * right size. - * @param - * isotropic_prolongation_only - * only the prolongation matrices - * required for isotropic - * refinement are reinited to the - * right size. + * Reinit the vectors of restriction and prolongation matrices to the right + * sizes: For every refinement case, except for + * RefinementCase::no_refinement, and for every child of that refinement + * case the space of one restriction and prolongation matrix is allocated, + * see the documentation of the restriction and prolongation vectors for + * more detail on the actual vector sizes. + * + * @param isotropic_restriction_only only the restriction matrices required + * for isotropic refinement are reinited to the right size. + * @param isotropic_prolongation_only only the prolongation matrices + * required for isotropic refinement are reinited to the right size. */ void reinit_restriction_and_prolongation_matrices(const bool isotropic_restriction_only=false, const bool isotropic_prolongation_only=false); /** - * Vector of projection - * matrices. See - * get_restriction_matrix() - * above. The constructor - * initializes these matrices to - * zero dimensions, which can be - * changed by derived classes - * implementing them. - * - * Note, that - * restriction[refinement_case-1][child] - * includes the restriction - * matrix of child - * child for the - * RefinementCase - * refinement_case. Here, - * we use - * refinement_case-1 - * instead of - * refinement_case - * as for - * RefinementCase::no_refinement(=0) - * there are no restriction - * matrices available. + * Vector of projection matrices. See get_restriction_matrix() above. The + * constructor initializes these matrices to zero dimensions, which can be + * changed by derived classes implementing them. + * + * Note, that restriction[refinement_case-1][child] includes + * the restriction matrix of child child for the RefinementCase + * refinement_case. Here, we use refinement_case-1 + * instead of refinement_case as for + * RefinementCase::no_refinement(=0) there are no restriction matrices + * available. */ std::vector > > restriction; /** - * Vector of embedding - * matrices. See - * get_prolongation_matrix() - * above. The constructor - * initializes these matrices to - * zero dimensions, which can be - * changed by derived classes - * implementing them. - * - * Note, that - * prolongation[refinement_case-1][child] - * includes the prolongation - * matrix of child - * child for the - * RefinementCase - * refinement_case. Here, - * we use - * refinement_case-1 - * instead of - * refinement_case - * as for - * RefinementCase::no_refinement(=0) - * there are no prolongation - * matrices available. + * Vector of embedding matrices. See get_prolongation_matrix() + * above. The constructor initializes these matrices to zero dimensions, + * which can be changed by derived classes implementing them. + * + * Note, that prolongation[refinement_case-1][child] includes + * the prolongation matrix of child child for the + * RefinementCase refinement_case. Here, we use + * refinement_case-1 instead of refinement_case as + * for RefinementCase::no_refinement(=0) there are no prolongation matrices + * available. */ std::vector > > prolongation; /** - * Specify the constraints which - * the dofs on the two sides of a - * cell interface underly if the - * line connects two cells of - * which one is refined once. + * Specify the constraints which the dofs on the two sides of a cell + * interface underly if the line connects two cells of which one is refined + * once. * - * For further details see the - * general description of the - * derived class. + * For further details see the general description of the derived class. * - * This field is obviously - * useless in one dimension - * and has there a zero size. + * This field is obviously useless in one dimension and has there a zero + * size. */ FullMatrix interface_constraints; /** - * List of support points on the - * unit cell, in case the finite - * element has any. The - * constructor leaves this field - * empty, derived classes may - * write in some contents. - * - * Finite elements that allow - * some kind of interpolation - * operation usually have support - * points. On the other hand, - * elements that define their - * degrees of freedom by, for - * example, moments on faces, or - * as derivatives, don't have - * support points. In that case, - * this field remains empty. + * List of support points on the unit cell, in case the finite element has + * any. The constructor leaves this field empty, derived classes may write + * in some contents. + * + * Finite elements that allow some kind of interpolation operation usually + * have support points. On the other hand, elements that define their + * degrees of freedom by, for example, moments on faces, or as derivatives, + * don't have support points. In that case, this field remains empty. */ std::vector > unit_support_points; /** - * Same for the faces. See the - * description of the - * get_unit_face_support_points() - * function for a discussion of - * what contributes a face - * support point. + * Same for the faces. See the description of the + * get_unit_face_support_points() function for a discussion of what + * contributes a face support point. */ std::vector > unit_face_support_points; /** - * Support points used for - * interpolation functions of - * non-Lagrangian elements. + * Support points used for interpolation functions of non-Lagrangian + * elements. */ std::vector > generalized_support_points; /** - * Face support points used for - * interpolation functions of - * non-Lagrangian elements. + * Face support points used for interpolation functions of non-Lagrangian + * elements. */ std::vector > generalized_face_support_points; /** - * For faces with non-standard - * face_orientation in 3D, the dofs on - * faces (quads) have to be permuted in - * order to be combined with the correct - * shape functions. Given a local dof @p - * index on a quad, return the shift in the - * local index, if the face has - * non-standard face_orientation, - * i.e. old_index + shift = - * new_index. In 2D and 1D there is - * no need for permutation so the vector is - * empty. In 3D it has the size of - * #dofs_per_quad * 8 , where 8 is - * the number of orientations, a face can - * be in (all combinations of the three - * bool flags face_orientation, face_flip - * and face_rotation). - * - * The standard implementation fills this - * with zeros, i.e. no permuatation at - * all. Derived finite element classes have - * to fill this Table with the correct - * values. + * For faces with non-standard face_orientation in 3D, the dofs on faces + * (quads) have to be permuted in order to be combined with the correct + * shape functions. Given a local dof @p index on a quad, return the shift + * in the local index, if the face has non-standard face_orientation, + * i.e. old_index + shift = new_index. In 2D and 1D there is no + * need for permutation so the vector is empty. In 3D it has the size of + * #dofs_per_quad * 8 , where 8 is the number of orientations, + * a face can be in (all combinations of the three bool flags + * face_orientation, face_flip and face_rotation). + * + * The standard implementation fills this with zeros, i.e. no permuatation + * at all. Derived finite element classes have to fill this Table with the + * correct values. */ Table<2,int> adjust_quad_dof_index_for_face_orientation_table; /** - * For lines with non-standard - * line_orientation in 3D, the dofs on - * lines have to be permuted in - * order to be combined with the correct - * shape functions. Given a local dof @p - * index on a line, return the shift in the - * local index, if the line has - * non-standard line_orientation, - * i.e. old_index + shift = - * new_index. In 2D and 1D there is - * no need for permutation so the vector is - * empty. In 3D it has the size of + * For lines with non-standard line_orientation in 3D, the dofs on lines + * have to be permuted in order to be combined with the correct shape + * functions. Given a local dof @p index on a line, return the shift in the + * local index, if the line has non-standard line_orientation, + * i.e. old_index + shift = new_index. In 2D and 1D there is no + * need for permutation so the vector is empty. In 3D it has the size of * #dofs_per_line. * - * The standard implementation fills this - * with zeros, i.e. no permutation at - * all. Derived finite element classes have - * to fill this vector with the correct - * values. + * The standard implementation fills this with zeros, i.e. no permutation at + * all. Derived finite element classes have to fill this vector with the + * correct values. */ std::vector adjust_line_dof_index_for_line_orientation_table; /** - * Return the size of interface - * constraint matrices. Since - * this is needed in every - * derived finite element class - * when initializing their size, - * it is placed into this - * function, to avoid having to - * recompute the - * dimension-dependent size of - * these matrices each time. - * - * Note that some elements do not - * implement the interface - * constraints for certain - * polynomial degrees. In this - * case, this function still - * returns the size these - * matrices should have when - * implemented, but the actual - * matrices are empty. + * Return the size of interface constraint matrices. Since this is needed in + * every derived finite element class when initializing their size, it is + * placed into this function, to avoid having to recompute the + * dimension-dependent size of these matrices each time. + * + * Note that some elements do not implement the interface constraints for + * certain polynomial degrees. In this case, this function still returns the + * size these matrices should have when implemented, but the actual matrices + * are empty. */ TableIndices<2> interface_constraints_size () const; /** - * Compute second derivatives by - * finite differences of - * gradients. + * Compute second derivatives by finite differences of gradients. */ void compute_2nd (const Mapping &mapping, const typename Triangulation::cell_iterator &cell, @@ -2340,174 +1667,107 @@ protected: FEValuesData &data) const; /** - * Given the pattern of nonzero - * components for each shape - * function, compute for each - * entry how many components are - * non-zero for each shape - * function. This function is - * used in the constructor of - * this class. + * Given the pattern of nonzero components for each shape function, compute + * for each entry how many components are non-zero for each shape + * function. This function is used in the constructor of this class. */ static std::vector compute_n_nonzero_components (const std::vector &nonzero_components); /** - * Determine the values a finite - * element should compute on - * initialization of data for - * FEValues. - * - * Given a set of flags - * indicating what quantities are - * requested from a FEValues - * object, update_once() and - * update_each() compute which - * values must really be - * computed. Then, the - * fill_*_values functions - * are called with the result of - * these. - * - * Furthermore, values must be - * computed either on the unit - * cell or on the physical - * cell. For instance, the - * function values of FE_Q do - * only depend on the quadrature - * points on the unit - * cell. Therefore, this flags - * will be returned by - * update_once(). The gradients - * require computation of the - * covariant transformation - * matrix. Therefore, - * @p update_covariant_transformation - * and @p update_gradients will - * be returned by - * update_each(). - * - * For an example see the same - * function in the derived class - * FE_Q. + * Determine the values a finite element should compute on initialization of + * data for FEValues. + * + * Given a set of flags indicating what quantities are requested from a + * FEValues object, update_once() and update_each() compute which values + * must really be computed. Then, the fill_*_values functions are + * called with the result of these. + * + * Furthermore, values must be computed either on the unit cell or on the + * physical cell. For instance, the function values of FE_Q do only depend + * on the quadrature points on the unit cell. Therefore, this flags will be + * returned by update_once(). The gradients require computation of the + * covariant transformation matrix. Therefore, @p + * update_covariant_transformation and @p update_gradients will be returned + * by update_each(). + * + * For an example see the same function in the derived class FE_Q. */ virtual UpdateFlags update_once (const UpdateFlags flags) const = 0; /** - * Complementary function for - * update_once(). + * Complementary function for update_once(). * - * While update_once() returns - * the values to be computed on - * the unit cell for yielding the - * required data, this function - * determines the values that - * must be recomputed on each - * cell. + * While update_once() returns the values to be computed on the unit cell + * for yielding the required data, this function determines the values that + * must be recomputed on each cell. * - * Refer to update_once() for - * more details. + * Refer to update_once() for more details. */ virtual UpdateFlags update_each (const UpdateFlags flags) const = 0; /** - * A sort of virtual copy - * constructor. Some places in - * the library, for example the - * constructors of FESystem as - * well as the hp::FECollection - * class, need to make copies of - * finite elements without - * knowing their exact type. They - * do so through this function. + * A sort of virtual copy constructor. Some places in the library, for + * example the constructors of FESystem as well as the hp::FECollection + * class, need to make copies of finite elements without knowing their exact + * type. They do so through this function. */ virtual FiniteElement *clone() const = 0; private: /** - * Store what - * system_to_component_index() - * will return. + * Store what system_to_component_index() will return. */ std::vector< std::pair > system_to_component_table; /** - * Map between linear dofs and - * component dofs on face. This - * is filled with default values - * in the constructor, but - * derived classes will have to - * overwrite the information if - * necessary. + * Map between linear dofs and component dofs on face. This is filled with + * default values in the constructor, but derived classes will have to + * overwrite the information if necessary. * - * By component, we mean the - * vector component, not the base - * element. The information thus - * makes only sense if a shape - * function is non-zero in only + * By component, we mean the vector component, not the base element. The + * information thus makes only sense if a shape function is non-zero in only * one component. */ std::vector< std::pair > face_system_to_component_table; /** - * For each shape function, store - * to which base element and - * which instance of this base - * element (in case its - * multiplicity is greater than - * one) it belongs, and its index - * within this base element. If - * the element is not composed of - * others, then base and instance - * are always zero, and the index - * is equal to the number of the - * shape function. If the element - * is composed of single - * instances of other elements - * (i.e. all with multiplicity - * one) all of which are scalar, - * then base values and dof - * indices within this element - * are equal to the - * #system_to_component_table. It - * differs only in case the - * element is composed of other - * elements and at least one of - * them is vector-valued itself. - * - * This array has valid values - * also in the case of - * vector-valued - * (i.e. non-primitive) shape - * functions, in contrast to the + * For each shape function, store to which base element and which instance + * of this base element (in case its multiplicity is greater than one) it + * belongs, and its index within this base element. If the element is not + * composed of others, then base and instance are always zero, and the index + * is equal to the number of the shape function. If the element is composed + * of single instances of other elements (i.e. all with multiplicity one) + * all of which are scalar, then base values and dof indices within this + * element are equal to the #system_to_component_table. It differs only in + * case the element is composed of other elements and at least one of them + * is vector-valued itself. + * + * This array has valid values also in the case of vector-valued + * (i.e. non-primitive) shape functions, in contrast to the * #system_to_component_table. */ std::vector,unsigned int> > system_to_base_table; /** - * Likewise for the indices on - * faces. + * Likewise for the indices on faces. */ std::vector,unsigned int> > face_system_to_base_table; /** - * For each base element, store - * the number of blocks generated - * by the base and the first block in a block - * vector it will generate. + * For each base element, store the number of blocks generated by the base + * and the first block in a block vector it will generate. */ BlockIndices base_to_block_indices; /** - * The base element establishing - * a component. + * The base element establishing a component. * - * For each component number - * c, the entries have - * the following meaning: + * For each component number c, the entries have the following + * meaning: *
    *
    table[c].first.first
    *
    Number of the base element for c.
    @@ -2517,147 +1777,82 @@ private: *
    Multiple of the base element for c.
    *
    * - * This variable is set to the - * correct size by the - * constructor of this class, but - * needs to be initialized by - * derived classes, unless its - * size is one and the only entry - * is a zero, which is the case - * for scalar elements. In that - * case, the initialization by - * the base class is sufficient. + * This variable is set to the correct size by the constructor of this + * class, but needs to be initialized by derived classes, unless its size is + * one and the only entry is a zero, which is the case for scalar + * elements. In that case, the initialization by the base class is + * sufficient. */ std::vector, unsigned int> > component_to_base_table; /** - * Projection matrices are - * concatenated or summed up. - * - * This flags decides on how the - * projection matrices of the - * children of the same father - * are put together to one - * operator. The possible modes - * are concatenation and - * summation. - * - * If the projection is defined - * by an interpolation operator, - * the child matrices are - * concatenated, i.e. values - * belonging to the same node - * functional are identified and - * enter the interpolated value - * only once. In this case, the - * flag must be @p false. - * - * For projections with respect - * to scalar products, the child - * matrices must be summed up to - * build the complete matrix. The - * flag should be @p true. - * - * For examples of use of these - * flags, see the places in the - * library where it is queried. - * - * There is one flag per shape - * function, indicating whether - * it belongs to the class of - * shape functions that are - * additive in the restriction or - * not. - * - * Note that in previous versions - * of the library, there was one - * flag per vector component of - * the element. This is based on - * the fact that all the shape - * functions that belong to the - * same vector component must - * necessarily behave in the same - * way, to make things - * reasonable. However, the - * problem is that it is - * sometimes impossible to query - * this flag in the vector-valued - * case: this used to be done - * with the - * #system_to_component_index - * function that returns which - * vector component a shape - * function is associated - * with. The point is that since - * we now support shape functions - * that are associated with more - * than one vector component (for - * example the shape functions of - * Raviart-Thomas, or Nedelec - * elements), that function can - * no more be used, so it can be - * difficult to find out which - * for vector component we would - * like to query the + * Projection matrices are concatenated or summed up. + * + * This flags decides on how the projection matrices of the children of the + * same father are put together to one operator. The possible modes are + * concatenation and summation. + * + * If the projection is defined by an interpolation operator, the child + * matrices are concatenated, i.e. values belonging to the same node + * functional are identified and enter the interpolated value only once. In + * this case, the flag must be @p false. + * + * For projections with respect to scalar products, the child matrices must + * be summed up to build the complete matrix. The flag should be @p true. + * + * For examples of use of these flags, see the places in the library where + * it is queried. + * + * There is one flag per shape function, indicating whether it belongs to + * the class of shape functions that are additive in the restriction or not. + * + * Note that in previous versions of the library, there was one flag per + * vector component of the element. This is based on the fact that all the + * shape functions that belong to the same vector component must necessarily + * behave in the same way, to make things reasonable. However, the problem + * is that it is sometimes impossible to query this flag in the + * vector-valued case: this used to be done with the + * #system_to_component_index function that returns which vector component a + * shape function is associated with. The point is that since we now support + * shape functions that are associated with more than one vector component + * (for example the shape functions of Raviart-Thomas, or Nedelec elements), + * that function can no more be used, so it can be difficult to find out + * which for vector component we would like to query the * restriction-is-additive flags. */ const std::vector restriction_is_additive_flags; /** - * For each shape function, give - * a vector of bools (with size - * equal to the number of vector - * components which this finite - * element has) indicating in - * which component each of these - * shape functions is non-zero. + * For each shape function, give a vector of bools (with size equal to the + * number of vector components which this finite element has) indicating in + * which component each of these shape functions is non-zero. * - * For primitive elements, there - * is only one non-zero - * component. + * For primitive elements, there is only one non-zero component. */ const std::vector nonzero_components; /** - * This array holds how many - * values in the respective entry - * of the #nonzero_components - * element are non-zero. The - * array is thus a short-cut to - * allow faster access to this - * information than if we had to - * count the non-zero entries - * upon each request for this - * information. The field is - * initialized in the constructor - * of this class. + * This array holds how many values in the respective entry of the + * #nonzero_components element are non-zero. The array is thus a short-cut + * to allow faster access to this information than if we had to count the + * non-zero entries upon each request for this information. The field is + * initialized in the constructor of this class. */ const std::vector n_nonzero_components_table; /** - * Second derivatives of shapes - * functions are not computed - * analytically, but by finite - * differences of the - * gradients. This static - * variable denotes the step - * length to be used for - * that. It's value is set to - * 1e-6. + * Second derivatives of shapes functions are not computed analytically, but + * by finite differences of the gradients. This static variable denotes the + * step length to be used for that. It's value is set to 1e-6. */ static const double fd_step_length; /** - * Prepare internal data - * structures and fill in values - * independent of the - * cell. Returns a pointer to an - * object of which the caller of - * this function then has to - * assume ownership (which - * includes destruction when it - * is no more needed). + * Prepare internal data structures and fill in values independent of the + * cell. Returns a pointer to an object of which the caller of this function + * then has to assume ownership (which includes destruction when it is no + * more needed). */ virtual typename Mapping::InternalDataBase * get_data (const UpdateFlags flags, @@ -2665,16 +1860,10 @@ private: const Quadrature &quadrature) const = 0; /** - * Prepare internal data - * structure for transformation - * of faces and fill in values - * independent of the - * cell. Returns a pointer to an - * object of which the caller of - * this function then has to - * assume ownership (which - * includes destruction when it - * is no more needed). + * Prepare internal data structure for transformation of faces and fill in + * values independent of the cell. Returns a pointer to an object of which + * the caller of this function then has to assume ownership (which includes + * destruction when it is no more needed). */ virtual typename Mapping::InternalDataBase * get_face_data (const UpdateFlags flags, @@ -2682,16 +1871,10 @@ private: const Quadrature &quadrature) const; /** - * Prepare internal data - * structure for transformation - * of children of faces and fill - * in values independent of the - * cell. Returns a pointer to an - * object of which the caller of - * this function then has to - * assume ownership (which - * includes destruction when it - * is no more needed). + * Prepare internal data structure for transformation of children of faces + * and fill in values independent of the cell. Returns a pointer to an + * object of which the caller of this function then has to assume ownership + * (which includes destruction when it is no more needed). */ virtual typename Mapping::InternalDataBase * get_subface_data (const UpdateFlags flags, @@ -2699,15 +1882,11 @@ private: const Quadrature &quadrature) const; /** - * Fill the fields of - * FEValues. This function - * performs all the operations - * needed to compute the data of an - * FEValues object. + * Fill the fields of FEValues. This function performs all the operations + * needed to compute the data of an FEValues object. * - * The same function in - * @p mapping must have been - * called for the same cell first! + * The same function in @p mapping must have been called for the same cell + * first! */ virtual void fill_fe_values (const Mapping &mapping, @@ -2719,15 +1898,11 @@ private: CellSimilarity::Similarity &cell_similarity) const = 0; /** - * Fill the fields of - * FEFaceValues. This function - * performs all the operations - * needed to compute the data of an - * FEFaceValues object. + * Fill the fields of FEFaceValues. This function performs all the + * operations needed to compute the data of an FEFaceValues object. * - * The same function in - * @p mapping must have been - * called for the same cell first! + * The same function in @p mapping must have been called for the same cell + * first! */ virtual void fill_fe_face_values (const Mapping &mapping, @@ -2739,15 +1914,11 @@ private: FEValuesData &data) const = 0; /** - * Fill the fields of - * FESubfaceValues. This function - * performs all the operations - * needed to compute the data of an - * FESubfaceValues object. + * Fill the fields of FESubfaceValues. This function performs all the + * operations needed to compute the data of an FESubfaceValues object. * - * The same function in - * @p mapping must have been - * called for the same cell first! + * The same function in @p mapping must have been called for the same cell + * first! */ virtual void fill_fe_subface_values (const Mapping &mapping, diff --git a/deal.II/include/deal.II/fe/fe_face.h b/deal.II/include/deal.II/fe/fe_face.h index 8747b6d4f9..4fdddd2e07 100644 --- a/deal.II/include/deal.II/fe/fe_face.h +++ b/deal.II/include/deal.II/fe/fe_face.h @@ -47,49 +47,35 @@ class FE_FaceQ : public FE_PolyFace, dim, spaced { public: /** - * Constructor for tensor product - * polynomials of degree - * p. The shape - * functions created using this - * constructor correspond to - * Legendre polynomials in each - * coordinate direction. + * Constructor for tensor product polynomials of degree p. The + * shape functions created using this constructor correspond to Lagrange + * polynomials in each coordinate direction. */ FE_FaceQ(unsigned int p); virtual FiniteElement *clone() const; /** - * Return a string that uniquely - * identifies a finite - * element. This class returns - * FE_FaceQ(degree), with - * dim and degree - * replaced by appropriate - * values. + * Return a string that uniquely identifies a finite element. This class + * returns FE_FaceQ(degree), with dim and + * degree replaced by appropriate values. */ virtual std::string get_name () const; /** * Check for non-zero values on a face. * - * This function returns - * @p true, if the shape - * function @p shape_index has - * non-zero values on the face - * @p face_index. + * This function returns @p true, if the shape function @p shape_index has + * non-zero values on the face @p face_index. * - * Implementation of the - * interface in - * FiniteElement + * Implementation of the interface in FiniteElement */ virtual bool has_support_on_face (const unsigned int shape_index, const unsigned int face_index) const; private: /** - * Return vector with dofs per - * vertex, line, quad, hex. + * Return vector with dofs per vertex, line, quad, hex. */ static std::vector get_dpo_vector (const unsigned int deg); }; diff --git a/deal.II/include/deal.II/fe/fe_poly.h b/deal.II/include/deal.II/fe/fe_poly.h index ba8ccd7297..1ff0992513 100644 --- a/deal.II/include/deal.II/fe/fe_poly.h +++ b/deal.II/include/deal.II/fe/fe_poly.h @@ -74,122 +74,85 @@ public: const std::vector &nonzero_components); /** - * Return the polynomial degree - * of this finite element, - * i.e. the value passed to the - * constructor. + * Return the polynomial degree of this finite element, i.e. the value + * passed to the constructor. */ unsigned int get_degree () const; /** - * Return the numbering of the underlying - * polynomial space compared to - * lexicographic ordering of the basis - * functions. Returns + * Return the numbering of the underlying polynomial space compared to + * lexicographic ordering of the basis functions. Returns * POLY::get_numbering(). */ std::vector get_poly_space_numbering() const; /** - * Return the inverse numbering of the - * underlying polynomial space. Returns + * Return the inverse numbering of the underlying polynomial space. Returns * POLY::get_numbering_inverse(). */ std::vector get_poly_space_numbering_inverse() const; /** - * Return the value of the - * ith shape function at - * the point p. See the - * FiniteElement base class - * for more information about the - * semantics of this function. + * Return the value of the ith shape function at the point + * p. See the FiniteElement base class for more information about + * the semantics of this function. */ virtual double shape_value (const unsigned int i, const Point &p) const; /** - * Return the value of the - * componentth vector - * component of the ith - * shape function at the point - * p. See the - * FiniteElement base class - * for more information about the - * semantics of this function. + * Return the value of the componentth vector component of the + * ith shape function at the point p. See the + * FiniteElement base class for more information about the semantics of this + * function. * - * Since this element is scalar, - * the returned value is the same - * as if the function without the - * _component suffix - * were called, provided that the - * specified component is zero. + * Since this element is scalar, the returned value is the same as if the + * function without the _component suffix were called, provided + * that the specified component is zero. */ virtual double shape_value_component (const unsigned int i, const Point &p, const unsigned int component) const; /** - * Return the gradient of the - * ith shape function at - * the point p. See the - * FiniteElement base class - * for more information about the - * semantics of this function. + * Return the gradient of the ith shape function at the point + * p. See the FiniteElement base class for more information about + * the semantics of this function. */ virtual Tensor<1,dim> shape_grad (const unsigned int i, const Point &p) const; /** - * Return the gradient of the - * componentth vector - * component of the ith - * shape function at the point - * p. See the - * FiniteElement base class - * for more information about the - * semantics of this function. + * Return the gradient of the componentth vector component of the + * ith shape function at the point p. See the + * FiniteElement base class for more information about the semantics of this + * function. * - * Since this element is scalar, - * the returned value is the same - * as if the function without the - * _component suffix - * were called, provided that the - * specified component is zero. + * Since this element is scalar, the returned value is the same as if the + * function without the _component suffix were called, provided + * that the specified component is zero. */ virtual Tensor<1,dim> shape_grad_component (const unsigned int i, const Point &p, const unsigned int component) const; /** - * Return the tensor of second - * derivatives of the - * ith shape function at - * point p on the unit - * cell. See the - * FiniteElement base class - * for more information about the - * semantics of this function. + * Return the tensor of second derivatives of the ith shape + * function at point p on the unit cell. See the FiniteElement base + * class for more information about the semantics of this function. */ virtual Tensor<2,dim> shape_grad_grad (const unsigned int i, const Point &p) const; /** - * Return the second derivative - * of the componentth - * vector component of the - * ith shape function at - * the point p. See the - * FiniteElement base class - * for more information about the - * semantics of this function. + * Return the second derivative of the componentth vector component + * of the ith shape function at the point p. See the + * FiniteElement base class for more information about the semantics of this + * function. * - * Since this element is scalar, - * the returned value is the same - * as if the function without the - * _component suffix - * were called, provided that the - * specified component is zero. + * Since this element is scalar, the returned value is the same as if the + * function without the _component suffix were called, provided + * that the specified component is zero. */ virtual Tensor<2,dim> shape_grad_grad_component (const unsigned int i, const Point &p, @@ -232,84 +195,46 @@ protected: /** - * Determine the values that need - * to be computed on the unit - * cell to be able to compute all - * values required by - * flags. + * Determine the values that need to be computed on the unit cell to be able + * to compute all values required by flags. * - * For the purpuse of this - * function, refer to the - * documentation in + * For the purpuse of this function, refer to the documentation in * FiniteElement. * - * This class assumes that shape - * functions of this - * FiniteElement do not - * depend on the actual shape of - * the cells in real - * space. Therefore, the effect - * in this element is as follows: - * if update_values is - * set in flags, copy it - * to the result. All other flags - * of the result are cleared, - * since everything else must be - * computed for each cell. + * This class assumes that shape functions of this FiniteElement do + * not depend on the actual shape of the cells in real + * space. Therefore, the effect in this element is as follows: if + * update_values is set in flags, copy it to the + * result. All other flags of the result are cleared, since everything else + * must be computed for each cell. */ virtual UpdateFlags update_once (const UpdateFlags flags) const; /** - * Determine the values that need - * to be computed on every cell - * to be able to compute all - * values required by - * flags. + * Determine the values that need to be computed on every cell to be able to + * compute all values required by flags. * - * For the purpuse of this - * function, refer to the - * documentation in + * For the purpuse of this function, refer to the documentation in * FiniteElement. * - * This class assumes that shape - * functions of this - * FiniteElement do not - * depend on the actual shape of - * the cells in real - * space. + * This class assumes that shape functions of this FiniteElement do + * not depend on the actual shape of the cells in real space. * - * The effect in this element is - * as follows: + * The effect in this element is as follows: *
      - - *
    • if - * update_gradients is - * set, the result will contain - * update_gradients and - * update_covariant_transformation. - * The latter is required to - * transform the gradient on the - * unit cell to the real - * cell. Remark, that the action - * required by - * update_covariant_transformation - * is actually performed by the - * Mapping object used in - * conjunction with this finite - * element.
    • if - * update_hessians - * is set, the result will - * contain - * update_hessians - * and - * update_covariant_transformation. - * The rationale is the same as - * above and no higher - * derivatives of the - * transformation are required, - * since we use difference - * quotients for the actual - * computation. + * + *
    • if update_gradients is set, the result will contain + * update_gradients and update_covariant_transformation. + * The latter is required to transform the gradient on the unit cell to the + * real cell. Remark, that the action required by + * update_covariant_transformation is actually performed by the + * Mapping object used in conjunction with this finite element. + * + *
    • if update_hessians is set, the result will contain + * update_hessians and update_covariant_transformation. + * The rationale is the same as above and no higher derivatives of the + * transformation are required, since we use difference quotients for the + * actual computation. * *
    */ @@ -319,61 +244,37 @@ protected: /** * Fields of cell-independent data. * - * For information about the - * general purpose of this class, - * see the documentation of the - * base class. + * For information about the general purpose of this class, see the + * documentation of the base class. */ class InternalData : public FiniteElement::InternalDataBase { public: /** - * Array with shape function - * values in quadrature - * points. There is one - * row for each shape - * function, containing - * values for each quadrature - * point. + * Array with shape function values in quadrature points. There is one row + * for each shape function, containing values for each quadrature point. * - * In this array, we store - * the values of the shape - * function in the quadrature - * points on the unit - * cell. Since these values - * do not change under - * transformation to the real - * cell, we only need to copy - * them over when visiting a - * concrete cell. + * In this array, we store the values of the shape function in the + * quadrature points on the unit cell. Since these values do not change + * under transformation to the real cell, we only need to copy them over + * when visiting a concrete cell. */ std::vector > shape_values; /** - * Array with shape function - * gradients in quadrature - * points. There is one - * row for each shape - * function, containing - * values for each quadrature + * Array with shape function gradients in quadrature points. There is one + * row for each shape function, containing values for each quadrature * point. * - * We store the gradients in - * the quadrature points on - * the unit cell. We then - * only have to apply the - * transformation (which is a - * matrix-vector - * multiplication) when - * visiting an actual cell. + * We store the gradients in the quadrature points on the unit cell. We + * then only have to apply the transformation (which is a matrix-vector + * multiplication) when visiting an actual cell. */ std::vector > > shape_gradients; }; /** - * The polynomial space. Its type - * is given by the template - * parameter POLY. + * The polynomial space. Its type is given by the template parameter POLY. */ POLY poly_space; }; diff --git a/deal.II/include/deal.II/fe/fe_poly.templates.h b/deal.II/include/deal.II/fe/fe_poly.templates.h index 99f49d7a02..f5ba25953e 100644 --- a/deal.II/include/deal.II/fe/fe_poly.templates.h +++ b/deal.II/include/deal.II/fe/fe_poly.templates.h @@ -460,18 +460,18 @@ namespace internal return std::vector(); } - template + template inline std::vector - get_poly_space_numbering (const TensorProductPolynomials &poly) + get_poly_space_numbering (const TensorProductPolynomials &poly) { return poly.get_numbering(); } - template + template inline std::vector - get_poly_space_numbering_inverse (const TensorProductPolynomials &poly) + get_poly_space_numbering_inverse (const TensorProductPolynomials &poly) { return poly.get_numbering_inverse(); } diff --git a/deal.II/include/deal.II/fe/fe_q.h b/deal.II/include/deal.II/fe/fe_q.h index e17e1e4e07..294daad459 100644 --- a/deal.II/include/deal.II/fe/fe_q.h +++ b/deal.II/include/deal.II/fe/fe_q.h @@ -14,7 +14,7 @@ #include #include -#include +#include DEAL_II_NAMESPACE_OPEN @@ -220,386 +220,55 @@ DEAL_II_NAMESPACE_OPEN * @author Wolfgang Bangerth, 1998, 2003; Guido Kanschat, 2001; Ralf Hartmann, 2001, 2004, 2005; Oliver Kayser-Herold, 2004; Katharina Kormann, 2008; Martin Kronbichler, 2008 */ template -class FE_Q : public FE_Poly,dim,spacedim> +class FE_Q : public FE_Q_Base,dim,spacedim> { public: /** - * Constructor for tensor product - * polynomials of degree @p p. + * Constructor for tensor product polynomials of degree @p p. */ FE_Q (const unsigned int p); /** - * Constructor for tensor product - * polynomials with support - * points @p points based on a - * one-dimensional quadrature - * formula. The degree of the - * finite element is - * points.size()-1. - * Note that the first point has - * to be 0 and the last one 1. If - * FE_Q(QGaussLobatto<1>(fe_degree+1)) - * is specified, so-called - * Gauss-Lobatto elements are - * obtained which can give a - * diagonal mass matrix if - * combined with Gauss-Lobatto - * quadrature on the same - * points. Their use is shown in - * step-48. + * Constructor for tensor product polynomials with support points @p points + * based on a one-dimensional quadrature formula. The degree of the finite + * element is points.size()-1. Note that the first point has to be + * 0 and the last one 1. If + * FE_Q(QGaussLobatto<1>(fe_degree+1)) is specified, so-called + * Gauss-Lobatto elements are obtained which can give a diagonal mass matrix + * if combined with Gauss-Lobatto quadrature on the same points. Their use + * is shown in step-48. */ FE_Q (const Quadrature<1> &points); /** - * Return a string that uniquely - * identifies a finite - * element. This class returns - * FE_Q(degree), with - * @p dim and @p degree - * replaced by appropriate - * values. + * Constructs a FE_Q_isoQ1 element. That element shares large parts of code + * with FE_Q so most of the construction work is done in this routine, + * whereas the public constructor is in the class FE_Q_isoQ1. */ - virtual std::string get_name () const; - - /** - * Return the matrix - * interpolating from the given - * finite element to the present - * one. The size of the matrix is - * then @p dofs_per_cell times - * source.dofs_per_cell. - * - * These matrices are only - * available if the source - * element is also a @p FE_Q - * element. Otherwise, an - * exception of type - * FiniteElement::ExcInterpolationNotImplemented - * is thrown. - */ - virtual void - get_interpolation_matrix (const FiniteElement &source, - FullMatrix &matrix) const; - - - /** - * Return the matrix - * interpolating from a face of - * of one element to the face of - * the neighboring element. - * The size of the matrix is - * then source.dofs_per_face times - * this->dofs_per_face. - * - * Derived elements will have to - * implement this function. They - * may only provide interpolation - * matrices for certain source - * finite elements, for example - * those from the same family. If - * they don't implement - * interpolation from a given - * element, then they must throw - * an exception of type - * FiniteElement::ExcInterpolationNotImplemented. - */ - virtual void - get_face_interpolation_matrix (const FiniteElement &source, - FullMatrix &matrix) const; - - /** - * Return the matrix - * interpolating from a face of - * of one element to the face of - * the neighboring element. - * The size of the matrix is - * then source.dofs_per_face times - * this->dofs_per_face. - * - * Derived elements will have to - * implement this function. They - * may only provide interpolation - * matrices for certain source - * finite elements, for example - * those from the same family. If - * they don't implement - * interpolation from a given - * element, then they must throw - * an exception of type - * FiniteElement::ExcInterpolationNotImplemented. - */ - virtual void - get_subface_interpolation_matrix (const FiniteElement &source, - const unsigned int subface, - FullMatrix &matrix) const; - - /** - * Check for non-zero values on a face. - * - * This function returns - * @p true, if the shape - * function @p shape_index has - * non-zero values on the face - * @p face_index. - * - * Implementation of the - * interface in - * FiniteElement - */ - virtual bool has_support_on_face (const unsigned int shape_index, - const unsigned int face_index) const; - - /** - * @name Functions to support hp - * @{ - */ - - /** - * Return whether this element - * implements its hanging node - * constraints in the new way, - * which has to be used to make - * elements "hp compatible". - * - * For the FE_Q class the result is - * always true (independent of the degree - * of the element), as it implements the - * complete set of functions necessary - * for hp capability. - */ - virtual bool hp_constraints_are_implemented () const; - - /** - * If, on a vertex, several finite elements are active, the hp code - * first assigns the degrees of freedom of each of these FEs - * different global indices. It then calls this function to find out - * which of them should get identical values, and consequently can - * receive the same global DoF index. This function therefore - * returns a list of identities between DoFs of the present finite - * element object with the DoFs of @p fe_other, which is a reference - * to a finite element object representing one of the other finite - * elements active on this particular vertex. The function computes - * which of the degrees of freedom of the two finite element objects - * are equivalent, both numbered between zero and the corresponding - * value of dofs_per_vertex of the two finite elements. The first - * index of each pair denotes one of the vertex dofs of the present - * element, whereas the second is the corresponding index of the - * other finite element. - */ - virtual - std::vector > - hp_vertex_dof_identities (const FiniteElement &fe_other) const; + FE_Q(const unsigned int subdivisions_per_dimension, + const unsigned int base_degree); /** - * Same as hp_vertex_dof_indices(), except that the function treats - * degrees of freedom on lines. + * Return a string that uniquely identifies a finite element. This class + * returns FE_Q(degree), with @p dim and @p degree replaced by + * appropriate values. */ - virtual - std::vector > - hp_line_dof_identities (const FiniteElement &fe_other) const; - - /** - * Same as hp_vertex_dof_indices(), except that the function treats - * degrees of freedom on quads. - */ - virtual - std::vector > - hp_quad_dof_identities (const FiniteElement &fe_other) const; - - /** - * Return whether this element dominates - * the one given as argument when they - * meet at a common face, - * whether it is the other way around, - * whether neither dominates, or if - * either could dominate. - * - * For a definition of domination, see - * FiniteElementBase::Domination and in - * particular the @ref hp_paper "hp paper". - */ - virtual - FiniteElementDomination::Domination - compare_for_face_domination (const FiniteElement &fe_other) const; - //@} - - /** - * Determine an estimate for the - * memory consumption (in bytes) - * of this object. - * - * This function is made virtual, - * since finite element objects - * are usually accessed through - * pointers to their base class, - * rather than the class itself. - */ - virtual std::size_t memory_consumption () const; + virtual std::string get_name () const; protected: - /** - * @p clone function instead of - * a copy constructor. - * - * This function is needed by the - * constructors of @p FESystem. - */ - virtual FiniteElement *clone() const; - -private: /** - * Only for internal use. Its - * full name is - * @p get_dofs_per_object_vector - * function and it creates the - * @p dofs_per_object vector that is - * needed within the constructor to - * be passed to the constructor of - * @p FiniteElementData. - */ - static std::vector get_dpo_vector(const unsigned int degree); - - /** - * This is an analogon to the - * FETools::lexicographic_to_hierarchic_numbering - * function, but working on - * faces. Called from the - * constructor. - */ - static - std::vector - face_lexicographic_to_hierarchic_numbering (const unsigned int degree); - - /** - * Initialize the hanging node - * constraints matrices. Called - * from the constructor. - */ - void initialize_constraints (); - - /** - * Initialize the embedding - * matrices. Called from the - * constructor. - */ - void initialize_embedding (); - - /** - * Initialize the restriction - * matrices. Called from the - * constructor. - */ - void initialize_restriction (); - - /** - * Initialize the - * @p unit_support_points field - * of the FiniteElement - * class. Called from the - * constructor. - */ - void initialize_unit_support_points (); - - /** - * Initialize the @p unit_support_points - * field of the FiniteElement - * class. Called from the constructor in - * case the finite element is based on - * quadrature points. - */ - void initialize_unit_support_points (const Quadrature<1> &points); - - /** - * Initialize the - * @p unit_face_support_points field - * of the FiniteElement - * class. Called from the - * constructor. - */ - void initialize_unit_face_support_points (); - - /** - * Initialize the @p - * unit_face_support_points field of the - * FiniteElement class. Called from the - * constructor in case the finite element - * is based on quadrature points. - */ - void initialize_unit_face_support_points (const Quadrature<1> &points); - - /** - * Initialize the - * @p adjust_quad_dof_index_for_face_orientation_table field - * of the FiniteElement - * class. Called from the - * constructor. - */ - void initialize_quad_dof_index_permutation (); - - /** - * Mapping from hierarchic to - * lexicographic numbering on - * first face. Hierarchic is the - * numbering of the shape - * functions. - */ - const std::vector face_index_map; - - - /** - * Forward declaration of a class - * into which we put significant - * parts of the implementation. + * @p clone function instead of a copy constructor. * - * See the .cc file for more - * information. - */ - struct Implementation; - - /** - * Allow access from other - * dimensions. We need this since - * we want to call the functions - * @p get_dpo_vector and - * @p lexicographic_to_hierarchic_numbering - * for the faces of the finite - * element of dimension dim+1. + * This function is needed by the constructors of @p FESystem. */ - template friend class FE_Q; - - friend struct FE_Q::Implementation; + virtual FiniteElement *clone() const; }; /*@}*/ -/* -------------- declaration of explicit specializations ------------- */ - -template <> -void FE_Q<1>::initialize_unit_face_support_points (); - -template <> -std::vector -FE_Q<1>::face_lexicographic_to_hierarchic_numbering (const unsigned int); - - -template <> -void FE_Q<1,2>::initialize_unit_face_support_points (); - -template <> -std::vector -FE_Q<1,2>::face_lexicographic_to_hierarchic_numbering (const unsigned int); - -template <> -void FE_Q<1,3>::initialize_unit_face_support_points (); - -template <> -std::vector -FE_Q<1,3>::face_lexicographic_to_hierarchic_numbering (const unsigned int); - DEAL_II_NAMESPACE_CLOSE #endif diff --git a/deal.II/include/deal.II/fe/fe_q_base.h b/deal.II/include/deal.II/fe/fe_q_base.h new file mode 100644 index 0000000000..e3fe259c17 --- /dev/null +++ b/deal.II/include/deal.II/fe/fe_q_base.h @@ -0,0 +1,285 @@ +//--------------------------------------------------------------------------- +// $Id$ +// +// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//--------------------------------------------------------------------------- +#ifndef __deal2__fe_q_base_h +#define __deal2__fe_q_base_h + +#include +#include +#include + +DEAL_II_NAMESPACE_OPEN + + +/*!@addtogroup fe */ +/*@{*/ + +/** + * This class collects the basic methods used in FE_Q and FE_Q_DG0. There is + * no public constructor for this class as it is not functional as a + * stand-alone. The completion of definitions is left to the derived classes. + * + * @author Wolfgang Bangerth, 1998, 2003; Guido Kanschat, 2001; Ralf Hartmann, 2001, 2004, 2005; Oliver Kayser-Herold, 2004; Katharina Kormann, 2008; Martin Kronbichler, 2008, 2013 + */ +template +class FE_Q_Base : public FE_Poly +{ +public: + /** + * Constructor. + */ + FE_Q_Base (const POLY &poly_space, + const FiniteElementData &fe_data, + const std::vector &restriction_is_additive_flags); + + /** + * Return the matrix interpolating from the given finite element to the + * present one. The size of the matrix is then @p dofs_per_cell times + * source.dofs_per_cell. + * + * These matrices are only available if the source element is also a @p FE_Q + * element. Otherwise, an exception of type + * FiniteElement::ExcInterpolationNotImplemented is thrown. + */ + virtual void + get_interpolation_matrix (const FiniteElement &source, + FullMatrix &matrix) const; + + + /** + * Return the matrix interpolating from a face of of one element to the face + * of the neighboring element. The size of the matrix is then + * source.dofs_per_face times this->dofs_per_face. + * + * Derived elements will have to implement this function. They may only + * provide interpolation matrices for certain source finite elements, for + * example those from the same family. If they don't implement interpolation + * from a given element, then they must throw an exception of type + * FiniteElement::ExcInterpolationNotImplemented. + */ + virtual void + get_face_interpolation_matrix (const FiniteElement &source, + FullMatrix &matrix) const; + + /** + * Return the matrix interpolating from a face of of one element to the face + * of the neighboring element. The size of the matrix is then + * source.dofs_per_face times this->dofs_per_face. + * + * Derived elements will have to implement this function. They may only + * provide interpolation matrices for certain source finite elements, for + * example those from the same family. If they don't implement interpolation + * from a given element, then they must throw an exception of type + * FiniteElement::ExcInterpolationNotImplemented. + */ + virtual void + get_subface_interpolation_matrix (const FiniteElement &source, + const unsigned int subface, + FullMatrix &matrix) const; + + /** + * Check for non-zero values on a face. + * + * This function returns @p true, if the shape function @p shape_index has + * non-zero values on the face @p face_index. + * + * Implementation of the interface in FiniteElement + */ + virtual bool has_support_on_face (const unsigned int shape_index, + const unsigned int face_index) const; + + /** + * Projection from a fine grid space onto a coarse grid space. Overrides the + * respective method in FiniteElement, implementing lazy evaluation + * (initialize when requested). + * + * If this projection operator is associated with a matrix @p P, then the + * restriction of this matrix @p P_i to a single child cell is returned + * here. + * + * The matrix @p P is the concatenation or the sum of the cell matrices @p + * P_i, depending on the #restriction_is_additive_flags. This distinguishes + * interpolation (concatenation) and projection with respect to scalar + * products (summation). + * + * Row and column indices are related to coarse grid and fine grid spaces, + * respectively, consistent with the definition of the associated operator. + * + * If projection matrices are not implemented in the derived finite element + * class, this function aborts with ExcProjectionVoid. You can check whether + * this is the case by calling the restriction_is_implemented() or the + * isotropic_restriction_is_implemented() function. + */ + virtual const FullMatrix & + get_restriction_matrix (const unsigned int child, + const RefinementCase &refinement_case=RefinementCase::isotropic_refinement) const; + + /** + * Embedding matrix between grids. Overrides the respective method in + * FiniteElement, implementing lazy evaluation (initialize when queried). + * + * The identity operator from a coarse grid space into a fine grid space is + * associated with a matrix @p P. The restriction of this matrix @p P_i to a + * single child cell is returned here. + * + * The matrix @p P is the concatenation, not the sum of the cell matrices @p + * P_i. That is, if the same non-zero entry j,k exists in in two + * different child matrices @p P_i, the value should be the same in both + * matrices and it is copied into the matrix @p P only once. + * + * Row and column indices are related to fine grid and coarse grid spaces, + * respectively, consistent with the definition of the associated operator. + * + * These matrices are used by routines assembling the prolongation matrix + * for multi-level methods. Upon assembling the transfer matrix between + * cells using this matrix array, zero elements in the prolongation matrix + * are discarded and will not fill up the transfer matrix. + * + * If projection matrices are not implemented in the derived finite element + * class, this function aborts with ExcEmbeddingVoid. You can check whether + * this is the case by calling the prolongation_is_implemented() or the + * isotropic_prolongation_is_implemented() function. + */ + virtual const FullMatrix & + get_prolongation_matrix (const unsigned int child, + const RefinementCase &refinement_case=RefinementCase::isotropic_refinement) const; + + /** + * @name Functions to support hp + * @{ + */ + + /** + * Return whether this element implements its hanging node constraints in + * the new way, which has to be used to make elements "hp compatible". + * + * For the FE_Q class the result is always true (independent of the degree + * of the element), as it implements the complete set of functions necessary + * for hp capability. + */ + virtual bool hp_constraints_are_implemented () const; + + /** + * If, on a vertex, several finite elements are active, the hp code + * first assigns the degrees of freedom of each of these FEs + * different global indices. It then calls this function to find out + * which of them should get identical values, and consequently can + * receive the same global DoF index. This function therefore + * returns a list of identities between DoFs of the present finite + * element object with the DoFs of @p fe_other, which is a reference + * to a finite element object representing one of the other finite + * elements active on this particular vertex. The function computes + * which of the degrees of freedom of the two finite element objects + * are equivalent, both numbered between zero and the corresponding + * value of dofs_per_vertex of the two finite elements. The first + * index of each pair denotes one of the vertex dofs of the present + * element, whereas the second is the corresponding index of the + * other finite element. + */ + virtual + std::vector > + hp_vertex_dof_identities (const FiniteElement &fe_other) const; + + /** + * Same as hp_vertex_dof_indices(), except that the function treats + * degrees of freedom on lines. + */ + virtual + std::vector > + hp_line_dof_identities (const FiniteElement &fe_other) const; + + /** + * Same as hp_vertex_dof_indices(), except that the function treats + * degrees of freedom on quads. + */ + virtual + std::vector > + hp_quad_dof_identities (const FiniteElement &fe_other) const; + + /** + * Return whether this element dominates the one given as argument when they + * meet at a common face, whether it is the other way around, whether + * neither dominates, or if either could dominate. + * + * For a definition of domination, see FiniteElementBase::Domination and in + * particular the @ref hp_paper "hp paper". + */ + virtual + FiniteElementDomination::Domination + compare_for_face_domination (const FiniteElement &fe_other) const; + //@} + +protected: + /** + * Only for internal use. Its full name is @p get_dofs_per_object_vector + * function and it creates the @p dofs_per_object vector that is needed + * within the constructor to be passed to the constructor of @p + * FiniteElementData. + */ + static std::vector get_dpo_vector(const unsigned int degree); + + /** + * Performs the initialization of the element based on 1D support points, + * i.e., sets renumbering, initializes unit support points, initializes + * constraints as well as restriction and prolongation matrices. + */ + void initialize (const std::vector > &support_points_1d); + + /** + * Initialize the hanging node constraints matrices. Called from + * initialize(). + */ + void initialize_constraints (const std::vector > &points); + + /** + * Initialize the @p unit_support_points field of the FiniteElement + * class. Called from initialize(). + */ + void initialize_unit_support_points (const std::vector > &points); + + /** + * Initialize the @p unit_face_support_points field of the FiniteElement + * class. Called from initialize(). + */ + void initialize_unit_face_support_points (const std::vector > &points); + + /** + * Initialize the @p adjust_quad_dof_index_for_face_orientation_table field + * of the FiniteElement class. Called from initialize(). + */ + void initialize_quad_dof_index_permutation (); + + /** + * Forward declaration of a class into which we put significant parts of the + * implementation. + * + * See the .cc file for more information. + */ + struct Implementation; + + /* + * Declare implementation friend. + */ + friend struct FE_Q_Base::Implementation; + +private: + /* + * Mutex for protecting initialization of restriction and embedding matrix. + */ + mutable Threads::Mutex mutex; +}; + + +/*@}*/ + +DEAL_II_NAMESPACE_CLOSE + +#endif diff --git a/deal.II/include/deal.II/fe/fe_q_dg0.h b/deal.II/include/deal.II/fe/fe_q_dg0.h index 189ad4ab14..d01c6b22c8 100644 --- a/deal.II/include/deal.II/fe/fe_q_dg0.h +++ b/deal.II/include/deal.II/fe/fe_q_dg0.h @@ -1,9 +1,21 @@ +//--------------------------------------------------------------------------- +// $Id: fe_q.h 29043 2013-03-26 19:51:19Z bangerth $ +// +// Copyright (C) 2012, 2013 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//--------------------------------------------------------------------------- + #ifndef __deal2__fe_q_dg0_h #define __deal2__fe_q_dg0_h #include #include -#include +#include DEAL_II_NAMESPACE_OPEN @@ -218,264 +230,93 @@ DEAL_II_NAMESPACE_OPEN * */ template -class FE_Q_DG0 : public FE_Poly,dim,spacedim> +class FE_Q_DG0 : public FE_Q_Base,dim,spacedim> { public: /** - * Constructor for tensor product - * polynomials of degree @p p plus locally + * Constructor for tensor product polynomials of degree @p p plus locally * constant functions. */ FE_Q_DG0 (const unsigned int p); /** - * Constructor for tensor product - * polynomials with support - * points @p points plus locally constant - * functions based on a one-dimensional - * quadrature formula. The degree of the - * finite element is - * points.size()-1. - * Note that the first point has - * to be 0 and the last one 1. + * Constructor for tensor product polynomials with support points @p points + * plus locally constant functions based on a one-dimensional quadrature + * formula. The degree of the finite element is points.size()-1. + * Note that the first point has to be 0 and the last one 1. */ FE_Q_DG0 (const Quadrature<1> &points); /** - * Return a string that uniquely - * identifies a finite - * element. This class returns - * FE_Q_DG0(degree), with - * @p dim and @p degree - * replaced by appropriate - * values. + * Return a string that uniquely identifies a finite element. This class + * returns FE_Q_DG0(degree), with @p dim and @p degree + * replaced by appropriate values. */ virtual std::string get_name () const; /** - * Interpolate a set of scalar - * values, computed in the - * generalized support points. + * Interpolate a set of scalar values, computed in the generalized support + * points. */ virtual void interpolate(std::vector &local_dofs, const std::vector &values) const; /** - * Interpolate a set of vector - * values, computed in the - * generalized support points. + * Interpolate a set of vector values, computed in the generalized support + * points. * - * Since a finite element often - * only interpolates part of a - * vector, offset is - * used to determine the first - * component of the vector to be - * interpolated. Maybe consider - * changing your data structures - * to use the next function. + * Since a finite element often only interpolates part of a vector, + * offset is used to determine the first component of the vector to + * be interpolated. Maybe consider changing your data structures to use the + * next function. */ virtual void interpolate(std::vector &local_dofs, const std::vector > &values, unsigned int offset = 0) const; /** - * Interpolate a set of vector - * values, computed in the - * generalized support points. + * Interpolate a set of vector values, computed in the generalized support + * points. */ virtual void interpolate( std::vector &local_dofs, const VectorSlice > > &values) const; /** - * Return the matrix interpolating from the - * given finite element to the present one. - * The size of the matrix is then @p - * dofs_per_cell times + * Return the matrix interpolating from the given finite element to the + * present one. The size of the matrix is then @p dofs_per_cell times * source.dofs_per_cell. * - * These matrices are only available if the - * source element is also a @p FE_Q_DG0 - * element. Otherwise, an exception of type - * FiniteElement::ExcInterpolationNotImplemented - * is thrown. + * These matrices are only available if the source element is also a @p + * FE_Q_DG0 element. Otherwise, an exception of type + * FiniteElement::ExcInterpolationNotImplemented is thrown. */ virtual void get_interpolation_matrix (const FiniteElement &source, FullMatrix &matrix) const; - /** - * Return the matrix - * interpolating from a face of - * of one element to the face of - * the neighboring element. - * The size of the matrix is - * then source.dofs_per_face times - * this->dofs_per_face. - * - * Derived elements will have to - * implement this function. They - * may only provide interpolation - * matrices for certain source - * finite elements, for example - * those from the same family. If - * they don't implement - * interpolation from a given - * element, then they must throw - * an exception of type - * FiniteElement::ExcInterpolationNotImplemented. - */ - virtual void - get_face_interpolation_matrix (const FiniteElement &source, - FullMatrix &matrix) const; - - /** - * Return the matrix - * interpolating from a face of - * of one element to the face of - * the neighboring element. - * The size of the matrix is - * then source.dofs_per_face times - * this->dofs_per_face. - * - * Derived elements will have to - * implement this function. They - * may only provide interpolation - * matrices for certain source - * finite elements, for example - * those from the same family. If - * they don't implement - * interpolation from a given - * element, then they must throw - * an exception of type - * FiniteElement::ExcInterpolationNotImplemented. - */ - virtual void - get_subface_interpolation_matrix (const FiniteElement &source, - const unsigned int subface, - FullMatrix &matrix) const; - /** * Check for non-zero values on a face. * - * This function returns - * @p true, if the shape - * function @p shape_index has - * non-zero values on the face - * @p face_index. + * This function returns @p true, if the shape function @p shape_index has + * non-zero values on the face @p face_index. * - * Implementation of the - * interface in - * FiniteElement + * Implementation of the interface in FiniteElement */ virtual bool has_support_on_face (const unsigned int shape_index, const unsigned int face_index) const; - /** - * @name Functions to support hp - * @{ - */ - - /** - * Return whether this element - * implements its hanging node - * constraints in the new way, - * which has to be used to make - * elements "hp compatible". - * - * For the FE_Q_DG0 class the result is - * always true (independent of the degree - * of the element), as it implements the - * complete set of functions necessary - * for hp capability. - */ - virtual bool hp_constraints_are_implemented () const; - - /** - * If, on a vertex, several finite elements are active, the hp code - * first assigns the degrees of freedom of each of these FEs - * different global indices. It then calls this function to find out - * which of them should get identical values, and consequently can - * receive the same global DoF index. This function therefore - * returns a list of identities between DoFs of the present finite - * element object with the DoFs of @p fe_other, which is a reference - * to a finite element object representing one of the other finite - * elements active on this particular vertex. The function computes - * which of the degrees of freedom of the two finite element objects - * are equivalent, both numbered between zero and the corresponding - * value of dofs_per_vertex of the two finite elements. The first - * index of each pair denotes one of the vertex dofs of the present - * element, whereas the second is the corresponding index of the - * other finite element. - */ - virtual - std::vector > - hp_vertex_dof_identities (const FiniteElement &fe_other) const; - - /** - * Same as - * hp_vertex_dof_indices(), - * except that the function - * treats degrees of freedom on - * lines. - */ - virtual - std::vector > - hp_line_dof_identities (const FiniteElement &fe_other) const; - - /** - * Same as - * hp_vertex_dof_indices(), - * except that the function - * treats degrees of freedom on - * quads. - */ - virtual - std::vector > - hp_quad_dof_identities (const FiniteElement &fe_other) const; - - /** - * Return whether this element dominates - * the one given as argument when they - * meet at a common face, - * whether it is the other way around, - * whether neither dominates, or if - * either could dominate. - * - * For a definition of domination, see - * FiniteElementBase::Domination and in - * particular the @ref hp_paper "hp paper". - */ - virtual - FiniteElementDomination::Domination - compare_for_face_domination (const FiniteElement &fe_other) const; - //@} - - /** - * Determine an estimate for the - * memory consumption (in bytes) - * of this object. - * - * This function is made virtual, - * since finite element objects - * are usually accessed through - * pointers to their base class, - * rather than the class itself. - */ - virtual std::size_t memory_consumption () const; - protected: /** - * @p clone function instead of - * a copy constructor. + * @p clone function instead of a copy constructor. * - * This function is needed by the - * constructors of @p FESystem. + * This function is needed by the constructors of @p FESystem. */ virtual FiniteElement *clone() const; private: + /** * Returns the restriction_is_additive flags. * Only the last component is true. @@ -483,168 +324,18 @@ private: static std::vector get_riaf_vector(const unsigned int degree); /** - * Only for internal use. Its - * full name is - * @p get_dofs_per_object_vector - * function and it creates the - * @p dofs_per_object vector that is - * needed within the constructor to - * be passed to the constructor of - * @p FiniteElementData. + * Only for internal use. Its full name is @p get_dofs_per_object_vector + * function and it creates the @p dofs_per_object vector that is needed + * within the constructor to be passed to the constructor of @p + * FiniteElementData. */ static std::vector get_dpo_vector(const unsigned int degree); - - /** - * Only for internal use. Its - * full name is - * @p get_dofs_per_object_vector_q - * function and it creates the - * @p dofs_per_object vector that is - * needed within the constructor to - * be passed to the constructor of - * @p FiniteElementData for FE_Q_DG0 - * objects. - */ - static std::vector get_dpo_vector_q(const unsigned int degree); - - /** - * This is an analogon to the - * FETools::lexicographic_to_hierarchic_numbering - * function, but working on - * faces. Called from the - * constructor. - */ - static - std::vector - face_lexicographic_to_hierarchic_numbering (const unsigned int degree); - - /** - * Initialize the hanging node - * constraints matrices. Called - * from the constructor. - */ - void initialize_constraints (); - - /** - * Initialize the embedding - * matrices. Called from the - * constructor. - */ - void initialize_embedding (); - - /** - * Initialize the restriction - * matrices. Called from the - * constructor. - */ - void initialize_restriction (); - - /** - * Initialize the - * @p unit_support_points field - * of the FiniteElement - * class. Called from the - * constructor. - */ - void initialize_unit_support_points (); - - /** - * Initialize the @p unit_support_points - * field of the FiniteElement - * class. Called from the constructor in - * case the finite element is based on - * quadrature points. - */ - void initialize_unit_support_points (const Quadrature<1> &points); - - /** - * Initialize the - * @p unit_face_support_points field - * of the FiniteElement - * class. Called from the - * constructor. - */ - void initialize_unit_face_support_points (); - - /** - * Initialize the @p - * unit_face_support_points field of the - * FiniteElement class. Called from the - * constructor in case the finite element - * is based on quadrature points. - */ - void initialize_unit_face_support_points (const Quadrature<1> &points); - - /** - * Initialize the - * @p adjust_quad_dof_index_for_face_orientation_table field - * of the FiniteElement - * class. Called from the - * constructor. - */ - void initialize_quad_dof_index_permutation (); - - /** - * Mapping from hierarchic to - * lexicographic numbering on - * first face. Hierarchic is the - * numbering of the shape - * functions. - */ - const std::vector face_index_map; - - - /** - * Forward declaration of a class - * into which we put significant - * parts of the implementation. - * - * See the .cc file for more - * information. - */ - struct Implementation; - - /** - * Allow access from other - * dimensions. We need this since - * we want to call the functions - * @p get_dpo_vector and - * @p lexicographic_to_hierarchic_numbering - * for the faces of the finite - * element of dimension dim+1. - */ - template friend class FE_Q_DG0; - - friend struct FE_Q_DG0::Implementation; }; /*@}*/ -/* -------------- declaration of explicit specializations ------------- */ - -template <> -void FE_Q_DG0<1>::initialize_unit_face_support_points (); - -template <> -std::vector -FE_Q_DG0<1>::face_lexicographic_to_hierarchic_numbering (const unsigned int); - - -template <> -void FE_Q_DG0<1,2>::initialize_unit_face_support_points (); - -template <> -std::vector -FE_Q_DG0<1,2>::face_lexicographic_to_hierarchic_numbering (const unsigned int); - -template <> -void FE_Q_DG0<1,3>::initialize_unit_face_support_points (); - -template <> -std::vector -FE_Q_DG0<1,3>::face_lexicographic_to_hierarchic_numbering (const unsigned int); DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/include/deal.II/fe/fe_q_iso_q1.h b/deal.II/include/deal.II/fe/fe_q_iso_q1.h new file mode 100644 index 0000000000..7f4c5cc620 --- /dev/null +++ b/deal.II/include/deal.II/fe/fe_q_iso_q1.h @@ -0,0 +1,155 @@ +//--------------------------------------------------------------------------- +// $Id$ +// +// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//--------------------------------------------------------------------------- +#ifndef __deal2__fe_q_iso_q1_h +#define __deal2__fe_q_iso_q1_h + +#include +#include +#include +#include + +DEAL_II_NAMESPACE_OPEN + + +/*!@addtogroup fe */ +/*@{*/ + +/** + * Implementation of a scalar Lagrange finite element @p Qp-iso-Q1 that + * defines the finite element space of continuous, piecewise linear elements + * with @p p subdivisions in each coordinate direction. It yields an element + * with the same number of degrees of freedom as the @p Qp elements but using + * linear interpolation instead of higher order one. This type of element is + * also called macro element in the literature as it really consists of several + * smaller elements, namely pdim. + * + * The numbering of degrees of freedom is done in exactly the same way as in + * FE_Q of degree @p p. See there for a detailed description on how degrees of + * freedom are numbered within one element. + * + * This element represents a Q-linear finite element space on a reduced mesh + * size h/p. Its effect is equivalent to using FE_Q of degree one on a + * finer mesh by a factor @p p if an equivalent quadrature is used. However, + * this element reduces the flexibility in the choice of (adaptive) mesh size + * by exactly this factor @p p, which typically reduces efficiency. On the + * other hand, comparing this element with @p p subdivisions to the FE_Q + * element of degree @p p on the same mesh shows that the convergence is + * typically much worse for smooth problems. In particular, @p Qp elements + * achieve interpolation orders of hp+1 in the L2 norm, + * whereas these elements reach only (h/p)2. For these two + * reasons, this element is usually not very useful as a standalone. In + * addition, any evaluation of face terms on the boundaries within the + * elements becomes impossible with this element. + * + * Nonetheless, there are a few use cases where this element actually is + * useful: + *
      + * + *
    1. Systems of PDEs where certain variables demand for higher resolutions + * than the others and the additional degrees of freedom should be spend on + * increasing the resolution of linears instead of higher order polynomials, + * and you do not want to use two different meshes for the different + * components. This can be the case when irregularities (shocks) appear in the + * solution and stabilization techniques are used that work for linears but + * not higher order elements.
    2. + * + *
    3. Stokes/Navier Stokes systems as the one discussed in step-22 could be + * solved with Q2-iso-Q1 elements for velocities instead of Q2 + * elements. Combined with Q1 pressures they give a stable mixed element + * pair. However, they perform worse than the standard approach in most + * situations.
    4. + * + *
    5. Preconditioning systems of FE_Q systems of higher order @p p with a + * preconditioner based on @p Qp-iso-Q1 elements: Some preconditioners like + * algebraic multigrid perform much better with linear elements than with higher + * order elements because they often implicitly assume a sparse connectivity + * between entries. Then, creating a preconditioner matrix based on these + * elements yields the same number of degrees of freedom (and a spectrally + * equivalent linear system), which can be combined with a (high order) system + * matrix in an iterative solver like CG.
    6. + *
    + * + *

    Appropriate integration

    + * + * Due to the nature of these elements as a concatenation of linears, care + * must be taken when selecting quadrature formulas for this element. The + * standard choice for an element of @p p subelements is a formula + * QIterated(QGauss<1>(2), p), which corresponds to the formula + * that would be used for integrating functions on a finer mesh. This is in + * contrast with FE_Q(p) where QGauss(p+1) is the default choice. In + * particular, care must be taken to not use a quadrature formula that + * evaluates the basis functions (and their derivatives) on sub-element + * boundaries as the gradients of piecewiese functions on internal boundaries + * are set to zero. No checks are performed internally to ensure that this is + * not the case - it is the user's responsibility to avoid these situations. + * + * Also note that the usual deal.II routines for setting up sparsity patterns + * and assembling matrices do not make use of the increased sparsity in this + * element compared to FE_Q. This is because DoFTools::make_sparsity_pattern + * assumes coupling between all degrees of freedom within the element, whereas + * FE_Q_iso_Q1 with more than one subdivision does have less coupling. + * + * @author Martin Kronbichler, 2013 + */ +template +class FE_Q_iso_Q1 : public FE_Q_Base >,dim,spacedim> +{ +public: + /** + * Constructs a FE_Q_iso_Q1 element with a given number of subdivisions. The + * number of subdivision is similar to the degree in FE_Q in the sense that + * both elements produce the same number of degrees of freedom. + */ + FE_Q_iso_Q1(const unsigned int n_subdivisions); + + /** + * Return a string that uniquely identifies a finite element. This class + * returns FE_Q_iso_q1(equivalent_degree), with @p dim and @p + * equivalent_degree replaced by appropriate values. + */ + virtual std::string get_name () const; + + /** + * @name Functions to support hp + * @{ + */ + + /** + * Return whether this element dominates the one given as argument when they + * meet at a common face, whether it is the other way around, whether + * neither dominates, or if either could dominate. + * + * For a definition of domination, see FiniteElementBase::Domination and in + * particular the @ref hp_paper "hp paper". + */ + virtual + FiniteElementDomination::Domination + compare_for_face_domination (const FiniteElement &fe_other) const; + //@} + +protected: + + /** + * @p clone function instead of a copy constructor. + * + * This function is needed by the constructors of @p FESystem. + */ + virtual FiniteElement *clone() const; +}; + + + +/*@}*/ + +DEAL_II_NAMESPACE_CLOSE + +#endif diff --git a/deal.II/include/deal.II/fe/fe_system.h b/deal.II/include/deal.II/fe/fe_system.h index e5f408b544..650dbc5a43 100644 --- a/deal.II/include/deal.II/fe/fe_system.h +++ b/deal.II/include/deal.II/fe/fe_system.h @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -152,32 +153,23 @@ class FESystem : public FiniteElement public: /** - * Constructor. Take a finite element type - * and the number of elements you want to - * group together using this class. + * Constructor. Take a finite element type and the number of elements you + * want to group together using this class. * - * In fact, the object @p fe is not used, - * apart from getting the number of dofs - * per vertex, line, etc for that finite - * element class. The objects creates its - * own copy of the finite element object - * at construction time (but after - * the initialization of the base class - * @p FiniteElement, which is why we need - * a valid finite element object passed - * to the constructor). + * In fact, the object @p fe is not used, apart from getting the number of + * dofs per vertex, line, etc for that finite element class. The objects + * creates its own copy of the finite element object at construction time + * (but after the initialization of the base class @p FiniteElement, which + * is why we need a valid finite element object passed to the constructor). * - * Obviously, the template finite element - * class needs to be of the same dimension - * as is this object. + * Obviously, the template finite element class needs to be of the same + * dimension as is this object. */ FESystem (const FiniteElement &fe, const unsigned int n_elements); /** - * Constructor for mixed - * discretizations with two - * base elements. + * Constructor for mixed discretizations with two base elements. * * See the other constructor. */ @@ -185,9 +177,7 @@ public: const FiniteElement &fe2, const unsigned int n2); /** - * Constructor for mixed - * discretizations with three - * base elements. + * Constructor for mixed discretizations with three base elements. * * See the other constructor. */ @@ -196,9 +186,7 @@ public: const FiniteElement &fe3, const unsigned int n3); /** - * Constructor for mixed - * discretizations with four - * base elements. + * Constructor for mixed discretizations with four base elements. * * See the other constructor. */ @@ -208,9 +196,7 @@ public: const FiniteElement &fe4, const unsigned int n4); /** - * Constructor for mixed - * discretizations with five - * base elements. + * Constructor for mixed discretizations with five base elements. * * See the other constructor. */ @@ -221,15 +207,9 @@ public: const FiniteElement &fe5, const unsigned int n5); /** - * Same as above but for any - * number of base - * elements. Pointers to the base - * elements and their - * multiplicities are passed as - * vectors to this - * constructor. The length of - * these vectors is assumed to be - * equal. + * Same as above but for any number of base elements. Pointers to the base + * elements and their multiplicities are passed as vectors to this + * constructor. The length of these vectors is assumed to be equal. */ FESystem (const std::vector*> &fes, @@ -241,172 +221,95 @@ public: virtual ~FESystem (); /** - * Return a string that uniquely - * identifies a finite - * element. This element returns - * a string that is composed of - * the strings - * @p name1...@p nameN returned - * by the basis elements. From - * these, we create a sequence - * FESystem[name1^m1-name2^m2-...-nameN^mN], - * where @p mi are the - * multiplicities of the basis - * elements. If a multiplicity is - * equal to one, then the - * superscript is omitted. + * Return a string that uniquely identifies a finite element. This element + * returns a string that is composed of the strings @p name1...@p nameN + * returned by the basis elements. From these, we create a sequence + * FESystem[name1^m1-name2^m2-...-nameN^mN], where @p mi are + * the multiplicities of the basis elements. If a multiplicity is equal to + * one, then the superscript is omitted. */ virtual std::string get_name () const; /** - * Return the value of the - * @p ith shape function at the - * point @p p. @p p is a point - * on the reference element. Since - * this finite element is always - * vector-valued, we return the - * value of the only non-zero - * component of the vector value - * of this shape function. If the - * shape function has more than - * one non-zero component (which - * we refer to with the term - * non-primitive), then throw an - * exception of type - * @p ExcShapeFunctionNotPrimitive. + * Return the value of the @p ith shape function at the point @p p. @p p is + * a point on the reference element. Since this finite element is always + * vector-valued, we return the value of the only non-zero component of the + * vector value of this shape function. If the shape function has more than + * one non-zero component (which we refer to with the term non-primitive), + * then throw an exception of type @p ExcShapeFunctionNotPrimitive. * - * An - * @p ExcUnitShapeValuesDoNotExist - * is thrown if the shape values - * of the @p FiniteElement - * (corresponding to the @p ith - * shape function) depend on the - * shape of the cell in real - * space. + * An @p ExcUnitShapeValuesDoNotExist is thrown if the shape values of the + * @p FiniteElement (corresponding to the @p ith shape function) depend on + * the shape of the cell in real space. */ virtual double shape_value (const unsigned int i, const Point &p) const; /** - * Return the value of the - * @p componentth vector - * component of the @p ith shape - * function at the point - * @p p. See the - * FiniteElement base - * class for more information - * about the semantics of this - * function. + * Return the value of the @p componentth vector component of the @p ith + * shape function at the point @p p. See the FiniteElement base class for + * more information about the semantics of this function. * - * Since this element is vector - * valued in general, it relays - * the computation of these - * values to the base elements. + * Since this element is vector valued in general, it relays the computation + * of these values to the base elements. */ virtual double shape_value_component (const unsigned int i, const Point &p, const unsigned int component) const; /** - * Return the gradient of the - * @p ith shape function at the - * point @p p. @p p is a point - * on the reference element, and - * likewise the gradient is the - * gradient on the unit cell with - * respect to unit cell - * coordinates. Since - * this finite element is always - * vector-valued, we return the - * value of the only non-zero - * component of the vector value - * of this shape function. If the - * shape function has more than - * one non-zero component (which - * we refer to with the term - * non-primitive), then throw an - * exception of type - * @p ExcShapeFunctionNotPrimitive. + * Return the gradient of the @p ith shape function at the point @p p. @p p + * is a point on the reference element, and likewise the gradient is the + * gradient on the unit cell with respect to unit cell coordinates. Since + * this finite element is always vector-valued, we return the value of the + * only non-zero component of the vector value of this shape function. If + * the shape function has more than one non-zero component (which we refer + * to with the term non-primitive), then throw an exception of type @p + * ExcShapeFunctionNotPrimitive. * - * An - * @p ExcUnitShapeValuesDoNotExist - * is thrown if the shape values - * of the @p FiniteElement - * (corresponding to the @p ith - * shape function) depend on the - * shape of the cell in real - * space. + * An @p ExcUnitShapeValuesDoNotExist is thrown if the shape values of the + * @p FiniteElement (corresponding to the @p ith shape function) depend on + * the shape of the cell in real space. */ virtual Tensor<1,dim> shape_grad (const unsigned int i, const Point &p) const; /** - * Return the gradient of the - * @p componentth vector - * component of the @p ith shape - * function at the point - * @p p. See the - * FiniteElement base - * class for more information - * about the semantics of this - * function. + * Return the gradient of the @p componentth vector component of the @p ith + * shape function at the point @p p. See the FiniteElement base class for + * more information about the semantics of this function. * - * Since this element is vector - * valued in general, it relays - * the computation of these - * values to the base elements. + * Since this element is vector valued in general, it relays the computation + * of these values to the base elements. */ virtual Tensor<1,dim> shape_grad_component (const unsigned int i, const Point &p, const unsigned int component) const; /** - * Return the tensor of second - * derivatives of the @p ith - * shape function at point @p p - * on the unit cell. The - * derivatives are derivatives on - * the unit cell with respect to - * unit cell coordinates. Since - * this finite element is always - * vector-valued, we return the - * value of the only non-zero - * component of the vector value - * of this shape function. If the - * shape function has more than - * one non-zero component (which - * we refer to with the term - * non-primitive), then throw an - * exception of type - * @p ExcShapeFunctionNotPrimitive. + * Return the tensor of second derivatives of the @p ith shape function at + * point @p p on the unit cell. The derivatives are derivatives on the unit + * cell with respect to unit cell coordinates. Since this finite element is + * always vector-valued, we return the value of the only non-zero component + * of the vector value of this shape function. If the shape function has + * more than one non-zero component (which we refer to with the term + * non-primitive), then throw an exception of type @p + * ExcShapeFunctionNotPrimitive. * - * An - * @p ExcUnitShapeValuesDoNotExist - * is thrown if the shape values - * of the @p FiniteElement - * (corresponding to the @p ith - * shape function) depend on the - * shape of the cell in real - * space. + * An @p ExcUnitShapeValuesDoNotExist is thrown if the shape values of the + * @p FiniteElement (corresponding to the @p ith shape function) depend on + * the shape of the cell in real space. */ virtual Tensor<2,dim> shape_grad_grad (const unsigned int i, const Point &p) const; /** - * Return the second derivatives - * of the @p componentth vector - * component of the @p ith shape - * function at the point - * @p p. See the - * FiniteElement base - * class for more information - * about the semantics of this - * function. + * Return the second derivatives of the @p componentth vector component of + * the @p ith shape function at the point @p p. See the FiniteElement base + * class for more information about the semantics of this function. * - * Since this element is vector - * valued in general, it relays - * the computation of these - * values to the base elements. + * Since this element is vector valued in general, it relays the computation + * of these values to the base elements. */ virtual Tensor<2,dim> @@ -415,76 +318,105 @@ public: const unsigned int component) const; /** - * Return the matrix - * interpolating from the given - * finite element to the present - * one. The size of the matrix is - * then @p dofs_per_cell times + * Return the matrix interpolating from the given finite element to the + * present one. The size of the matrix is then @p dofs_per_cell times * source.dofs_per_cell. * - * These matrices are available - * if source and destination - * element are both @p FESystem - * elements, have the same number - * of base elements with same - * element multiplicity, and if - * these base elements also - * implement their - * @p get_interpolation_matrix - * functions. Otherwise, an - * exception of type - * FiniteElement::ExcInterpolationNotImplemented - * is thrown. + * These matrices are available if source and destination element are both + * @p FESystem elements, have the same number of base elements with same + * element multiplicity, and if these base elements also implement their @p + * get_interpolation_matrix functions. Otherwise, an exception of type + * FiniteElement::ExcInterpolationNotImplemented is thrown. */ virtual void get_interpolation_matrix (const FiniteElement &source, FullMatrix &matrix) const; /** - * Access to a composing - * element. The index needs to be - * smaller than the number of - * base elements. Note that the - * number of base elements may in - * turn be smaller than the - * number of components of the - * system element, if the - * multiplicities are greater - * than one. + * Access to a composing element. The index needs to be smaller than the + * number of base elements. Note that the number of base elements may in + * turn be smaller than the number of components of the system element, if + * the multiplicities are greater than one. */ virtual const FiniteElement & base_element (const unsigned int index) const; /** - * Check for non-zero values on a - * face. + * Check for non-zero values on a face. * - * This function returns - * @p true, if the shape - * function @p shape_index has - * non-zero values on the face - * @p face_index. + * This function returns @p true, if the shape function @p shape_index has + * non-zero values on the face @p face_index. * - * Implementation of the - * interface in - * FiniteElement + * Implementation of the interface in FiniteElement */ virtual bool has_support_on_face (const unsigned int shape_index, const unsigned int face_index) const; /** - * Implementation of the - * respective function in the - * base class. + * Projection from a fine grid space onto a coarse grid space. Overrides the + * respective method in FiniteElement, implementing lazy evaluation + * (initialize when requested). + * + * If this projection operator is associated with a matrix @p P, then the + * restriction of this matrix @p P_i to a single child cell is returned + * here. + * + * The matrix @p P is the concatenation or the sum of the cell matrices @p + * P_i, depending on the #restriction_is_additive_flags. This distinguishes + * interpolation (concatenation) and projection with respect to scalar + * products (summation). + * + * Row and column indices are related to coarse grid and fine grid spaces, + * respectively, consistent with the definition of the associated operator. + * + * If projection matrices are not implemented in the derived finite element + * class, this function aborts with ExcProjectionVoid. You can check whether + * this is the case by calling the restriction_is_implemented() or the + * isotropic_restriction_is_implemented() function. + */ + virtual const FullMatrix & + get_restriction_matrix (const unsigned int child, + const RefinementCase &refinement_case=RefinementCase::isotropic_refinement) const; + + /** + * Embedding matrix between grids. Overrides the respective method in + * FiniteElement, implementing lazy evaluation (initialize when queried). + * + * The identity operator from a coarse grid space into a fine grid space is + * associated with a matrix @p P. The restriction of this matrix @p P_i to a + * single child cell is returned here. + * + * The matrix @p P is the concatenation, not the sum of the cell matrices @p + * P_i. That is, if the same non-zero entry j,k exists in in two + * different child matrices @p P_i, the value should be the same in both + * matrices and it is copied into the matrix @p P only once. + * + * Row and column indices are related to fine grid and coarse grid spaces, + * respectively, consistent with the definition of the associated operator. + * + * These matrices are used by routines assembling the prolongation matrix + * for multi-level methods. Upon assembling the transfer matrix between + * cells using this matrix array, zero elements in the prolongation matrix + * are discarded and will not fill up the transfer matrix. + * + * If projection matrices are not implemented in the derived finite element + * class, this function aborts with ExcEmbeddingVoid. You can check whether + * this is the case by calling the prolongation_is_implemented() or the + * isotropic_prolongation_is_implemented() function. + */ + virtual const FullMatrix & + get_prolongation_matrix (const unsigned int child, + const RefinementCase &refinement_case=RefinementCase::isotropic_refinement) const; + + /** + * Implementation of the respective function in the base class. */ virtual Point unit_support_point (const unsigned int index) const; /** - * Implementation of the - * respective function in the - * base class. + * Implementation of the respective function in the base class. */ virtual Point @@ -496,39 +428,25 @@ public: */ /** - * Return whether this element - * implements its hanging node - * constraints in the new way, - * which has to be used to make - * elements "hp compatible". + * Return whether this element implements its hanging node constraints in + * the new way, which has to be used to make elements "hp compatible". * - * This function returns @p true iff all - * its base elements return @p true for - * this function. + * This function returns @p true iff all its base elements return @p true + * for this function. */ virtual bool hp_constraints_are_implemented () const; /** - * Return the matrix - * interpolating from a face of - * of one element to the face of - * the neighboring element. - * The size of the matrix is - * then source.dofs_per_face times - * this->dofs_per_face. + * Return the matrix interpolating from a face of of one element to the face + * of the neighboring element. The size of the matrix is then + * source.dofs_per_face times this->dofs_per_face. * - * Base elements of this element will - * have to implement this function. They - * may only provide interpolation - * matrices for certain source finite - * elements, for example those from the - * same family. If they don't implement - * interpolation from a given element, - * then they must throw an exception of - * type - * FiniteElement::ExcInterpolationNotImplemented, - * which will get propagated out from - * this element. + * Base elements of this element will have to implement this function. They + * may only provide interpolation matrices for certain source finite + * elements, for example those from the same family. If they don't implement + * interpolation from a given element, then they must throw an exception of + * type FiniteElement::ExcInterpolationNotImplemented, which + * will get propagated out from this element. */ virtual void get_face_interpolation_matrix (const FiniteElement &source, @@ -536,26 +454,16 @@ public: /** - * Return the matrix - * interpolating from a face of - * of one element to the subface of - * the neighboring element. - * The size of the matrix is - * then source.dofs_per_face times - * this->dofs_per_face. + * Return the matrix interpolating from a face of of one element to the + * subface of the neighboring element. The size of the matrix is then + * source.dofs_per_face times this->dofs_per_face. * - * Base elements of this element will - * have to implement this function. They - * may only provide interpolation - * matrices for certain source finite - * elements, for example those from the - * same family. If they don't implement - * interpolation from a given element, - * then they must throw an exception of - * type - * FiniteElement::ExcInterpolationNotImplemented, - * which will get propagated out from - * this element. + * Base elements of this element will have to implement this function. They + * may only provide interpolation matrices for certain source finite + * elements, for example those from the same family. If they don't implement + * interpolation from a given element, then they must throw an exception of + * type FiniteElement::ExcInterpolationNotImplemented, which + * will get propagated out from this element. */ virtual void get_subface_interpolation_matrix (const FiniteElement &source, @@ -600,15 +508,11 @@ public: hp_quad_dof_identities (const FiniteElement &fe_other) const; /** - * Return whether this element dominates - * the one given as argument when they - * meet at a common face, - * whether it is the other way around, - * whether neither dominates, or if - * either could dominate. + * Return whether this element dominates the one given as argument when they + * meet at a common face, whether it is the other way around, whether + * neither dominates, or if either could dominate. * - * For a definition of domination, see - * FiniteElementBase::Domination and in + * For a definition of domination, see FiniteElementBase::Domination and in * particular the @ref hp_paper "hp paper". */ virtual @@ -617,37 +521,30 @@ public: //@} /** - * Determine an estimate for the - * memory consumption (in bytes) - * of this object. + * Determine an estimate for the memory consumption (in bytes) of this + * object. * - * This function is made virtual, - * since finite element objects - * are usually accessed through - * pointers to their base class, - * rather than the class itself. + * This function is made virtual, since finite element objects are usually + * accessed through pointers to their base class, rather than the class + * itself. */ virtual std::size_t memory_consumption () const; protected: /** - * Compute flags for initial - * update only. + * Compute flags for initial update only. */ virtual UpdateFlags update_once (const UpdateFlags flags) const; /** - * Compute flags for update on - * each cell. + * Compute flags for update on each cell. */ virtual UpdateFlags update_each (const UpdateFlags flags) const; /** - * @p clone function instead of - * a copy constructor. + * @p clone function instead of a copy constructor. * - * This function is needed by the - * constructors of @p FESystem. + * This function is needed by the constructors of @p FESystem. */ virtual FiniteElement *clone() const; @@ -667,15 +564,10 @@ protected: const Quadrature &quadrature) const ; /** - * Implementation of the same - * function in - * FiniteElement. + * Implementation of the same function in FiniteElement. * - * Passes on control to - * @p compute_fill that does the - * work for all three - * fill_fe*_values - * functions. + * Passes on control to @p compute_fill that does the work for all three + * fill_fe*_values functions. */ virtual void fill_fe_values (const Mapping &mapping, @@ -687,13 +579,9 @@ protected: CellSimilarity::Similarity &cell_similarity) const; /** - * Implementation of the same - * function in - * FiniteElement. + * Implementation of the same function in FiniteElement. * - * Passes on control to - * @p compute_fill that does the - * work for all three + * Passes on control to @p compute_fill that does the work for all three * fill_fe*_values functions. */ virtual void @@ -706,13 +594,9 @@ protected: FEValuesData &data) const ; /** - * Implementation of the same - * function in - * FiniteElement. + * Implementation of the same function in FiniteElement. * - * Passes on control to - * @p compute_fill that does the - * work for all three + * Passes on control to @p compute_fill that does the work for all three * fill_fe*_values functions. */ virtual void @@ -727,26 +611,14 @@ protected: /** - * Do the work for the three - * fill_fe*_values functions. + * Do the work for the three fill_fe*_values functions. * - * Calls (among other things) - * fill_fe_([sub]face)_values - * of the base elements. Calls - * @p fill_fe_values if - * face_no==invalid_face_no - * and - * sub_no==invalid_face_no; - * calls @p fill_fe_face_values - * if - * face_no==invalid_face_no - * and - * sub_no!=invalid_face_no; - * and calls - * @p fill_fe_subface_values if - * face_no!=invalid_face_no - * and - * sub_no!=invalid_face_no. + * Calls (among other things) fill_fe_([sub]face)_values of the + * base elements. Calls @p fill_fe_values if + * face_no==invalid_face_no and sub_no==invalid_face_no; + * calls @p fill_fe_face_values if face_no==invalid_face_no and + * sub_no!=invalid_face_no; and calls @p fill_fe_subface_values if + * face_no!=invalid_face_no and sub_no!=invalid_face_no. */ template void compute_fill (const Mapping &mapping, @@ -762,9 +634,7 @@ protected: private: /** - * Value to indicate that a given - * face or subface number is - * invalid. + * Value to indicate that a given face or subface number is invalid. */ static const unsigned int invalid_face_number = numbers::invalid_unsigned_int; @@ -788,42 +658,28 @@ private: /** - * Initialize the - * @p unit_support_points field - * of the FiniteElement - * class. Called from the - * constructor. + * Initialize the @p unit_support_points field of the FiniteElement + * class. Called from the constructor. */ void initialize_unit_support_points (); /** - * Initialize the - * @p unit_face_support_points field - * of the FiniteElement - * class. Called from the - * constructor. + * Initialize the @p unit_face_support_points field of the FiniteElement + * class. Called from the constructor. */ void initialize_unit_face_support_points (); /** - * Initialize the - * @p adjust_quad_dof_index_for_face_orientation_table field - * of the FiniteElement - * class. Called from the - * constructor. + * Initialize the @p adjust_quad_dof_index_for_face_orientation_table field + * of the FiniteElement class. Called from the constructor. */ void initialize_quad_dof_index_permutation (); /** - * Helper function used in the constructor: - * take a @p FiniteElementData object - * and return an object of the same type - * with the number of degrees of - * freedom per vertex, line, etc. - * multiplied by @p n. Don't touch the - * number of functions for the - * transformation from unit to real - * cell. + * Helper function used in the constructor: take a @p FiniteElementData + * object and return an object of the same type with the number of degrees + * of freedom per vertex, line, etc. multiplied by @p n. Don't touch the + * number of functions for the transformation from unit to real cell. */ static FiniteElementData multiply_dof_numbers (const FiniteElement *fe1, @@ -838,8 +694,7 @@ private: const unsigned int N5=0); /** - * Same as above but for - * any number of sub-elements. + * Same as above but for any number of sub-elements. */ static FiniteElementData multiply_dof_numbers (const std::vector*> &fes, @@ -848,15 +703,10 @@ private: /** - * Helper function used in the - * constructor: takes a - * @p FiniteElement object and - * returns an boolean vector - * including the - * @p restriction_is_additive_flags - * of the mixed element - * consisting of @p N elements - * of the sub-element @p fe. + * Helper function used in the constructor: takes a @p FiniteElement object + * and returns an boolean vector including the @p + * restriction_is_additive_flags of the mixed element consisting of @p N + * elements of the sub-element @p fe. */ static std::vector compute_restriction_is_additive_flags ( @@ -872,11 +722,8 @@ private: const unsigned int N5=0); /** - * Compute the named flags for a - * list of finite elements with - * multiplicities given in the - * second argument. This function - * is called from all the above + * Compute the named flags for a list of finite elements with multiplicities + * given in the second argument. This function is called from all the above * functions. */ static std::vector @@ -886,9 +733,7 @@ private: /** - * Compute the non-zero vector - * components of a composed - * finite element. + * Compute the non-zero vector components of a composed finite element. */ static std::vector compute_nonzero_components (const FiniteElement *fe1, @@ -903,26 +748,18 @@ private: const unsigned int N5=0); /** - * Compute the nonzero components - * of a list of finite elements - * with multiplicities given in - * the second argument. This - * function is called from all - * the above functions. + * Compute the nonzero components of a list of finite elements with + * multiplicities given in the second argument. This function is called from + * all the above functions. */ static std::vector compute_nonzero_components (const std::vector*> &fes, const std::vector &multiplicities); /** - * This function is simply - * singled out of the - * constructors since there are - * several of them. It sets up - * the index table for the system - * as well as @p restriction and - * @p prolongation - * matrices. + * This function is simply singled out of the constructors since there are + * several of them. It sets up the index table for the system as well as @p + * restriction and @p prolongation matrices. */ void initialize (const std::vector*> &fes, const std::vector &multiplicities); @@ -943,176 +780,116 @@ private: void build_interface_constraints (); /** - * A function that computes the - * hp_vertex_dof_identities(), - * hp_line_dof_identities(), or - * hp_quad_dof_identities(), depending on - * the value of the template parameter. + * A function that computes the hp_vertex_dof_identities(), + * hp_line_dof_identities(), or hp_quad_dof_identities(), depending on the + * value of the template parameter. */ template std::vector > hp_object_dof_identities (const FiniteElement &fe_other) const; /** - * Usually: Fields of - * cell-independent data. + * Usually: Fields of cell-independent data. * - * However, here, this class does - * not itself store the data but - * only pointers to - * @p InternalData objects for - * each of the base elements. + * However, here, this class does not itself store the data but only + * pointers to @p InternalData objects for each of the base elements. */ class InternalData : public FiniteElement::InternalDataBase { public: /** - * Constructor. Is called by - * the @p get_data - * function. Sets the size of - * the @p base_fe_datas - * vector to - * @p n_base_elements. + * Constructor. Is called by the @p get_data function. Sets the size of + * the @p base_fe_datas vector to @p n_base_elements. */ InternalData (const unsigned int n_base_elements); /** - * Destructor. Deletes all - * @p InternalDatas whose - * pointers are stored by the - * @p base_fe_datas - * vector. + * Destructor. Deletes all @p InternalDatas whose pointers are stored by + * the @p base_fe_datas vector. */ ~InternalData(); /** - * Flag indicating whether - * second derivatives shall - * be computed. + * Flag indicating whether second derivatives shall be computed. */ bool compute_hessians; /** - * Gives write-access to the - * pointer to a - * @p InternalData of the - * @p base_noth base - * element. + * Gives write-access to the pointer to a @p InternalData of the @p + * base_noth base element. */ void set_fe_data(const unsigned int base_no, typename FiniteElement::InternalDataBase *); /** - * Gives read-access to the - * pointer to a - * @p InternalData of the - * @p base_noth base element. + * Gives read-access to the pointer to a @p InternalData of the @p + * base_noth base element. */ typename FiniteElement::InternalDataBase & get_fe_data (const unsigned int base_no) const; /** - * Gives write-access to the - * pointer to a - * @p FEValuesData for the - * @p base_noth base - * element. + * Gives write-access to the pointer to a @p FEValuesData for the @p + * base_noth base element. */ void set_fe_values_data (const unsigned int base_no, FEValuesData *); /** - * Gives read-access to the - * pointer to a - * @p FEValuesData for the - * @p base_noth base element. + * Gives read-access to the pointer to a @p FEValuesData for the @p + * base_noth base element. */ FEValuesData &get_fe_values_data (const unsigned int base_no) const; /** - * Deletes the - * @p FEValuesData the - * fe_datas[base_no] - * pointer is pointing - * to. Sets - * fe_datas[base_no] to - * zero. + * Deletes the @p FEValuesData the fe_datas[base_no] pointer is + * pointing to. Sets fe_datas[base_no] to zero. * - * This function is used to - * delete @p FEValuesData - * that are needed only on - * the first cell but not any - * more afterwards. This is - * the case for - * e.g. Lagrangian elements - * (see e.g. @p FE_Q - * classes). + * This function is used to delete @p FEValuesData that are needed only on + * the first cell but not any more afterwards. This is the case for + * e.g. Lagrangian elements (see e.g. @p FE_Q classes). */ void delete_fe_values_data (const unsigned int base_no); /** - * Set the @p first_cell - * flag to @p false. Used by - * the @p FEValues class to - * indicate that we have - * already done the work on - * the first cell. + * Set the @p first_cell flag to @p false. Used by the @p FEValues class + * to indicate that we have already done the work on the first cell. * - * In addition to calling the - * respective function of the - * base class, this function - * also calls the functions - * of the sub-data objects. + * In addition to calling the respective function of the base class, this + * function also calls the functions of the sub-data objects. */ virtual void clear_first_cell (); private: /** - * Pointers to - * @p InternalData objects - * for each of the base - * elements. They are - * accessed to by the - * @p set_ and - * @p get_fe_data functions. + * Pointers to @p InternalData objects for each of the base elements. They + * are accessed to by the @p set_ and @p get_fe_data functions. * - * The size of this vector is - * set to @p n_base_elements - * by the InternalData - * constructor. It is filled - * by the @p get_data - * function. Note that since - * the data for each instance - * of a base class is - * necessarily the same, we - * only need as many of these - * objects as there are base - * elements, irrespective of - * their multiplicity. + * The size of this vector is set to @p n_base_elements by the + * InternalData constructor. It is filled by the @p get_data + * function. Note that since the data for each instance of a base class is + * necessarily the same, we only need as many of these objects as there + * are base elements, irrespective of their multiplicity. */ typename std::vector::InternalDataBase *> base_fe_datas; /** - * Pointers to the - * @p FEValuesData objects - * that are given to the - * @p fill_fe_values - * function of the base - * elements. They are - * accessed to by the - * @p set_ and - * @p get_fe_values_data - * functions. + * Pointers to the @p FEValuesData objects that are given to the @p + * fill_fe_values function of the base elements. They are accessed to by + * the @p set_ and @p get_fe_values_data functions. * - * The size of this vector is - * set to @p n_base_elements - * by the InternalData - * constructor. + * The size of this vector is set to @p n_base_elements by the + * InternalData constructor. */ std::vector *> base_fe_values_datas; }; + + /* + * Mutex for protecting initialization of restriction and embedding matrix. + */ + mutable Threads::Mutex mutex; }; diff --git a/deal.II/include/deal.II/fe/fe_tools.h b/deal.II/include/deal.II/fe/fe_tools.h index 926eaa6e50..fe54359e3d 100644 --- a/deal.II/include/deal.II/fe/fe_tools.h +++ b/deal.II/include/deal.II/fe/fe_tools.h @@ -21,6 +21,8 @@ #include #include +#include + DEAL_II_NAMESPACE_OPEN @@ -35,19 +37,8 @@ namespace hp } template class FiniteElementData; class ConstraintMatrix; -namespace PETScWrappers -{ - namespace MPI - { - class Vector; - } -} -#include -#include -#include -#include /*!@addtogroup feall */ /*@{*/ @@ -546,7 +537,7 @@ namespace FETools * course Q1 on each cell. * * For this case (continuous elements on grids with hanging nodes), please - * use the @p interpolate function with an additional @p ConstraintMatrix + * use the @p interpolate() function with an additional ConstraintMatrix * argument, see below, or make the field conforming yourself by calling the * @p distribute function of your hanging node constraints object. */ @@ -573,8 +564,8 @@ namespace FETools * interpolation. The same is true if @p fe1 is a continuous and @p fe2 is a * discontinuous finite element. For the case that @p fe1 is a discontinuous * and @p fe2 is a continuous finite element there is no point interpolation - * defined at the discontinuities. Therefore the meanvalue is taken at the - * DoF values on the discontinuities. + * defined at the discontinuities. Therefore the mean value is taken at the + * DoF values at the discontinuities. */ template class DH1, diff --git a/deal.II/include/deal.II/fe/mapping_q.h b/deal.II/include/deal.II/fe/mapping_q.h index f32ecb3ea6..3e1ae95444 100644 --- a/deal.II/include/deal.II/fe/mapping_q.h +++ b/deal.II/include/deal.II/fe/mapping_q.h @@ -20,7 +20,7 @@ DEAL_II_NAMESPACE_OPEN -template class TensorProductPolynomials; +template class TensorProductPolynomials; /*!@addtogroup mapping */ diff --git a/deal.II/include/deal.II/grid/grid_in.h b/deal.II/include/deal.II/grid/grid_in.h index d18023c592..8a23c03805 100644 --- a/deal.II/include/deal.II/grid/grid_in.h +++ b/deal.II/include/deal.II/grid/grid_in.h @@ -32,13 +32,21 @@ struct SubCellData; * This class implements an input mechanism for grid data. It allows to read a * grid structure into a triangulation object. At present, UCD (unstructured * cell data), DB Mesh, XDA, Gmsh, Tecplot, NetCDF, UNV, and Cubit are supported as - * input format for grid data. Any numerical data after the block of geometric + * input format for grid data. Any numerical data other than geometric * (vertex locations) and topological (how vertices form cells) information is - * ignored. Notice also that at the moment in the codimension 1 case only UCD - * and Gmsh format are accepted. + * ignored. * - * Since the coarse mesh fed into a @p Triangulation object cannot have - * hanging nodes, strange things will happen if the input file has them. This + * @note Since deal.II only supports line, quadrilateral and hexahedral meshes, + * the functions in this class can only read meshes that consist exclusively + * of such cells. If you absolutely need to work with a mesh that uses triangles + * or tetrahedra, then your only option is to convert the mesh to quadrilaterals + * and hexahedra. A tool that can do this is tethex, see + * http://code.google.com/p/tethex/wiki/Tethex . + * + * The mesh you read will form the coarsest level of a @p Triangulation object. + * As such, it must not contain hanging nodes or other forms or adaptive + * refinement and strange things will happen if the mesh represented by the + * input file does in fact have them. This * is due to the fact that most mesh description formats do not store * neighborship information between cells, so the grid reading functions have * to regenerate it. They do so by checking whether two cells have a common @@ -46,13 +54,19 @@ struct SubCellData; * common (complete) face, so the grid reader concludes that the adjacent * cells have no neighbors along these faces and must therefore be at the * boundary. In effect, an internal crack of the domain is introduced this - * way. Since such cases are very hard to detect, the library does not make - * any attempt to abort on such situations, and you will get a triangulation - * that probably does not do what you want. A solution to this problem is the + * way. Since such cases are very hard to detect (how is GridIn supposed to + * decide whether a place where the faces of two small cells coincide with + * the face or a larger cell is in fact a hanging node associated with local + * refinement, or is indeed meant to be a crack in the domain?), the library + * does not make any attempt to catch such situations, and you will get a triangulation + * that probably does not do what you want. If your goal is to save and later + * read again a triangulation that has been adaptively refined, then this + * class is not your solution; rather take a look at the * PersistentTriangulation class. * - * Note: if you experience unexpected problems with the use of this - * class, be sure to read the documentation right until the end, and + * @note It is not uncommon to experience unexpected problems when reading + * generated meshes for the first time using this class. If this applies to + * you, be sure to read the documentation right until the end, and * also read the documentation of the GridReordering class. * * To read grid data, the triangulation to be fed with has to be empty. diff --git a/deal.II/include/deal.II/grid/grid_out.h b/deal.II/include/deal.II/grid/grid_out.h index 0382975532..01d778f595 100644 --- a/deal.II/include/deal.II/grid/grid_out.h +++ b/deal.II/include/deal.II/grid/grid_out.h @@ -634,7 +634,14 @@ namespace GridOutFlags Background background; // View angles for the perspective view of the grid; Default is 0, 0 (top view). - int azimuth_angle, polar_angle; + /** + * The azimuth angle measured from ??? in degrees. Default is 0. + */ + int azimuth_angle; + /** + * The angle from vertically above the xy-plane. Default is 0. + */ + int polar_angle; /** * Cell coloring. @@ -647,11 +654,13 @@ namespace GridOutFlags /// Convert the level number into the cell color level_number, /// Convert the subdomain id into the cell color - subdomain_id}; + subdomain_id, + /// Convert the level subdomain id into the cell color + level_subdomain_id}; Coloring coloring; - // Interpret the level number of the cells as altitude over the x-y-plane (may be useful in the perpspective view). + /// Interpret the level number of the cells as altitude over the x-y-plane (useful in the perpspective view). bool convert_level_number_to_height; /** @@ -660,11 +669,11 @@ namespace GridOutFlags * The following booleans determine which properties of the cell * shall be displayed as text in the middle of each cell. */ - bool label_level_number; /// default: true - bool label_cell_index; /// default: true - bool label_material_id; /// default: false - bool label_subdomain_id; /// default: false - //bool label_level_subdomain_id; // TODO [CW]: not yet implemented ... + bool label_level_number; // default: true + bool label_cell_index; // default: true + bool label_material_id; // default: false + bool label_subdomain_id; // default: false + bool label_level_subdomain_id; // default: false /// Draw a colorbar next to the plotted grid with respect to the chosen coloring of the cells bool draw_colorbar; @@ -674,13 +683,13 @@ namespace GridOutFlags /** * Constructor. */ - Svg(const unsigned int line_thickness = 3, - const unsigned int boundary_line_thickness = 7, + Svg(const unsigned int line_thickness = 2, + const unsigned int boundary_line_thickness = 4, bool margin = true, - const Background background = dealii, + const Background background = white, const int azimuth_angle = 0, const int polar_angle = 0, - const Coloring coloring = material_id, + const Coloring coloring = level_number, const bool convert_level_number_to_height = false, const bool label_level_number = true, const bool label_cell_index = true, @@ -1494,26 +1503,20 @@ private: /** - * This function projects a three-dimensional - * point (Point<3> point) onto a two-dimensional - * image plane, specified by the position of - * the camera viewing system - * (Point<3> camera_position), - * camera direction - * (Point<3> camera_position), - * camera horizontal - * (Point<3> camera_horizontal, necessary for - * the correct alignment of the later images), - * and the focus of the camera - * (float camera_focus). + * This function projects a three-dimensional point (Point<3> point) + * onto a two-dimensional image plane, specified by the position of + * the camera viewing system (Point<3> camera_position), camera + * direction (Point<3> camera_position), camera horizontal (Point<3> + * camera_horizontal, necessary for the correct alignment of the + * later images), and the focus of the camera (float camera_focus). * * For SVG output of grids. */ - Point<2> svg_project_point(Point<3> point, - Point<3> camera_position, - Point<3> camera_direction, - Point<3> camera_horizontal, - float camera_focus) const; + static Point<2> svg_project_point(Point<3> point, + Point<3> camera_position, + Point<3> camera_direction, + Point<3> camera_horizontal, + float camera_focus); /** * Return the number of faces in the triangulation which have a diff --git a/deal.II/include/deal.II/grid/tria_accessor.h b/deal.II/include/deal.II/grid/tria_accessor.h index a046301507..32db0f30fb 100644 --- a/deal.II/include/deal.II/grid/tria_accessor.h +++ b/deal.II/include/deal.II/grid/tria_accessor.h @@ -2832,11 +2832,7 @@ public: * * See the @ref GlossSubdomainId * "glossary" for more - * information. This function - * should not be called if you - * use a - * parallel::distributed::Triangulation - * object. + * information. */ types::subdomain_id subdomain_id () const; diff --git a/deal.II/include/deal.II/grid/tria_accessor.templates.h b/deal.II/include/deal.II/grid/tria_accessor.templates.h index bb1b20ec80..be5a6c8fb1 100644 --- a/deal.II/include/deal.II/grid/tria_accessor.templates.h +++ b/deal.II/include/deal.II/grid/tria_accessor.templates.h @@ -2265,6 +2265,7 @@ unsigned int TriaAccessor<0, 1, spacedim>::vertex_index (const unsigned int i) const { Assert(i==0, ExcIndexRange(i, 0, 1)); + (void)i; return global_vertex_index; } @@ -2276,6 +2277,7 @@ Point & TriaAccessor<0, 1, spacedim>::vertex (const unsigned int i) const { Assert(i==0, ExcIndexRange(i, 0, 1)); + (void)i; return const_cast &> (this->tria->vertices[global_vertex_index]); } diff --git a/deal.II/include/deal.II/lac/arpack_solver.h b/deal.II/include/deal.II/lac/arpack_solver.h index d0d1d6666e..2c25c8f3c2 100644 --- a/deal.II/include/deal.II/lac/arpack_solver.h +++ b/deal.II/include/deal.II/lac/arpack_solver.h @@ -15,6 +15,8 @@ #define __deal2__arpack_solver_h #include +#include +#include #ifdef DEAL_II_WITH_ARPACK @@ -479,7 +481,7 @@ void ArpackSolver::solve ( } else if (info == 3) { - Assert (false, ExcArpackNoShifts(1));; + Assert (false, ExcArpackNoShifts(1)); } else if (info!=0) { diff --git a/deal.II/include/deal.II/lac/block_matrix_base.h b/deal.II/include/deal.II/lac/block_matrix_base.h index 1f796a01fc..f98b791278 100644 --- a/deal.II/include/deal.II/lac/block_matrix_base.h +++ b/deal.II/include/deal.II/lac/block_matrix_base.h @@ -2521,13 +2521,9 @@ BlockMatrixBase::vmult_add (BlockVectorType &dst, ExcDimensionMismatch(src.n_blocks(), n_block_cols())); for (unsigned int row=0; row::Tvmult_add (BlockVectorType &dst, ExcDimensionMismatch(src.n_blocks(), n_block_rows())); for (unsigned int row=0; row::size () const +template +inline +IndexSet +BlockVectorBase::locally_owned_elements () const +{ + IndexSet is (size()); + + // copy index sets from blocks into the global one + for (unsigned int b=0; b inline unsigned int diff --git a/deal.II/include/deal.II/lac/chunk_sparse_matrix.h b/deal.II/include/deal.II/lac/chunk_sparse_matrix.h index 9c20d69fac..8ceb359cfa 100644 --- a/deal.II/include/deal.II/lac/chunk_sparse_matrix.h +++ b/deal.II/include/deal.II/lac/chunk_sparse_matrix.h @@ -29,6 +29,359 @@ template class FullMatrix; *@{ */ +/** + * A namespace in which we declare iterators over the elements of sparse + * matrices. + */ +namespace ChunkSparseMatrixIterators +{ + // forward declaration + template + class Iterator; + + /** + * General template for sparse matrix accessors. The first template argument + * denotes the underlying numeric type, the second the constness of the + * matrix. + * + * The general template is not implemented, only the specializations for the + * two possible values of the second template argument. Therefore, the + * interface listed here only serves as a template provided since doxygen + * does not link the specializations. + */ + template + class Accessor : public ChunkSparsityPatternIterators::Accessor + { + public: + /** + * Value of this matrix entry. + */ + number value() const; + + /** + * Value of this matrix entry. + */ + number &value(); + + /** + * Return a reference to the matrix into which this accessor points. Note + * that in the present case, this is a constant reference. + */ + const ChunkSparseMatrix &get_matrix () const; + }; + + + + /** + * Accessor class for constant matrices, used in the const_iterators. This + * class builds on the accessor classes used for sparsity patterns to loop + * over all nonzero entries, and only adds the accessor functions to gain + * access to the actual value stored at a certain location. + */ + template + class Accessor : public ChunkSparsityPatternIterators::Accessor + { + public: + /** + * Typedef for the type (including constness) of the matrix to be used + * here. + */ + typedef const ChunkSparseMatrix MatrixType; + + /** + * Constructor. + */ + Accessor (MatrixType *matrix, + const unsigned int row); + + /** + * Constructor. Construct the end accessor for the given matrix. + */ + Accessor (MatrixType *matrix); + + /** + * Copy constructor to get from a non-const accessor to a const accessor. + */ + Accessor (const ChunkSparseMatrixIterators::Accessor &a); + + /** + * Value of this matrix entry. + */ + number value() const; + + /** + * Return a reference to the matrix into which this accessor points. Note + * that in the present case, this is a constant reference. + */ + MatrixType &get_matrix () const; + + private: + /** + * Pointer to the matrix we use. + */ + MatrixType *matrix; + + /** + * Make the advance function of the base class available. + */ + using ChunkSparsityPatternIterators::Accessor::advance; + + /** + * Make iterator class a friend. + */ + template + friend class Iterator; + }; + + + /** + * Accessor class for non-constant matrices, used in the iterators. This + * class builds on the accessor classes used for sparsity patterns to loop + * over all nonzero entries, and only adds the accessor functions to gain + * access to the actual value stored at a certain location. + */ + template + class Accessor : public ChunkSparsityPatternIterators::Accessor + { + private: + /** + * Reference class. This is what the accessor class returns when you call + * the value() function. The reference acts just as if it were a reference + * to the actual value of a matrix entry, i.e. you can read and write it, + * you can add and multiply to it, etc, but since the matrix does not give + * away the address of this matrix entry, we have to go through functions + * to do all this. + * + * The constructor takes a pointer to an accessor object that describes + * which element of the matrix it points to. This creates an ambiguity + * when one writes code like iterator->value()=0 (instead of + * iterator->value()=0.0), since the right hand side is an integer that + * can both be converted to a number (i.e., most commonly a + * double) or to another object of type Reference. The compiler + * then complains about not knowing which conversion to take. + * + * For some reason, adding another overload operator=(int) doesn't seem to + * cure the problem. We avoid it, however, by adding a second, dummy + * argument to the Reference constructor, that is unused, but makes sure + * there is no second matching conversion sequence using a one-argument + * right hand side. + */ + class Reference + { + public: + /** + * Constructor. For the second argument, see the general class + * documentation. + */ + Reference (const Accessor *accessor, + const bool dummy); + + /** + * Conversion operator to the data type of the matrix. + */ + operator number () const; + + /** + * Set the element of the matrix we presently point to to @p n. + */ + const Reference &operator = (const number n) const; + + /** + * Add @p n to the element of the matrix we presently point to. + */ + const Reference &operator += (const number n) const; + + /** + * Subtract @p n from the element of the matrix we presently point to. + */ + const Reference &operator -= (const number n) const; + + /** + * Multiply the element of the matrix we presently point to by @p n. + */ + const Reference &operator *= (const number n) const; + + /** + * Divide the element of the matrix we presently point to by @p n. + */ + const Reference &operator /= (const number n) const; + + private: + /** + * Pointer to the accessor that denotes which element we presently point + * to. + */ + const Accessor *accessor; + }; + + public: + /** + * Typedef for the type (including constness) of the matrix to be used + * here. + */ + typedef ChunkSparseMatrix MatrixType; + + /** + * Constructor. + */ + Accessor (MatrixType *matrix, + const unsigned int row); + + /** + * Constructor. Construct the end accessor for the given matrix. + */ + Accessor (MatrixType *matrix); + + /** + * Value of this matrix entry, returned as a read- and writable reference. + */ + Reference value() const; + + /** + * Return a reference to the matrix into which this accessor points. Note + * that in the present case, this is a non-constant reference. + */ + MatrixType &get_matrix () const; + + private: + /** + * Pointer to the matrix we use. + */ + MatrixType *matrix; + + /** + * Make the advance function of the base class available. + */ + using ChunkSparsityPatternIterators::Accessor::advance; + + /** + * Make iterator class a friend. + */ + template + friend class Iterator; + + /** + * Make the inner reference class a friend if the compiler has a bug and + * requires this. + */ + }; + + + + /** + * STL conforming iterator for constant and non-constant matrices. + * + * The first template argument denotes the underlying numeric type, the + * second the constness of the matrix. + * + * Since there is a specialization of this class for + * Constness=false, this class is for iterators to constant + * matrices. + */ + template + class Iterator + { + public: + /** + * Typedef for the matrix type (including constness) we are to operate on. + */ + typedef + typename Accessor::MatrixType + MatrixType; + + /** + * A typedef for the type you get when you dereference an iterator + * of the current kind. + */ + typedef + const Accessor & value_type; + + /** + * Constructor. Create an iterator into the matrix @p matrix for the given + * row and the index within it. + */ + Iterator (MatrixType *matrix, + const unsigned int row); + + /** + * Constructor. Create the end iterator for the given matrix. + */ + Iterator (MatrixType *matrix); + + /** + * Conversion constructor to get from a non-const iterator to a const + * iterator. + */ + Iterator (const ChunkSparseMatrixIterators::Iterator &i); + + /** + * Prefix increment. + */ + Iterator &operator++ (); + + /** + * Postfix increment. + */ + Iterator operator++ (int); + + /** + * Dereferencing operator. + */ + const Accessor &operator* () const; + + /** + * Dereferencing operator. + */ + const Accessor *operator-> () const; + + /** + * Comparison. True, if both iterators point to the same matrix position. + */ + bool operator == (const Iterator &) const; + + /** + * Inverse of ==. + */ + bool operator != (const Iterator &) const; + + /** + * Comparison operator. Result is true if either the first row number is + * smaller or if the row numbers are equal and the first index is smaller. + * + * This function is only valid if both iterators point into the same + * matrix. + */ + bool operator < (const Iterator &) const; + + /** + * Comparison operator. Works in the same way as above operator, just the + * other way round. + */ + bool operator > (const Iterator &) const; + + /** + * Return the distance between the current iterator and the argument. + * The distance is given by how many times one has to apply operator++ + * to the current iterator to get the argument (for a positive return + * value), or operator-- (for a negative return value). + */ + int operator - (const Iterator &p) const; + + /** + * Return an iterator that is @p n ahead of the current one. + */ + Iterator operator + (const unsigned int n) const; + + private: + /** + * Store an object of the accessor class. + */ + Accessor accessor; + }; + +} + + /** * Sparse matrix. This class implements the function to store values @@ -56,42 +409,46 @@ public: typedef number value_type; /** - * Declare a type that has holds - * real-valued numbers with the - * same precision as the template - * argument to this class. If the - * template argument of this - * class is a real data type, - * then real_type equals the - * template argument. If the - * template argument is a - * std::complex type then - * real_type equals the type - * underlying the complex - * numbers. + * Declare a type that has holds real-valued numbers with the same precision + * as the template argument to this class. If the template argument of this + * class is a real data type, then real_type equals the template + * argument. If the template argument is a std::complex type then real_type + * equals the type underlying the complex numbers. * - * This typedef is used to - * represent the return type of - * norms. + * This typedef is used to represent the return type of norms. */ typedef typename numbers::NumberTraits::real_type real_type; /** - * A structure that describes some of the - * traits of this class in terms of its - * run-time behavior. Some other classes - * (such as the block matrix classes) - * that take one or other of the matrix - * classes as its template parameters can - * tune their behavior based on the - * variables in this class. + * Typedef of an STL conforming iterator class walking over all the nonzero + * entries of this matrix. This iterator cannot change the values of the + * matrix. + */ + typedef + ChunkSparseMatrixIterators::Iterator + const_iterator; + + /** + * Typedef of an STL conforming iterator class walking over all the nonzero + * entries of this matrix. This iterator @em can change the values of the + * matrix, but of course can't change the sparsity pattern as this is fixed + * once a sparse matrix is attached to it. + */ + typedef + ChunkSparseMatrixIterators::Iterator + iterator; + + /** + * A structure that describes some of the traits of this class in terms of + * its run-time behavior. Some other classes (such as the block matrix + * classes) that take one or other of the matrix classes as its template + * parameters can tune their behavior based on the variables in this class. */ struct Traits { /** - * It is safe to elide additions of - * zeros to individual elements of - * this matrix. + * It is safe to elide additions of zeros to individual elements of this + * matrix. */ static const bool zero_addition_can_be_elided = true; }; @@ -101,172 +458,106 @@ public: */ //@{ /** - * Constructor; initializes the matrix to - * be empty, without any structure, i.e. - * the matrix is not usable at all. This - * constructor is therefore only useful - * for matrices which are members of a - * class. All other matrices should be - * created at a point in the data flow - * where all necessary information is - * available. + * Constructor; initializes the matrix to be empty, without any structure, + * i.e. the matrix is not usable at all. This constructor is therefore only + * useful for matrices which are members of a class. All other matrices + * should be created at a point in the data flow where all necessary + * information is available. * - * You have to initialize - * the matrix before usage with - * reinit(const ChunkSparsityPattern&). + * You have to initialize the matrix before usage with reinit(const + * ChunkSparsityPattern&). */ ChunkSparseMatrix (); /** - * Copy constructor. This constructor is - * only allowed to be called if the matrix - * to be copied is empty. This is for the - * same reason as for the - * ChunkSparsityPattern, see there for the - * details. + * Copy constructor. This constructor is only allowed to be called if the + * matrix to be copied is empty. This is for the same reason as for the + * ChunkSparsityPattern, see there for the details. * - * If you really want to copy a whole - * matrix, you can do so by using the + * If you really want to copy a whole matrix, you can do so by using the * copy_from() function. */ ChunkSparseMatrix (const ChunkSparseMatrix &); /** - * Constructor. Takes the given - * matrix sparsity structure to - * represent the sparsity pattern - * of this matrix. You can change - * the sparsity pattern later on - * by calling the reinit(const - * ChunkSparsityPattern&) function. + * Constructor. Takes the given matrix sparsity structure to represent the + * sparsity pattern of this matrix. You can change the sparsity pattern + * later on by calling the reinit(const ChunkSparsityPattern&) function. * - * You have to make sure that the - * lifetime of the sparsity - * structure is at least as long - * as that of this matrix or as - * long as reinit(const - * ChunkSparsityPattern&) is not - * called with a new sparsity - * pattern. + * You have to make sure that the lifetime of the sparsity structure is at + * least as long as that of this matrix or as long as reinit(const + * ChunkSparsityPattern&) is not called with a new sparsity pattern. * - * The constructor is marked - * explicit so as to disallow - * that someone passes a sparsity - * pattern in place of a sparse - * matrix to some function, where - * an empty matrix would be - * generated then. + * The constructor is marked explicit so as to disallow that someone passes + * a sparsity pattern in place of a sparse matrix to some function, where an + * empty matrix would be generated then. */ explicit ChunkSparseMatrix (const ChunkSparsityPattern &sparsity); /** - * Copy constructor: initialize - * the matrix with the identity - * matrix. This constructor will - * throw an exception if the - * sizes of the sparsity pattern - * and the identity matrix do not - * coincide, or if the sparsity - * pattern does not provide for - * nonzero entries on the entire - * diagonal. + * Copy constructor: initialize the matrix with the identity matrix. This + * constructor will throw an exception if the sizes of the sparsity pattern + * and the identity matrix do not coincide, or if the sparsity pattern does + * not provide for nonzero entries on the entire diagonal. */ ChunkSparseMatrix (const ChunkSparsityPattern &sparsity, const IdentityMatrix &id); /** - * Destructor. Free all memory, but do not - * release the memory of the sparsity - * structure. + * Destructor. Free all memory, but do not release the memory of the + * sparsity structure. */ virtual ~ChunkSparseMatrix (); /** - * Copy operator. Since copying - * entire sparse matrices is a - * very expensive operation, we - * disallow doing so except for - * the special case of empty - * matrices of size zero. This - * doesn't seem particularly - * useful, but is exactly what - * one needs if one wanted to - * have a - * std::vector@ - * @>: in that case, one - * can create a vector (which - * needs the ability to copy - * objects) of empty matrices - * that are then later filled - * with something useful. + * Copy operator. Since copying entire sparse matrices is a very expensive + * operation, we disallow doing so except for the special case of empty + * matrices of size zero. This doesn't seem particularly useful, but is + * exactly what one needs if one wanted to have a + * std::vector@ @>: in that case, + * one can create a vector (which needs the ability to copy objects) of + * empty matrices that are then later filled with something useful. */ ChunkSparseMatrix &operator = (const ChunkSparseMatrix &); /** - * Copy operator: initialize - * the matrix with the identity - * matrix. This operator will - * throw an exception if the - * sizes of the sparsity pattern - * and the identity matrix do not - * coincide, or if the sparsity - * pattern does not provide for - * nonzero entries on the entire - * diagonal. + * Copy operator: initialize the matrix with the identity matrix. This + * operator will throw an exception if the sizes of the sparsity pattern and + * the identity matrix do not coincide, or if the sparsity pattern does not + * provide for nonzero entries on the entire diagonal. */ ChunkSparseMatrix & operator= (const IdentityMatrix &id); /** - * This operator assigns a scalar to - * a matrix. Since this does usually - * not make much sense (should we set - * all matrix entries to this value? - * Only the nonzero entries of the - * sparsity pattern?), this operation - * is only allowed if the actual - * value to be assigned is zero. This - * operator only exists to allow for - * the obvious notation - * matrix=0, which sets all - * elements of the matrix to zero, - * but keep the sparsity pattern + * This operator assigns a scalar to a matrix. Since this does usually not + * make much sense (should we set all matrix entries to this value? Only + * the nonzero entries of the sparsity pattern?), this operation is only + * allowed if the actual value to be assigned is zero. This operator only + * exists to allow for the obvious notation matrix=0, which sets + * all elements of the matrix to zero, but keep the sparsity pattern * previously used. */ ChunkSparseMatrix &operator = (const double d); /** - * Reinitialize the sparse matrix - * with the given sparsity - * pattern. The latter tells the - * matrix how many nonzero - * elements there need to be + * Reinitialize the sparse matrix with the given sparsity pattern. The + * latter tells the matrix how many nonzero elements there need to be * reserved. * - * Regarding memory allocation, - * the same applies as said - * above. + * Regarding memory allocation, the same applies as said above. * - * You have to make sure that the - * lifetime of the sparsity - * structure is at least as long - * as that of this matrix or as - * long as reinit(const - * ChunkSparsityPattern &) is not - * called with a new sparsity - * structure. + * You have to make sure that the lifetime of the sparsity structure is at + * least as long as that of this matrix or as long as reinit(const + * ChunkSparsityPattern &) is not called with a new sparsity structure. * - * The elements of the matrix are - * set to zero by this function. + * The elements of the matrix are set to zero by this function. */ virtual void reinit (const ChunkSparsityPattern &sparsity); /** - * Release all memory and return - * to a state just like after - * having called the default - * constructor. It also forgets - * the sparsity pattern it was + * Release all memory and return to a state just like after having called + * the default constructor. It also forgets the sparsity pattern it was * previously tied to. */ virtual void clear (); @@ -276,74 +567,52 @@ public: */ //@{ /** - * Return whether the object is - * empty. It is empty if either - * both dimensions are zero or no - * ChunkSparsityPattern is - * associated. + * Return whether the object is empty. It is empty if either both dimensions + * are zero or no ChunkSparsityPattern is associated. */ bool empty () const; /** - * Return the dimension of the - * image space. To remember: the - * matrix is of dimension - * $m \times n$. + * Return the dimension of the image space. To remember: the matrix is of + * dimension $m \times n$. */ unsigned int m () const; /** - * Return the dimension of the - * range space. To remember: the - * matrix is of dimension - * $m \times n$. + * Return the dimension of the range space. To remember: the matrix is of + * dimension $m \times n$. */ unsigned int n () const; /** - * Return the number of nonzero - * elements of this - * matrix. Actually, it returns - * the number of entries in the - * sparsity pattern; if any of - * the entries should happen to - * be zero, it is counted anyway. + * Return the number of nonzero elements of this matrix. Actually, it + * returns the number of entries in the sparsity pattern; if any of the + * entries should happen to be zero, it is counted anyway. */ unsigned int n_nonzero_elements () const; /** - * Return the number of actually - * nonzero elements of this - * matrix. + * Return the number of actually nonzero elements of this matrix. * - * Note, that this function does - * (in contrary to - * n_nonzero_elements()) not - * count all entries of the - * sparsity pattern but only the - * ones that are nonzero. + * Note, that this function does (in contrary to n_nonzero_elements()) not + * count all entries of the sparsity pattern but only the ones that are + * nonzero. */ unsigned int n_actually_nonzero_elements () const; /** - * Return a (constant) reference - * to the underlying sparsity - * pattern of this matrix. + * Return a (constant) reference to the underlying sparsity pattern of this + * matrix. * - * Though the return value is - * declared const, you - * should be aware that it may - * change if you call any - * nonconstant function of - * objects which operate on it. + * Though the return value is declared const, you should be aware + * that it may change if you call any nonconstant function of objects which + * operate on it. */ const ChunkSparsityPattern &get_sparsity_pattern () const; /** - * Determine an estimate for the - * memory consumption (in bytes) - * of this object. See - * MemoryConsumption. + * Determine an estimate for the memory consumption (in bytes) of this + * object. See MemoryConsumption. */ std::size_t memory_consumption () const; @@ -353,47 +622,31 @@ public: */ //@{ /** - * Set the element (i,j) - * to value. Throws an - * error if the entry does not - * exist or if value is - * not a finite number. Still, it - * is allowed to store zero - * values in non-existent fields. + * Set the element (i,j) to value. Throws an error if the + * entry does not exist or if value is not a finite number. Still, + * it is allowed to store zero values in non-existent fields. */ void set (const unsigned int i, const unsigned int j, const number value); /** - * Add value to the - * element (i,j). Throws - * an error if the entry does not - * exist or if value is - * not a finite number. Still, it - * is allowed to store zero - * values in non-existent fields. + * Add value to the element (i,j). Throws an error if the + * entry does not exist or if value is not a finite number. Still, + * it is allowed to store zero values in non-existent fields. */ void add (const unsigned int i, const unsigned int j, const number value); /** - * Add an array of values given by - * values in the given - * global matrix row at columns - * specified by col_indices in the - * sparse matrix. + * Add an array of values given by values in the given global + * matrix row at columns specified by col_indices in the sparse matrix. * - * The optional parameter - * elide_zero_values can be - * used to specify whether zero - * values should be added anyway or - * these should be filtered away and - * only non-zero data is added. The - * default value is true, - * i.e., zero values won't be added - * into the matrix. + * The optional parameter elide_zero_values can be used to specify + * whether zero values should be added anyway or these should be filtered + * away and only non-zero data is added. The default value is true, + * i.e., zero values won't be added into the matrix. */ template void add (const unsigned int row, @@ -404,140 +657,84 @@ public: const bool col_indices_are_sorted = false); /** - * Multiply the entire matrix by a - * fixed factor. + * Multiply the entire matrix by a fixed factor. */ ChunkSparseMatrix &operator *= (const number factor); /** - * Divide the entire matrix by a - * fixed factor. + * Divide the entire matrix by a fixed factor. */ ChunkSparseMatrix &operator /= (const number factor); /** - * Symmetrize the matrix by - * forming the mean value between - * the existing matrix and its - * transpose, $A = \frac 12(A+A^T)$. + * Symmetrize the matrix by forming the mean value between the existing + * matrix and its transpose, $A = \frac 12(A+A^T)$. * - * This operation assumes that - * the underlying sparsity - * pattern represents a symmetric - * object. If this is not the - * case, then the result of this - * operation will not be a - * symmetric matrix, since it - * only explicitly symmetrizes - * by looping over the lower left - * triangular part for efficiency - * reasons; if there are entries - * in the upper right triangle, - * then these elements are missed - * in the - * symmetrization. Symmetrization - * of the sparsity pattern can be - * obtain by - * ChunkSparsityPattern::symmetrize(). + * This operation assumes that the underlying sparsity pattern represents a + * symmetric object. If this is not the case, then the result of this + * operation will not be a symmetric matrix, since it only explicitly + * symmetrizes by looping over the lower left triangular part for efficiency + * reasons; if there are entries in the upper right triangle, then these + * elements are missed in the symmetrization. Symmetrization of the sparsity + * pattern can be obtain by ChunkSparsityPattern::symmetrize(). */ void symmetrize (); /** - * Copy the given matrix to this - * one. The operation throws an - * error if the sparsity patterns - * of the two involved matrices - * do not point to the same - * object, since in this case the - * copy operation is - * cheaper. Since this operation - * is notheless not for free, we - * do not make it available - * through operator =, - * since this may lead to - * unwanted usage, e.g. in copy - * arguments to functions, which - * should really be arguments by - * reference. + * Copy the given matrix to this one. The operation throws an error if the + * sparsity patterns of the two involved matrices do not point to the same + * object, since in this case the copy operation is cheaper. Since this + * operation is notheless not for free, we do not make it available through + * operator =, since this may lead to unwanted usage, e.g. in copy + * arguments to functions, which should really be arguments by reference. * - * The source matrix may be a matrix - * of arbitrary type, as long as its - * data type is convertible to the - * data type of this matrix. + * The source matrix may be a matrix of arbitrary type, as long as its data + * type is convertible to the data type of this matrix. * - * The function returns a reference to - * *this. + * The function returns a reference to *this. */ template ChunkSparseMatrix & copy_from (const ChunkSparseMatrix &source); /** - * This function is complete - * analogous to the - * ChunkSparsityPattern::copy_from() - * function in that it allows to - * initialize a whole matrix in - * one step. See there for more - * information on argument types - * and their meaning. You can - * also find a small example on - * how to use this function - * there. + * This function is complete analogous to the + * ChunkSparsityPattern::copy_from() function in that it allows to + * initialize a whole matrix in one step. See there for more information on + * argument types and their meaning. You can also find a small example on + * how to use this function there. * - * The only difference to the - * cited function is that the - * objects which the inner - * iterator points to need to be - * of type std::pair, where - * value needs to be - * convertible to the element - * type of this class, as - * specified by the - * number template + * The only difference to the cited function is that the objects which the + * inner iterator points to need to be of type std::pair, where value needs to be convertible to the element + * type of this class, as specified by the number template * argument. * - * Previous content of the matrix - * is overwritten. Note that the - * entries specified by the input - * parameters need not - * necessarily cover all elements - * of the matrix. Elements not - * covered remain untouched. + * Previous content of the matrix is overwritten. Note that the entries + * specified by the input parameters need not necessarily cover all elements + * of the matrix. Elements not covered remain untouched. */ template void copy_from (const ForwardIterator begin, const ForwardIterator end); /** - * Copy the nonzero entries of a - * full matrix into this - * object. Previous content is - * deleted. Note that the - * underlying sparsity pattern - * must be appropriate to hold - * the nonzero entries of the - * full matrix. + * Copy the nonzero entries of a full matrix into this object. Previous + * content is deleted. Note that the underlying sparsity pattern must be + * appropriate to hold the nonzero entries of the full matrix. */ template void copy_from (const FullMatrix &matrix); /** - * Add matrix scaled by - * factor to this matrix, - * i.e. the matrix factor*matrix - * is added to this. This - * function throws an error if the - * sparsity patterns of the two involved - * matrices do not point to the same - * object, since in this case the - * operation is cheaper. + * Add matrix scaled by factor to this matrix, i.e. the + * matrix factor*matrix is added to this. This function + * throws an error if the sparsity patterns of the two involved matrices do + * not point to the same object, since in this case the operation is + * cheaper. * - * The source matrix may be a sparse - * matrix over an arbitrary underlying - * scalar type, as long as its data type - * is convertible to the data type of + * The source matrix may be a sparse matrix over an arbitrary underlying + * scalar type, as long as its data type is convertible to the data type of * this matrix. */ template @@ -551,54 +748,31 @@ public: //@{ /** - * Return the value of the entry - * (i,j). This may be an - * expensive operation and you - * should always take care where - * to call this function. In - * order to avoid abuse, this - * function throws an exception - * if the required element does - * not exist in the matrix. + * Return the value of the entry (i,j). This may be an expensive + * operation and you should always take care where to call this function. + * In order to avoid abuse, this function throws an exception if the + * required element does not exist in the matrix. * - * In case you want a function - * that returns zero instead (for - * entries that are not in the - * sparsity pattern of the - * matrix), use the el() - * function. + * In case you want a function that returns zero instead (for entries that + * are not in the sparsity pattern of the matrix), use the el() function. * - * If you are looping over all elements, - * consider using one of the iterator - * classes instead, since they are - * tailored better to a sparse matrix + * If you are looping over all elements, consider using one of the iterator + * classes instead, since they are tailored better to a sparse matrix * structure. */ number operator () (const unsigned int i, const unsigned int j) const; /** - * This function is mostly like - * operator()() in that it - * returns the value of the - * matrix entry (i,j). The - * only difference is that if - * this entry does not exist in - * the sparsity pattern, then - * instead of raising an - * exception, zero is - * returned. While this may be - * convenient in some cases, note - * that it is simple to write - * algorithms that are slow - * compared to an optimal - * solution, since the sparsity - * of the matrix is not used. + * This function is mostly like operator()() in that it returns the value of + * the matrix entry (i,j). The only difference is that if this entry + * does not exist in the sparsity pattern, then instead of raising an + * exception, zero is returned. While this may be convenient in some cases, + * note that it is simple to write algorithms that are slow compared to an + * optimal solution, since the sparsity of the matrix is not used. * - * If you are looping over all elements, - * consider using one of the iterator - * classes instead, since they are - * tailored better to a sparse matrix + * If you are looping over all elements, consider using one of the iterator + * classes instead, since they are tailored better to a sparse matrix * structure. */ number el (const unsigned int i, @@ -611,21 +785,16 @@ public: * error if the matrix is not * quadratic. * - * This function is considerably - * faster than the operator()(), - * since for quadratic matrices, the - * diagonal entry may be the - * first to be stored in each row - * and access therefore does not - * involve searching for the - * right column number. + * This function is considerably faster than the operator()(), since for + * quadratic matrices, the diagonal entry may be the first to be stored in + * each row and access therefore does not involve searching for the right + * column number. */ number diag_element (const unsigned int i) const; /** - * Same as above, but return a - * writeable reference. You're - * sure you know what you do? + * Same as above, but return a writeable reference. You're sure you know + * what you do? */ number &diag_element (const unsigned int i); @@ -635,166 +804,105 @@ public: */ //@{ /** - * Matrix-vector multiplication: - * let dst = M*src with - * M being this matrix. + * Matrix-vector multiplication: let dst = M*src with M being + * this matrix. * - * Note that while this function can - * operate on all vectors that offer - * iterator classes, it is only really - * effective for objects of type @ref - * Vector. For all classes for which - * iterating over elements, or random - * member access is expensive, this - * function is not efficient. In - * particular, if you want to multiply - * with BlockVector objects, you should - * consider using a BlockChunkSparseMatrix as - * well. + * Note that while this function can operate on all vectors that offer + * iterator classes, it is only really effective for objects of type @ref + * Vector. For all classes for which iterating over elements, or random + * member access is expensive, this function is not efficient. In + * particular, if you want to multiply with BlockVector objects, you should + * consider using a BlockChunkSparseMatrix as well. * - * Source and destination must - * not be the same vector. + * Source and destination must not be the same vector. */ template void vmult (OutVector &dst, const InVector &src) const; /** - * Matrix-vector multiplication: - * let dst = MT*src with - * M being this - * matrix. This function does the - * same as vmult() but takes - * the transposed matrix. + * Matrix-vector multiplication: let dst = MT*src with + * M being this matrix. This function does the same as vmult() but + * takes the transposed matrix. * - * Note that while this function can - * operate on all vectors that offer - * iterator classes, it is only really - * effective for objects of type @ref - * Vector. For all classes for which - * iterating over elements, or random - * member access is expensive, this - * function is not efficient. In - * particular, if you want to multiply - * with BlockVector objects, you should - * consider using a BlockChunkSparseMatrix as - * well. + * Note that while this function can operate on all vectors that offer + * iterator classes, it is only really effective for objects of type @ref + * Vector. For all classes for which iterating over elements, or random + * member access is expensive, this function is not efficient. In + * particular, if you want to multiply with BlockVector objects, you should + * consider using a BlockChunkSparseMatrix as well. * - * Source and destination must - * not be the same vector. + * Source and destination must not be the same vector. */ template void Tvmult (OutVector &dst, const InVector &src) const; /** - * Adding Matrix-vector - * multiplication. Add - * M*src on dst - * with M being this - * matrix. + * Adding Matrix-vector multiplication. Add M*src on dst with + * M being this matrix. * - * Note that while this function can - * operate on all vectors that offer - * iterator classes, it is only really - * effective for objects of type @ref - * Vector. For all classes for which - * iterating over elements, or random - * member access is expensive, this - * function is not efficient. In - * particular, if you want to multiply - * with BlockVector objects, you should - * consider using a BlockChunkSparseMatrix as - * well. + * Note that while this function can operate on all vectors that offer + * iterator classes, it is only really effective for objects of type @ref + * Vector. For all classes for which iterating over elements, or random + * member access is expensive, this function is not efficient. In + * particular, if you want to multiply with BlockVector objects, you should + * consider using a BlockChunkSparseMatrix as well. * - * Source and destination must - * not be the same vector. + * Source and destination must not be the same vector. */ template void vmult_add (OutVector &dst, const InVector &src) const; /** - * Adding Matrix-vector - * multiplication. Add - * MT*src to - * dst with M being - * this matrix. This function - * does the same as vmult_add() - * but takes the transposed - * matrix. + * Adding Matrix-vector multiplication. Add MT*src to + * dst with M being this matrix. This function does the same + * as vmult_add() but takes the transposed matrix. * - * Note that while this function can - * operate on all vectors that offer - * iterator classes, it is only really - * effective for objects of type @ref - * Vector. For all classes for which - * iterating over elements, or random - * member access is expensive, this - * function is not efficient. In - * particular, if you want to multiply - * with BlockVector objects, you should - * consider using a BlockChunkSparseMatrix as - * well. + * Note that while this function can operate on all vectors that offer + * iterator classes, it is only really effective for objects of type @ref + * Vector. For all classes for which iterating over elements, or random + * member access is expensive, this function is not efficient. In + * particular, if you want to multiply with BlockVector objects, you should + * consider using a BlockChunkSparseMatrix as well. * - * Source and destination must - * not be the same vector. + * Source and destination must not be the same vector. */ template void Tvmult_add (OutVector &dst, const InVector &src) const; /** - * Return the square of the norm - * of the vector $v$ with respect - * to the norm induced by this - * matrix, - * i.e. $\left(v,Mv\right)$. This - * is useful, e.g. in the finite - * element context, where the - * $L_2$ norm of a function - * equals the matrix norm with - * respect to the mass matrix of - * the vector representing the - * nodal values of the finite - * element function. + * Return the square of the norm of the vector $v$ with respect to the norm + * induced by this matrix, i.e. $\left(v,Mv\right)$. This is useful, e.g. in + * the finite element context, where the $L_2$ norm of a function equals the + * matrix norm with respect to the mass matrix of the vector representing + * the nodal values of the finite element function. * - * Obviously, the matrix needs to be - * quadratic for this operation, and for - * the result to actually be a norm it - * also needs to be either real symmetric - * or complex hermitian. + * Obviously, the matrix needs to be quadratic for this operation, and for + * the result to actually be a norm it also needs to be either real + * symmetric or complex hermitian. * - * The underlying template types of both - * this matrix and the given vector - * should either both be real or - * complex-valued, but not mixed, for - * this function to make sense. + * The underlying template types of both this matrix and the given vector + * should either both be real or complex-valued, but not mixed, for this + * function to make sense. */ template somenumber matrix_norm_square (const Vector &v) const; /** - * Compute the matrix scalar - * product $\left(u,Mv\right)$. + * Compute the matrix scalar product $\left(u,Mv\right)$. */ template somenumber matrix_scalar_product (const Vector &u, const Vector &v) const; /** - * Compute the residual of an - * equation Mx=b, where - * the residual is defined to be - * r=b-Mx. Write the - * residual into - * dst. The - * l2 norm of - * the residual vector is - * returned. + * Compute the residual of an equation Mx=b, where the residual is + * defined to be r=b-Mx. Write the residual into dst. The + * l2 norm of the residual vector is returned. * - * Source x and destination - * dst must not be the same - * vector. + * Source x and destination dst must not be the same vector. */ template somenumber residual (Vector &dst, @@ -808,37 +916,26 @@ public: //@{ /** - * Return the l1-norm of the matrix, that is - * $|M|_1=max_{all columns j}\sum_{all - * rows i} |M_ij|$, - * (max. sum of columns). - * This is the - * natural matrix norm that is compatible - * to the l1-norm for vectors, i.e. - * $|Mv|_1\leq |M|_1 |v|_1$. - * (cf. Haemmerlin-Hoffmann : Numerische Mathematik) + * Return the l1-norm of the matrix, that is $|M|_1=max_{all columns + * j}\sum_{all rows i} |M_ij|$, (max. sum of columns). This is the natural + * matrix norm that is compatible to the l1-norm for vectors, i.e. + * $|Mv|_1\leq |M|_1 |v|_1$. (cf. Haemmerlin-Hoffmann : Numerische + * Mathematik) */ real_type l1_norm () const; /** - * Return the linfty-norm of the - * matrix, that is - * $|M|_infty=max_{all rows i}\sum_{all - * columns j} |M_ij|$, - * (max. sum of rows). - * This is the - * natural matrix norm that is compatible - * to the linfty-norm of vectors, i.e. - * $|Mv|_infty \leq |M|_infty |v|_infty$. - * (cf. Haemmerlin-Hoffmann : Numerische Mathematik) + * Return the linfty-norm of the matrix, that is $|M|_infty=max_{all rows + * i}\sum_{all columns j} |M_ij|$, (max. sum of rows). This is the natural + * matrix norm that is compatible to the linfty-norm of vectors, i.e. + * $|Mv|_infty \leq |M|_infty |v|_infty$. (cf. Haemmerlin-Hoffmann : + * Numerische Mathematik) */ real_type linfty_norm () const; /** - * Return the frobenius norm of the - * matrix, i.e. the square root of the - * sum of squares of all entries in the - * matrix. + * Return the frobenius norm of the matrix, i.e. the square root of the sum + * of squares of all entries in the matrix. */ real_type frobenius_norm () const; //@} @@ -848,14 +945,9 @@ public: //@{ /** - * Apply the Jacobi - * preconditioner, which - * multiplies every element of - * the src vector by the - * inverse of the respective - * diagonal element and - * multiplies the result with the - * relaxation factor omega. + * Apply the Jacobi preconditioner, which multiplies every element of the + * src vector by the inverse of the respective diagonal element and + * multiplies the result with the relaxation factor omega. */ template void precondition_Jacobi (Vector &dst, @@ -863,8 +955,7 @@ public: const number omega = 1.) const; /** - * Apply SSOR preconditioning to - * src. + * Apply SSOR preconditioning to src. */ template void precondition_SSOR (Vector &dst, @@ -872,8 +963,7 @@ public: const number om = 1.) const; /** - * Apply SOR preconditioning - * matrix to src. + * Apply SOR preconditioning matrix to src. */ template void precondition_SOR (Vector &dst, @@ -881,9 +971,7 @@ public: const number om = 1.) const; /** - * Apply transpose SOR - * preconditioning matrix to - * src. + * Apply transpose SOR preconditioning matrix to src. */ template void precondition_TSOR (Vector &dst, @@ -891,11 +979,8 @@ public: const number om = 1.) const; /** - * Perform SSOR preconditioning - * in-place. Apply the - * preconditioner matrix without - * copying to a second vector. - * omega is the relaxation + * Perform SSOR preconditioning in-place. Apply the preconditioner matrix + * without copying to a second vector. omega is the relaxation * parameter. */ template @@ -903,18 +988,15 @@ public: const number omega = 1.) const; /** - * Perform an SOR preconditioning - * in-place. omega is - * the relaxation parameter. + * Perform an SOR preconditioning in-place. omega is the + * relaxation parameter. */ template void SOR (Vector &v, const number om = 1.) const; /** - * Perform a transpose SOR - * preconditioning in-place. - * omega is the + * Perform a transpose SOR preconditioning in-place. omega is the * relaxation parameter. */ template @@ -922,21 +1004,14 @@ public: const number om = 1.) const; /** - * Perform a permuted SOR - * preconditioning in-place. + * Perform a permuted SOR preconditioning in-place. * - * The standard SOR method is - * applied in the order - * prescribed by permutation, - * that is, first the row - * permutation[0], then - * permutation[1] and so - * on. For efficiency reasons, - * the permutation as well as its - * inverse are required. + * The standard SOR method is applied in the order prescribed by + * permutation, that is, first the row permutation[0], + * then permutation[1] and so on. For efficiency reasons, the + * permutation as well as its inverse are required. * - * omega is the - * relaxation parameter. + * omega is the relaxation parameter. */ template void PSOR (Vector &v, @@ -945,24 +1020,14 @@ public: const number om = 1.) const; /** - * Perform a transposed permuted SOR - * preconditioning in-place. + * Perform a transposed permuted SOR preconditioning in-place. * - * The transposed SOR method is - * applied in the order - * prescribed by - * permutation, that is, - * first the row - * permutation[m()-1], - * then - * permutation[m()-2] - * and so on. For efficiency - * reasons, the permutation as - * well as its inverse are - * required. + * The transposed SOR method is applied in the order prescribed by + * permutation, that is, first the row permutation[m()-1], + * then permutation[m()-2] and so on. For efficiency reasons, the + * permutation as well as its inverse are required. * - * omega is the - * relaxation parameter. + * omega is the relaxation parameter. */ template void TPSOR (Vector &v, @@ -971,10 +1036,8 @@ public: const number om = 1.) const; /** - * Do one SOR step on v. - * Performs a direct SOR step - * with right hand side - * b. + * Do one SOR step on v. Performs a direct SOR step with right + * hand side b. */ template void SOR_step (Vector &v, @@ -982,10 +1045,8 @@ public: const number om = 1.) const; /** - * Do one adjoint SOR step on - * v. Performs a direct - * TSOR step with right hand side - * b. + * Do one adjoint SOR step on v. Performs a direct TSOR step with + * right hand side b. */ template void TSOR_step (Vector &v, @@ -993,16 +1054,124 @@ public: const number om = 1.) const; /** - * Do one SSOR step on - * v. Performs a direct - * SSOR step with right hand side - * b by performing TSOR - * after SOR. + * Do one SSOR step on v. Performs a direct SSOR step with right + * hand side b by performing TSOR after SOR. */ template void SSOR_step (Vector &v, const Vector &b, const number om = 1.) const; +//@} + /** + * @name Iterators + */ +//@{ + + /** + * STL-like iterator with the first entry of the matrix. This is the version + * for constant matrices. + * + * Note that due to the layout in ChunkSparseMatrix, iterating over matrix + * entries is considerably slower than for a sparse matrix, as the iterator + * is travels row-by-row, whereas data is stored in chunks of several rows + * and columns. + */ + const_iterator begin () const; + + /** + * Final iterator. This is the version for constant matrices. + * + * Note that due to the layout in ChunkSparseMatrix, iterating over matrix + * entries is considerably slower than for a sparse matrix, as the iterator + * is travels row-by-row, whereas data is stored in chunks of several rows + * and columns. + */ + const_iterator end () const; + + /** + * STL-like iterator with the first entry of the matrix. This is the version + * for non-constant matrices. + * + * Note that due to the layout in ChunkSparseMatrix, iterating over matrix + * entries is considerably slower than for a sparse matrix, as the iterator + * is travels row-by-row, whereas data is stored in chunks of several rows + * and columns. + */ + iterator begin (); + + /** + * Final iterator. This is the version for non-constant matrices. + * + * Note that due to the layout in ChunkSparseMatrix, iterating over matrix + * entries is considerably slower than for a sparse matrix, as the iterator + * is travels row-by-row, whereas data is stored in chunks of several rows + * and columns. + */ + iterator end (); + + /** + * STL-like iterator with the first entry of row r. This is the + * version for constant matrices. + * + * Note that if the given row is empty, i.e. does not contain any nonzero + * entries, then the iterator returned by this function equals + * end(r). Note also that the iterator may not be dereferencable in + * that case. + * + * Note that due to the layout in ChunkSparseMatrix, iterating over matrix + * entries is considerably slower than for a sparse matrix, as the iterator + * is travels row-by-row, whereas data is stored in chunks of several rows + * and columns. + */ + const_iterator begin (const unsigned int r) const; + + /** + * Final iterator of row r. It points to the first element past the + * end of line @p r, or past the end of the entire sparsity pattern. This is + * the version for constant matrices. + * + * Note that the end iterator is not necessarily dereferencable. This is in + * particular the case if it is the end iterator for the last row of a + * matrix. + * + * Note that due to the layout in ChunkSparseMatrix, iterating over matrix + * entries is considerably slower than for a sparse matrix, as the iterator + * is travels row-by-row, whereas data is stored in chunks of several rows + * and columns. + */ + const_iterator end (const unsigned int r) const; + + /** + * STL-like iterator with the first entry of row r. This is the + * version for non-constant matrices. + * + * Note that if the given row is empty, i.e. does not contain any nonzero + * entries, then the iterator returned by this function equals + * end(r). Note also that the iterator may not be dereferencable in + * that case. + * + * Note that due to the layout in ChunkSparseMatrix, iterating over matrix + * entries is considerably slower than for a sparse matrix, as the iterator + * is travels row-by-row, whereas data is stored in chunks of several rows + * and columns. + */ + iterator begin (const unsigned int r); + + /** + * Final iterator of row r. It points to the first element past the + * end of line @p r, or past the end of the entire sparsity pattern. This is + * the version for non-constant matrices. + * + * Note that the end iterator is not necessarily dereferencable. This is in + * particular the case if it is the end iterator for the last row of a + * matrix. + * + * Note that due to the layout in ChunkSparseMatrix, iterating over matrix + * entries is considerably slower than for a sparse matrix, as the iterator + * is travels row-by-row, whereas data is stored in chunks of several rows + * and columns. + */ + iterator end (const unsigned int r); //@} /** * @name Input/Output @@ -1010,54 +1179,30 @@ public: //@{ /** - * Print the matrix to the given - * stream, using the format - * (line,col) value, - * i.e. one nonzero entry of the - * matrix per line. + * Print the matrix to the given stream, using the format (line,col) + * value, i.e. one nonzero entry of the matrix per line. */ void print (std::ostream &out) const; /** - * Print the matrix in the usual - * format, i.e. as a matrix and - * not as a list of nonzero - * elements. For better - * readability, elements not in - * the matrix are displayed as - * empty space, while matrix - * elements which are explicitly - * set to zero are displayed as - * such. + * Print the matrix in the usual format, i.e. as a matrix and not as a list + * of nonzero elements. For better readability, elements not in the matrix + * are displayed as empty space, while matrix elements which are explicitly + * set to zero are displayed as such. * - * The parameters allow for a - * flexible setting of the output - * format: precision and - * scientific are used - * to determine the number - * format, where scientific = - * false means fixed point - * notation. A zero entry for - * width makes the - * function compute a width, but - * it may be changed to a - * positive value, if output is - * crude. + * The parameters allow for a flexible setting of the output format: + * precision and scientific are used to determine the + * number format, where scientific = false means fixed point + * notation. A zero entry for width makes the function compute a + * width, but it may be changed to a positive value, if output is crude. * - * Additionally, a character for - * an empty value may be - * specified. + * Additionally, a character for an empty value may be specified. * - * Finally, the whole matrix can - * be multiplied with a common - * denominator to produce more - * readable output, even - * integers. + * Finally, the whole matrix can be multiplied with a common denominator to + * produce more readable output, even integers. * - * @attention This function may - * produce large amounts - * of output if applied to a - * large matrix! + * @attention This function may produce large amounts of output if + * applied to a large matrix! */ void print_formatted (std::ostream &out, const unsigned int precision = 3, @@ -1067,69 +1212,40 @@ public: const double denominator = 1.) const; /** - * Print the actual pattern of - * the matrix. For each entry - * with an absolute value larger - * than threshold, a '*' is - * printed, a ':' for every value - * smaller and a '.' for every - * entry not allocated. + * Print the actual pattern of the matrix. For each entry with an absolute + * value larger than threshold, a '*' is printed, a ':' for every value + * smaller and a '.' for every entry not allocated. */ void print_pattern(std::ostream &out, const double threshold = 0.) const; /** - * Write the data of this object - * en bloc to a file. This is - * done in a binary mode, so the - * output is neither readable by - * humans nor (probably) by other - * computers using a different - * operating system of number - * format. + * Write the data of this object en bloc to a file. This is done in a binary + * mode, so the output is neither readable by humans nor (probably) by other + * computers using a different operating system or number format. * - * The purpose of this function - * is that you can swap out - * matrices and sparsity pattern - * if you are short of memory, - * want to communicate between - * different programs, or allow - * objects to be persistent - * across different runs of the - * program. + * The purpose of this function is that you can swap out matrices and + * sparsity pattern if you are short of memory, want to communicate between + * different programs, or allow objects to be persistent across different + * runs of the program. */ void block_write (std::ostream &out) const; /** - * Read data that has previously - * been written by block_write() - * from a file. This is done - * using the inverse operations - * to the above function, so it - * is reasonably fast because the - * bitstream is not interpreted - * except for a few numbers up - * front. + * Read data that has previously been written by block_write() from a + * file. This is done using the inverse operations to the above function, so + * it is reasonably fast because the bitstream is not interpreted except for + * a few numbers up front. * - * The object is resized on this - * operation, and all previous - * contents are lost. Note, - * however, that no checks are - * performed whether new data and - * the underlying ChunkSparsityPattern - * object fit together. It is - * your responsibility to make - * sure that the sparsity pattern - * and the data to be read match. + * The object is resized on this operation, and all previous contents are + * lost. Note, however, that no checks are performed whether new data and + * the underlying ChunkSparsityPattern object fit together. It is your + * responsibility to make sure that the sparsity pattern and the data to be + * read match. * - * A primitive form of error - * checking is performed which - * will recognize the bluntest - * attempts to interpret some - * data as a matrix stored - * bitwise to a file that wasn't - * actually created that way, but - * not more. + * A primitive form of error checking is performed which will recognize the + * bluntest attempts to interpret some data as a matrix stored bitwise to a + * file that wasn't actually created that way, but not more. */ void block_read (std::istream &in); //@} @@ -1167,53 +1283,43 @@ public: //@} private: /** - * Pointer to the sparsity - * pattern used for this - * matrix. In order to guarantee - * that it is not deleted while - * still in use, we subscribe to - * it using the SmartPointer - * class. + * Pointer to the sparsity pattern used for this matrix. In order to + * guarantee that it is not deleted while still in use, we subscribe to it + * using the SmartPointer class. */ SmartPointer > cols; /** - * Array of values for all the - * nonzero entries. The position - * within the matrix, i.e. the - * row and column number for a - * given entry can only be - * deduced using the sparsity - * pattern. The same holds for - * the more common operation of - * finding an entry by its - * coordinates. + * Array of values for all the nonzero entries. The position within the + * matrix, i.e. the row and column number for a given entry can only be + * deduced using the sparsity pattern. The same holds for the more common + * operation of finding an entry by its coordinates. */ number *val; /** - * Allocated size of #val. This - * can be larger than the - * actually used part if the size - * of the matrix was reduced - * somewhen in the past by - * associating a sparsity pattern - * with a smaller size to this - * object, using the reinit() + * Allocated size of #val. This can be larger than the actually used part if + * the size of the matrix was reduced somewhen in the past by associating a + * sparsity pattern with a smaller size to this object, using the reinit() * function. */ unsigned int max_len; /** - * Return the location of entry - * $(i,j)$ within the val array. + * Return the location of entry $(i,j)$ within the val array. */ unsigned int compute_location (const unsigned int i, const unsigned int j) const; - // make all other sparse matrices - // friends + // make all other sparse matrices friends template friend class ChunkSparseMatrix; + + /** + * Also give access to internal details to the iterator/accessor + * classes. + */ + template friend class ChunkSparseMatrixIterators::Iterator; + template friend class ChunkSparseMatrixIterators::Accessor; }; /*@}*/ @@ -1242,6 +1348,17 @@ unsigned int ChunkSparseMatrix::n () const +template +inline +const ChunkSparsityPattern & +ChunkSparseMatrix::get_sparsity_pattern () const +{ + Assert (cols != 0, ExcNotInitialized()); + return *cols; +} + + + template inline unsigned int @@ -1250,7 +1367,7 @@ ChunkSparseMatrix::compute_location (const unsigned int i, { const unsigned int chunk_size = cols->get_chunk_size(); const unsigned int chunk_index - = cols->sparsity_pattern(i/chunk_size, j/chunk_size); + = cols->sparsity_pattern(i/chunk_size, j/chunk_size); if (chunk_index == ChunkSparsityPattern::invalid_entry) return ChunkSparsityPattern::invalid_entry; @@ -1275,10 +1392,8 @@ void ChunkSparseMatrix::set (const unsigned int i, Assert (numbers::is_finite(value), ExcNumberNotFinite()); Assert (cols != 0, ExcNotInitialized()); - // it is allowed to set elements of - // the matrix that are not part of - // the sparsity pattern, if the - // value to which we set it is zero + // it is allowed to set elements of the matrix that are not part of the + // sparsity pattern, if the value to which we set it is zero const unsigned int index = compute_location(i,j); Assert ((index != SparsityPattern::invalid_entry) || (value == 0.), @@ -1340,14 +1455,10 @@ ChunkSparseMatrix::operator *= (const number factor) const unsigned int chunk_size = cols->get_chunk_size(); - // multiply all elements of the matrix with - // the given factor. this includes the - // padding elements in chunks that overlap - // the boundaries of the actual matrix -- - // but since multiplication with a number - // does not violate the invariant of - // keeping these elements at zero nothing - // can happen + // multiply all elements of the matrix with the given factor. this includes + // the padding elements in chunks that overlap the boundaries of the actual + // matrix -- but since multiplication with a number does not violate the + // invariant of keeping these elements at zero nothing can happen number *val_ptr = val; const number *const end_ptr = val + cols->sparsity_pattern.n_nonzero_elements() @@ -1374,14 +1485,10 @@ ChunkSparseMatrix::operator /= (const number factor) const unsigned int chunk_size = cols->get_chunk_size(); - // multiply all elements of the matrix with - // the given factor. this includes the - // padding elements in chunks that overlap - // the boundaries of the actual matrix -- - // but since multiplication with a number - // does not violate the invariant of - // keeping these elements at zero nothing - // can happen + // multiply all elements of the matrix with the given factor. this includes + // the padding elements in chunks that overlap the boundaries of the actual + // matrix -- but since multiplication with a number does not violate the + // invariant of keeping these elements at zero nothing can happen number *val_ptr = val; const number *const end_ptr = val + cols->sparsity_pattern.n_nonzero_elements() @@ -1433,31 +1540,7 @@ number ChunkSparseMatrix::diag_element (const unsigned int i) const Assert (m() == n(), ExcNotQuadratic()); Assert (iget_chunk_size(); - return val[cols->sparsity_pattern.rowstart[i/chunk_size] - * - chunk_size * chunk_size - + - (i % chunk_size) * chunk_size - + - (i % chunk_size)]; -} - - - -template -inline -number &ChunkSparseMatrix::diag_element (const unsigned int i) -{ - Assert (cols != 0, ExcNotInitialized()); - Assert (m() == n(), ExcNotQuadratic()); - Assert (iget_chunk_size(); return val[cols->sparsity_pattern.rowstart[i/chunk_size] @@ -1473,6 +1556,7 @@ number &ChunkSparseMatrix::diag_element (const unsigned int i) template template +inline void ChunkSparseMatrix::copy_from (const ForwardIterator begin, const ForwardIterator end) @@ -1480,9 +1564,8 @@ ChunkSparseMatrix::copy_from (const ForwardIterator begin, Assert (static_cast(std::distance (begin, end)) == m(), ExcIteratorRange (std::distance (begin, end), m())); - // for use in the inner loop, we - // define a typedef to the type of - // the inner iterators + // for use in the inner loop, we define a typedef to the type of the inner + // iterators typedef typename std::iterator_traits::value_type::const_iterator inner_iterator; unsigned int row=0; for (ForwardIterator i=begin; i!=end; ++i, ++row) @@ -1495,6 +1578,466 @@ ChunkSparseMatrix::copy_from (const ForwardIterator begin, } + +//--------------------------------------------------------------------------- + + +namespace ChunkSparseMatrixIterators +{ + template + inline + Accessor:: + Accessor (const MatrixType *matrix, + const unsigned int row) + : + ChunkSparsityPatternIterators::Accessor (&matrix->get_sparsity_pattern(), + row), + matrix (matrix) + {} + + + + template + inline + Accessor:: + Accessor (const MatrixType *matrix) + : + ChunkSparsityPatternIterators::Accessor (&matrix->get_sparsity_pattern()), + matrix (matrix) + {} + + + + template + inline + Accessor:: + Accessor (const ChunkSparseMatrixIterators::Accessor &a) + : + ChunkSparsityPatternIterators::Accessor (a), + matrix (&a.get_matrix()) + {} + + + + template + inline + number + Accessor::value () const + { + const unsigned int chunk_size = matrix->get_sparsity_pattern().get_chunk_size(); + return matrix->val[reduced_index() * chunk_size * chunk_size + + + chunk_row * chunk_size + + + chunk_col]; + } + + + + template + inline + typename Accessor::MatrixType & + Accessor::get_matrix () const + { + return *matrix; + } + + + + template + inline + Accessor::Reference::Reference ( + const Accessor *accessor, + const bool) + : + accessor (accessor) + {} + + + template + inline + Accessor::Reference::operator number() const + { + const unsigned int chunk_size = accessor->matrix->get_sparsity_pattern().get_chunk_size(); + return accessor->matrix->val[accessor->reduced_index() * chunk_size * chunk_size + + + accessor->chunk_row * chunk_size + + + accessor->chunk_col]; + } + + + + template + inline + const typename Accessor::Reference & + Accessor::Reference::operator = (const number n) const + { + const unsigned int chunk_size = accessor->matrix->get_sparsity_pattern().get_chunk_size(); + accessor->matrix->val[accessor->reduced_index() * chunk_size * chunk_size + + + accessor->chunk_row * chunk_size + + + accessor->chunk_col] = n; + return *this; + } + + + + template + inline + const typename Accessor::Reference & + Accessor::Reference::operator += (const number n) const + { + const unsigned int chunk_size = accessor->matrix->get_sparsity_pattern().get_chunk_size(); + accessor->matrix->val[accessor->reduced_index() * chunk_size * chunk_size + + + accessor->chunk_row * chunk_size + + + accessor->chunk_col] += n; + return *this; + } + + + + template + inline + const typename Accessor::Reference & + Accessor::Reference::operator -= (const number n) const + { + const unsigned int chunk_size = accessor->matrix->get_sparsity_pattern().get_chunk_size(); + accessor->matrix->val[accessor->reduced_index() * chunk_size * chunk_size + + + accessor->chunk_row * chunk_size + + + accessor->chunk_col] -= n; + return *this; + } + + + + template + inline + const typename Accessor::Reference & + Accessor::Reference::operator *= (const number n) const + { + const unsigned int chunk_size = accessor->matrix->get_sparsity_pattern().get_chunk_size(); + accessor->matrix->val[accessor->reduced_index() * chunk_size * chunk_size + + + accessor->chunk_row * chunk_size + + + accessor->chunk_col] *= n; + return *this; + } + + + + template + inline + const typename Accessor::Reference & + Accessor::Reference::operator /= (const number n) const + { + const unsigned int chunk_size = accessor->matrix->get_sparsity_pattern().get_chunk_size(); + accessor->matrix->val[accessor->reduced_index() * chunk_size * chunk_size + + + accessor->chunk_row * chunk_size + + + accessor->chunk_col] /= n; + return *this; + } + + + + template + inline + Accessor:: + Accessor (MatrixType *matrix, + const unsigned int row) + : + ChunkSparsityPatternIterators::Accessor (&matrix->get_sparsity_pattern(), + row), + matrix (matrix) + {} + + + + template + inline + Accessor:: + Accessor (MatrixType *matrix) + : + ChunkSparsityPatternIterators::Accessor (&matrix->get_sparsity_pattern()), + matrix (matrix) + {} + + + + template + inline + typename Accessor::Reference + Accessor::value() const + { + return Reference(this,true); + } + + + + + template + inline + typename Accessor::MatrixType & + Accessor::get_matrix () const + { + return *matrix; + } + + + + template + inline + Iterator:: + Iterator (MatrixType *matrix, + const unsigned int row) + : + accessor(matrix, row) + {} + + + + template + inline + Iterator:: + Iterator (MatrixType *matrix) + : + accessor(matrix) + {} + + + + template + inline + Iterator:: + Iterator (const ChunkSparseMatrixIterators::Iterator &i) + : + accessor(*i) + {} + + + + template + inline + Iterator & + Iterator::operator++ () + { + accessor.advance (); + return *this; + } + + + template + inline + Iterator + Iterator::operator++ (int) + { + const Iterator iter = *this; + accessor.advance (); + return iter; + } + + + template + inline + const Accessor & + Iterator::operator* () const + { + return accessor; + } + + + template + inline + const Accessor * + Iterator::operator-> () const + { + return &accessor; + } + + + template + inline + bool + Iterator:: + operator == (const Iterator &other) const + { + return (accessor == other.accessor); + } + + + template + inline + bool + Iterator:: + operator != (const Iterator &other) const + { + return ! (*this == other); + } + + + template + inline + bool + Iterator:: + operator < (const Iterator &other) const + { + Assert (&accessor.get_matrix() == &other.accessor.get_matrix(), + ExcInternalError()); + + return (accessor < other.accessor); + } + + + template + inline + bool + Iterator:: + operator > (const Iterator &other) const + { + return (other < *this); + } + + + template + inline + int + Iterator:: + operator - (const Iterator &other) const + { + Assert (&accessor.get_matrix() == &other.accessor.get_matrix(), + ExcInternalError()); + + // TODO: can be optimized + int difference = 0; + if (*this < other) + { + Iterator copy = *this; + while (copy != other) + { + ++copy; + --difference; + } + } + else + { + Iterator copy = other; + while (copy != *this) + { + ++copy; + ++difference; + } + } + return difference; + } + + + + template + inline + Iterator + Iterator:: + operator + (const unsigned int n) const + { + Iterator x = *this; + for (unsigned int i=0; i +inline +typename ChunkSparseMatrix::const_iterator +ChunkSparseMatrix::begin () const +{ + return const_iterator(this, 0); +} + + +template +inline +typename ChunkSparseMatrix::const_iterator +ChunkSparseMatrix::end () const +{ + return const_iterator(this); +} + + +template +inline +typename ChunkSparseMatrix::iterator +ChunkSparseMatrix::begin () +{ + return iterator(this, 0); +} + + +template +inline +typename ChunkSparseMatrix::iterator +ChunkSparseMatrix::end () +{ + return iterator(this); +} + + +template +inline +typename ChunkSparseMatrix::const_iterator +ChunkSparseMatrix::begin (const unsigned int r) const +{ + Assert (r +inline +typename ChunkSparseMatrix::const_iterator +ChunkSparseMatrix::end (const unsigned int r) const +{ + Assert (r +inline +typename ChunkSparseMatrix::iterator +ChunkSparseMatrix::begin (const unsigned int r) +{ + Assert (r +inline +typename ChunkSparseMatrix::iterator +ChunkSparseMatrix::end (const unsigned int r) +{ + Assert (r +#include #include #include #include @@ -43,11 +44,9 @@ namespace internal namespace ChunkSparseMatrix { /** - * Add the result of multiplying a chunk - * of size chunk_size times chunk_size by - * a source vector fragment of size - * chunk_size to the destination vector - * fragment. + * Add the result of multiplying a chunk of size chunk_size times + * chunk_size by a source vector fragment of size chunk_size to the + * destination vector fragment. */ template + void vmult_add_on_subrange (const ChunkSparsityPattern &cols, + const unsigned int begin_row, + const unsigned int end_row, + const number *values, + const std::size_t *rowstart, + const unsigned int *colnums, + const InVector &src, + OutVector &dst) + { + const unsigned int m = cols.n_rows(); + const unsigned int n = cols.n_cols(); + const unsigned int chunk_size = cols.get_chunk_size(); + + // loop over all chunks. note that we need to treat the last chunk row + // and column differently if they have padding elements + const unsigned int n_filled_last_rows = m % chunk_size; + const unsigned int n_filled_last_cols = n % chunk_size; + + const unsigned int last_regular_row = n_filled_last_rows > 0 ? + std::min(m/chunk_size, end_row) : end_row; + const unsigned int irregular_col = n/chunk_size; + + typename OutVector::iterator dst_ptr = dst.begin()+chunk_size*begin_row; + const number *val_ptr= &values[rowstart[begin_row]*chunk_size*chunk_size]; + const unsigned int *colnum_ptr = &colnums[rowstart[begin_row]]; + for (unsigned int chunk_row=begin_row; chunk_row 0 && end_row == (m/chunk_size+1)) + { + const unsigned int chunk_row = last_regular_row; + + const number *const val_end_of_row = &values[rowstart[chunk_row+1] * + chunk_size * chunk_size]; + while (val_ptr != val_end_of_row) + { + if (*colnum_ptr != irregular_col) + { + // we're at a chunk row but not column that has padding + for (unsigned int r=0; r::~ChunkSparseMatrix () +namespace internal +{ + namespace ChunkSparseMatrix + { + template + void zero_subrange (const unsigned int begin, + const unsigned int end, + T *dst) + { + std::memset (dst+begin,0,(end-begin)*sizeof(T)); + } + } +} + + + template ChunkSparseMatrix & ChunkSparseMatrix::operator = (const double d) @@ -266,14 +377,27 @@ ChunkSparseMatrix::operator = (const double d) Assert (cols->sparsity_pattern.compressed || cols->empty(), ChunkSparsityPattern::ExcNotCompressed()); - if (val) - { - const unsigned int chunk_size = cols->get_chunk_size(); - std::fill_n (val, - cols->sparsity_pattern.n_nonzero_elements() * - chunk_size * chunk_size, - 0.); - } + // do initial zeroing of elements in parallel. Try to achieve a similar + // layout as when doing matrix-vector products, as on some NUMA systems, a + // memory block is assigned to memory banks where the first access is + // generated. For sparse matrices, the first operations is usually the + // operator=. The grain size is chosen to reflect the number of rows in + // minimum_parallel_grain_size, weighted by the number of nonzero entries + // per row on average. + const unsigned int matrix_size = cols->sparsity_pattern.n_nonzero_elements() + * cols->chunk_size * cols->chunk_size; + const unsigned int grain_size = + internal::SparseMatrix::minimum_parallel_grain_size * + (matrix_size+m()) / m(); + if (matrix_size>grain_size) + parallel::apply_to_subranges (0U, matrix_size, + std_cxx1x::bind(&internal::ChunkSparseMatrix::template + zero_subrange, + std_cxx1x::_1, std_cxx1x::_2, + val), + grain_size); + else if (matrix_size > 0) + std::memset (&val[0], 0, matrix_size*sizeof(number)); return *this; } @@ -313,10 +437,8 @@ ChunkSparseMatrix::reinit (const ChunkSparsityPattern &sparsity) return; } - // allocate not just m() * n() elements but - // enough so that we can store full - // chunks. this entails some padding - // elements + // allocate not just m() * n() elements but enough so that we can store full + // chunks. this entails some padding elements const unsigned int chunk_size = cols->get_chunk_size(); const unsigned int N = cols->sparsity_pattern.n_nonzero_elements() * chunk_size * chunk_size; @@ -328,12 +450,10 @@ ChunkSparseMatrix::reinit (const ChunkSparsityPattern &sparsity) max_len = N; } - // fill with zeros. do not just fill N - // elements but all that we allocated to - // ensure that also the padding elements - // are zero and not left at previous values - if (val != 0) - std::fill_n (&val[0], max_len, 0); + // fill with zeros. do not just fill N elements but all that we allocated to + // ensure that also the padding elements are zero and not left at previous + // values + this->operator=(0.); } @@ -378,11 +498,9 @@ ChunkSparseMatrix::n_actually_nonzero_elements () const { Assert (cols != 0, ExcNotInitialized()); - // count those elements that are nonzero, - // even if they lie in the padding around - // the matrix. since we have the invariant - // that padding elements are zero, nothing - // bad can happen here + // count those elements that are nonzero, even if they lie in the padding + // around the matrix. since we have the invariant that padding elements are + // zero, nothing bad can happen here const unsigned int chunk_size = cols->get_chunk_size(); return std::count_if(&val[0], &val[cols->sparsity_pattern.n_nonzero_elements () * @@ -413,8 +531,7 @@ ChunkSparseMatrix::copy_from (const ChunkSparseMatrix &matri Assert (val != 0, ExcNotInitialized()); Assert (cols == matrix.cols, ExcDifferentChunkSparsityPatterns()); - // copy everything, including padding - // elements + // copy everything, including padding elements const unsigned int chunk_size = cols->get_chunk_size(); std::copy (&matrix.val[0], &matrix.val[cols->sparsity_pattern.n_nonzero_elements() @@ -453,8 +570,7 @@ ChunkSparseMatrix::add (const number factor, Assert (val != 0, ExcNotInitialized()); Assert (cols == matrix.cols, ExcDifferentChunkSparsityPatterns()); - // add everything, including padding - // elements + // add everything, including padding elements const unsigned int chunk_size = cols->get_chunk_size(); number *val_ptr = &val[0]; const somenumber *matrix_ptr = &matrix.val[0]; @@ -479,10 +595,8 @@ ChunkSparseMatrix::vmult (OutVector &dst, Assert (!PointerComparison::equal(&src, &dst), ExcSourceEqualsDestination()); - // set the output vector to zero and then - // add to it the contributions of vmults - // from individual chunks. this is what - // vmult_add does + // set the output vector to zero and then add to it the contributions of + // vmults from individual chunks. this is what vmult_add does dst = 0; vmult_add (dst, src); } @@ -509,10 +623,8 @@ ChunkSparseMatrix::Tvmult (OutVector &dst, Assert (!PointerComparison::equal(&src, &dst), ExcSourceEqualsDestination()); - // set the output vector to zero and then - // add to it the contributions of vmults - // from individual chunks. this is what - // vmult_add does + // set the output vector to zero and then add to it the contributions of + // vmults from individual chunks. this is what vmult_add does dst = 0; Tvmult_add (dst, src); } @@ -531,93 +643,18 @@ ChunkSparseMatrix::vmult_add (OutVector &dst, Assert(n() == src.size(), ExcDimensionMismatch(n(),src.size())); Assert (!PointerComparison::equal(&src, &dst), ExcSourceEqualsDestination()); + parallel::apply_to_subranges (0U, cols->sparsity_pattern.n_rows(), + std_cxx1x::bind (&internal::ChunkSparseMatrix::vmult_add_on_subrange + , + std_cxx1x::cref(*cols), + std_cxx1x::_1, std_cxx1x::_2, + val, + cols->sparsity_pattern.rowstart, + cols->sparsity_pattern.colnums, + std_cxx1x::cref(src), + std_cxx1x::ref(dst)), + internal::SparseMatrix::minimum_parallel_grain_size/cols->chunk_size+1); - const unsigned int n_chunk_rows = cols->sparsity_pattern.n_rows(); - - // loop over all chunks. note that we need - // to treat the last chunk row and column - // differently if they have padding - // elements - const bool rows_have_padding = (m() % cols->chunk_size != 0), - cols_have_padding = (n() % cols->chunk_size != 0); - - const unsigned int n_regular_chunk_rows - = (rows_have_padding ? - n_chunk_rows-1 : - n_chunk_rows); - - const number *val_ptr = val; - const unsigned int *colnum_ptr = cols->sparsity_pattern.colnums; - typename OutVector::iterator dst_ptr = dst.begin(); - - for (unsigned int chunk_row=0; chunk_rowsparsity_pattern.rowstart[chunk_row+1] - * cols->chunk_size - * cols->chunk_size]; - while (val_ptr != val_end_of_row) - { - if ((cols_have_padding == false) - || - (*colnum_ptr != cols->sparsity_pattern.n_cols()-1)) - internal::ChunkSparseMatrix::chunk_vmult_add - (cols->chunk_size, - val_ptr, - src.begin() + *colnum_ptr * cols->chunk_size, - dst_ptr); - else - // we're at a chunk column that - // has padding - for (unsigned int r=0; rchunk_size; ++r) - for (unsigned int c=0; cchunk_size; ++c) - dst(chunk_row * cols->chunk_size + r) - += (val_ptr[r*cols->chunk_size + c] * - src(*colnum_ptr * cols->chunk_size + c)); - - ++colnum_ptr; - val_ptr += cols->chunk_size * cols->chunk_size; - } - - - dst_ptr += cols->chunk_size; - } - - // now deal with last chunk row if - // necessary - if (rows_have_padding) - { - const unsigned int chunk_row = n_chunk_rows - 1; - - const number *const val_end_of_row = &val[cols->sparsity_pattern.rowstart[chunk_row+1] - * cols->chunk_size - * cols->chunk_size]; - while (val_ptr != val_end_of_row) - { - if ((cols_have_padding == false) - || - (*colnum_ptr != cols->sparsity_pattern.n_cols()-1)) - { - // we're at a chunk row but not - // column that has padding - for (unsigned int r=0; rchunk_size; ++r) - for (unsigned int c=0; cchunk_size; ++c) - dst(chunk_row * cols->chunk_size + r) - += (val_ptr[r*cols->chunk_size + c] * - src(*colnum_ptr * cols->chunk_size + c)); - } - else - // we're at a chunk row and - // column that has padding - for (unsigned int r=0; rchunk_size; ++r) - for (unsigned int c=0; cchunk_size; ++c) - dst(chunk_row * cols->chunk_size + r) - += (val_ptr[r*cols->chunk_size + c] * - src(*colnum_ptr * cols->chunk_size + c)); - - ++colnum_ptr; - val_ptr += cols->chunk_size * cols->chunk_size; - } - } } @@ -636,10 +673,8 @@ ChunkSparseMatrix::Tvmult_add (OutVector &dst, const unsigned int n_chunk_rows = cols->sparsity_pattern.n_rows(); - // loop over all chunks. note that we need - // to treat the last chunk row and column - // differently if they have padding - // elements + // loop over all chunks. note that we need to treat the last chunk row and + // column differently if they have padding elements const bool rows_have_padding = (m() % cols->chunk_size != 0), cols_have_padding = (n() % cols->chunk_size != 0); @@ -648,9 +683,8 @@ ChunkSparseMatrix::Tvmult_add (OutVector &dst, n_chunk_rows-1 : n_chunk_rows); - // like in vmult_add, but don't keep an - // iterator into dst around since we're not - // traversing it sequentially this time + // like in vmult_add, but don't keep an iterator into dst around since we're + // not traversing it sequentially this time const number *val_ptr = val; const unsigned int *colnum_ptr = cols->sparsity_pattern.colnums; @@ -670,8 +704,7 @@ ChunkSparseMatrix::Tvmult_add (OutVector &dst, src.begin() + chunk_row * cols->chunk_size, dst.begin() + *colnum_ptr * cols->chunk_size); else - // we're at a chunk column that - // has padding + // we're at a chunk column that has padding for (unsigned int r=0; rchunk_size; ++r) for (unsigned int c=0; cchunk_size; ++c) dst(*colnum_ptr * cols->chunk_size + c) @@ -683,8 +716,7 @@ ChunkSparseMatrix::Tvmult_add (OutVector &dst, } } - // now deal with last chunk row if - // necessary + // now deal with last chunk row if necessary if (rows_have_padding) { const unsigned int chunk_row = n_chunk_rows - 1; @@ -698,8 +730,7 @@ ChunkSparseMatrix::Tvmult_add (OutVector &dst, || (*colnum_ptr != cols->sparsity_pattern.n_cols()-1)) { - // we're at a chunk row but not - // column that has padding + // we're at a chunk row but not column that has padding for (unsigned int r=0; rchunk_size; ++r) for (unsigned int c=0; cchunk_size; ++c) dst(*colnum_ptr * cols->chunk_size + c) @@ -707,8 +738,7 @@ ChunkSparseMatrix::Tvmult_add (OutVector &dst, src(chunk_row * cols->chunk_size + r)); } else - // we're at a chunk row and - // column that has padding + // we're at a chunk row and column that has padding for (unsigned int r=0; rchunk_size; ++r) for (unsigned int c=0; cchunk_size; ++c) dst(*colnum_ptr * cols->chunk_size + c) @@ -735,15 +765,12 @@ ChunkSparseMatrix::matrix_norm_square (const Vector &v) cons somenumber result = 0; //////////////// - // like matrix_scalar_product, except that - // the two vectors are now the same + // like matrix_scalar_product, except that the two vectors are now the same const unsigned int n_chunk_rows = cols->sparsity_pattern.n_rows(); - // loop over all chunks. note that we need - // to treat the last chunk row and column - // differently if they have padding - // elements + // loop over all chunks. note that we need to treat the last chunk row and + // column differently if they have padding elements const bool rows_have_padding = (m() % cols->chunk_size != 0), cols_have_padding = (n() % cols->chunk_size != 0); @@ -774,8 +801,7 @@ ChunkSparseMatrix::matrix_norm_square (const Vector &v) cons v_ptr, v.begin() + *colnum_ptr * cols->chunk_size); else - // we're at a chunk column that - // has padding + // we're at a chunk column that has padding for (unsigned int r=0; rchunk_size; ++r) for (unsigned int c=0; cchunk_size; ++c) result @@ -792,8 +818,7 @@ ChunkSparseMatrix::matrix_norm_square (const Vector &v) cons v_ptr += cols->chunk_size; } - // now deal with last chunk row if - // necessary + // now deal with last chunk row if necessary if (rows_have_padding) { const unsigned int chunk_row = n_chunk_rows - 1; @@ -807,8 +832,7 @@ ChunkSparseMatrix::matrix_norm_square (const Vector &v) cons || (*colnum_ptr != cols->sparsity_pattern.n_cols()-1)) { - // we're at a chunk row but not - // column that has padding + // we're at a chunk row but not column that has padding for (unsigned int r=0; rchunk_size; ++r) for (unsigned int c=0; cchunk_size; ++c) result @@ -818,8 +842,7 @@ ChunkSparseMatrix::matrix_norm_square (const Vector &v) cons v(*colnum_ptr * cols->chunk_size + c)); } else - // we're at a chunk row and - // column that has padding + // we're at a chunk row and column that has padding for (unsigned int r=0; rchunk_size; ++r) for (unsigned int c=0; cchunk_size; ++c) result @@ -849,16 +872,13 @@ ChunkSparseMatrix::matrix_scalar_product (const Vector &u, Assert(m() == u.size(), ExcDimensionMismatch(m(),u.size())); Assert(n() == v.size(), ExcDimensionMismatch(n(),v.size())); - // the following works like the vmult_add - // function + // the following works like the vmult_add function somenumber result = 0; const unsigned int n_chunk_rows = cols->sparsity_pattern.n_rows(); - // loop over all chunks. note that we need - // to treat the last chunk row and column - // differently if they have padding - // elements + // loop over all chunks. note that we need to treat the last chunk row and + // column differently if they have padding elements const bool rows_have_padding = (m() % cols->chunk_size != 0), cols_have_padding = (n() % cols->chunk_size != 0); @@ -889,8 +909,7 @@ ChunkSparseMatrix::matrix_scalar_product (const Vector &u, u_ptr, v.begin() + *colnum_ptr * cols->chunk_size); else - // we're at a chunk column that - // has padding + // we're at a chunk column that has padding for (unsigned int r=0; rchunk_size; ++r) for (unsigned int c=0; cchunk_size; ++c) result @@ -907,8 +926,7 @@ ChunkSparseMatrix::matrix_scalar_product (const Vector &u, u_ptr += cols->chunk_size; } - // now deal with last chunk row if - // necessary + // now deal with last chunk row if necessary if (rows_have_padding) { const unsigned int chunk_row = n_chunk_rows - 1; @@ -922,8 +940,7 @@ ChunkSparseMatrix::matrix_scalar_product (const Vector &u, || (*colnum_ptr != cols->sparsity_pattern.n_cols()-1)) { - // we're at a chunk row but not - // column that has padding + // we're at a chunk row but not column that has padding for (unsigned int r=0; rchunk_size; ++r) for (unsigned int c=0; cchunk_size; ++c) result @@ -933,8 +950,7 @@ ChunkSparseMatrix::matrix_scalar_product (const Vector &u, v(*colnum_ptr * cols->chunk_size + c)); } else - // we're at a chunk row and - // column that has padding + // we're at a chunk row and column that has padding for (unsigned int r=0; rchunk_size; ++r) for (unsigned int c=0; cchunk_size; ++c) result @@ -962,11 +978,9 @@ ChunkSparseMatrix::l1_norm () const const unsigned int n_chunk_rows = cols->sparsity_pattern.n_rows(); - // loop over all rows and columns; it is - // safe to also loop over the padding - // elements (they are zero) if we make sure - // that the vector into which we sum column - // sums is large enough + // loop over all rows and columns; it is safe to also loop over the padding + // elements (they are zero) if we make sure that the vector into which we + // sum column sums is large enough Vector column_sums(cols->sparsity_pattern.n_cols() * cols->chunk_size); @@ -994,19 +1008,15 @@ ChunkSparseMatrix::linfty_norm () const Assert (cols != 0, ExcNotInitialized()); Assert (val != 0, ExcNotInitialized()); - // this function works like l1_norm(). it - // can be made more efficient (without - // allocating a temporary vector) as is - // done in the SparseMatrix class but since - // it is rarely called in time critical - // places it is probably not worth it + // this function works like l1_norm(). it can be made more efficient + // (without allocating a temporary vector) as is done in the SparseMatrix + // class but since it is rarely called in time critical places it is + // probably not worth it const unsigned int n_chunk_rows = cols->sparsity_pattern.n_rows(); - // loop over all rows and columns; it is - // safe to also loop over the padding - // elements (they are zero) if we make sure - // that the vector into which we sum column - // sums is large enough + // loop over all rows and columns; it is safe to also loop over the padding + // elements (they are zero) if we make sure that the vector into which we + // sum column sums is large enough Vector row_sums(cols->sparsity_pattern.n_rows() * cols->chunk_size); @@ -1030,12 +1040,10 @@ template typename ChunkSparseMatrix::real_type ChunkSparseMatrix::frobenius_norm () const { - // simply add up all entries in the - // sparsity pattern, without taking any + // simply add up all entries in the sparsity pattern, without taking any // reference to rows or columns // - // padding elements are zero, so we can add - // them up as well + // padding elements are zero, so we can add them up as well real_type norm_sqr = 0; for (const number *ptr = &val[0]; ptr != &val[max_len]; ++ptr) norm_sqr += numbers::NumberTraits::abs_square(*ptr); @@ -1060,25 +1068,19 @@ ChunkSparseMatrix::residual (Vector &dst, Assert (&u != &dst, ExcSourceEqualsDestination()); - // set dst=b, then subtract the result of - // A*u from it. since the purpose of the - // current class is to promote streaming of - // data rather than more random access - // patterns, breaking things up into two - // loops may be reasonable + // set dst=b, then subtract the result of A*u from it. since the purpose of + // the current class is to promote streaming of data rather than more random + // access patterns, breaking things up into two loops may be reasonable dst = b; ///////// - // the rest of this function is like - // vmult_add, except that we subtract + // the rest of this function is like vmult_add, except that we subtract // rather than add A*u ///////// const unsigned int n_chunk_rows = cols->sparsity_pattern.n_rows(); - // loop over all chunks. note that we need - // to treat the last chunk row and column - // differently if they have padding - // elements + // loop over all chunks. note that we need to treat the last chunk row and + // column differently if they have padding elements const bool rows_have_padding = (m() % cols->chunk_size != 0), cols_have_padding = (n() % cols->chunk_size != 0); @@ -1107,8 +1109,7 @@ ChunkSparseMatrix::residual (Vector &dst, u.begin() + *colnum_ptr * cols->chunk_size, dst_ptr); else - // we're at a chunk column that - // has padding + // we're at a chunk column that has padding for (unsigned int r=0; rchunk_size; ++r) for (unsigned int c=0; cchunk_size; ++c) dst(chunk_row * cols->chunk_size + r) @@ -1123,8 +1124,7 @@ ChunkSparseMatrix::residual (Vector &dst, dst_ptr += cols->chunk_size; } - // now deal with last chunk row if - // necessary + // now deal with last chunk row if necessary if (rows_have_padding) { const unsigned int chunk_row = n_chunk_rows - 1; @@ -1138,8 +1138,7 @@ ChunkSparseMatrix::residual (Vector &dst, || (*colnum_ptr != cols->sparsity_pattern.n_cols()-1)) { - // we're at a chunk row but not - // column that has padding + // we're at a chunk row but not column that has padding for (unsigned int r=0; rchunk_size; ++r) for (unsigned int c=0; cchunk_size; ++c) dst(chunk_row * cols->chunk_size + r) @@ -1147,8 +1146,7 @@ ChunkSparseMatrix::residual (Vector &dst, u(*colnum_ptr * cols->chunk_size + c)); } else - // we're at a chunk row and - // column that has padding + // we're at a chunk row and column that has padding for (unsigned int r=0; rchunk_size; ++r) for (unsigned int c=0; cchunk_size; ++c) dst(chunk_row * cols->chunk_size + r) @@ -1195,10 +1193,8 @@ ChunkSparseMatrix::precondition_SSOR (Vector &dst, const Vector &src, const number /*om*/) const { - // to understand how this function works - // you may want to take a look at the CVS - // archives to see the original version - // which is much clearer... + // to understand how this function works you may want to take a look at the + // CVS archives to see the original version which is much clearer... Assert (cols != 0, ExcNotInitialized()); Assert (val != 0, ExcNotInitialized()); Assert (m() == n(), ExcMessage("This operation is only valid on square matrices.")); @@ -1387,16 +1383,6 @@ ChunkSparseMatrix::SSOR (Vector &dst, -template -const ChunkSparsityPattern & -ChunkSparseMatrix::get_sparsity_pattern () const -{ - Assert (cols != 0, ExcNotInitialized()); - return *cols; -} - - - template void ChunkSparseMatrix::print (std::ostream &out) const { @@ -1474,9 +1460,8 @@ void ChunkSparseMatrix::print_pattern (std::ostream &out, const unsigned int chunk_size = cols->get_chunk_size(); - // loop over all chunk rows and columns, - // and each time we find something repeat - // it chunk_size times in both directions + // loop over all chunk rows and columns, and each time we find something + // repeat it chunk_size times in both directions for (unsigned int i=0; isparsity_pattern.n_rows(); ++i) { for (unsigned int d=0; d::block_write (std::ostream &out) const { AssertThrow (out, ExcIO()); - // first the simple objects, - // bracketed in [...] + // first the simple objects, bracketed in [...] out << '[' << max_len << "]["; // then write out real data out.write (reinterpret_cast(&val[0]), diff --git a/deal.II/include/deal.II/lac/chunk_sparsity_pattern.h b/deal.II/include/deal.II/lac/chunk_sparsity_pattern.h index 948c8d017e..200c80303d 100644 --- a/deal.II/include/deal.II/lac/chunk_sparsity_pattern.h +++ b/deal.II/include/deal.II/lac/chunk_sparsity_pattern.h @@ -34,6 +34,181 @@ template class ChunkSparseMatrix; */ + +/** + * Iterators on sparsity patterns + */ +namespace ChunkSparsityPatternIterators +{ + // forward declaration + class Iterator; + + /** + * Accessor class for iterators into sparsity patterns. This class is + * also the base class for both const and non-const accessor classes + * into sparse matrices. + * + * Note that this class only allows read access to elements, providing + * their row and column number. It does not allow modifying the + * sparsity pattern itself. + * + * @author Martin Kronbichler + * @date 2013 + */ + class Accessor + { + public: + /** + * Constructor. + */ + Accessor (const ChunkSparsityPattern *matrix, + const unsigned int row); + + /** + * Constructor. Construct the end accessor for the given sparsity pattern. + */ + Accessor (const ChunkSparsityPattern *matrix); + + /** + * Row number of the element represented by this object. This function can + * only be called for entries for which is_valid_entry() is true. + */ + unsigned int row () const; + + /** + * Returns the global index from the reduced sparsity pattern. + */ + std::size_t reduced_index() const; + + /** + * Column number of the element represented by this object. This function + * can only be called for entries for which is_valid_entry() is true. + */ + unsigned int column () const; + + /** + * Return whether the sparsity pattern entry pointed to by this iterator + * is valid or not. Note that after compressing the sparsity pattern, all + * entries are valid. However, before compression, the sparsity pattern + * allocated some memory to be used while still adding new nonzero + * entries; if you create iterators in this phase of the sparsity + * pattern's lifetime, you will iterate over elements that are not + * valid. If this is so, then this function will return false. + */ + bool is_valid_entry () const; + + + /** + * Comparison. True, if both iterators point to the same matrix position. + */ + bool operator == (const Accessor &) const; + + + /** + * Comparison operator. Result is true if either the first row number is + * smaller or if the row numbers are equal and the first index is smaller. + * + * This function is only valid if both iterators point into the same + * sparsity pattern. + */ + bool operator < (const Accessor &) const; + + protected: + /** + * The sparsity pattern we operate on accessed. + */ + const ChunkSparsityPattern *sparsity_pattern; + + /** + * The accessor of the (reduced) sparsity pattern. + */ + SparsityPatternIterators::Accessor reduced_accessor; + + /** + * Current chunk row number. + */ + unsigned int chunk_row; + + /** + * Current chunk col number. + */ + unsigned int chunk_col; + + /** + * Move the accessor to the next nonzero entry in the matrix. + */ + void advance (); + + /** + * Grant access to iterator class. + */ + friend class Iterator; + }; + + + + /** + * STL conforming iterator walking over the elements of a sparsity pattern. + */ + class Iterator + { + public: + /** + * Constructor. Create an iterator into the sparsity pattern @p sp for the + * given row and the index within it. + */ + Iterator (const ChunkSparsityPattern *sp, + const unsigned int row); + + /** + * Prefix increment. + */ + Iterator &operator++ (); + + /** + * Postfix increment. + */ + Iterator operator++ (int); + + /** + * Dereferencing operator. + */ + const Accessor &operator* () const; + + /** + * Dereferencing operator. + */ + const Accessor *operator-> () const; + + /** + * Comparison. True, if both iterators point to the same matrix position. + */ + bool operator == (const Iterator &) const; + + /** + * Inverse of ==. + */ + bool operator != (const Iterator &) const; + + /** + * Comparison operator. Result is true if either the first row number is + * smaller or if the row numbers are equal and the first index is smaller. + * + * This function is only valid if both iterators point into the same + * matrix. + */ + bool operator < (const Iterator &) const; + + private: + /** + * Store an object of the accessor class. + */ + Accessor accessor; + }; +} + + + /** * Structure representing the sparsity pattern of a sparse matrix. * @@ -46,86 +221,67 @@ template class ChunkSparseMatrix; class ChunkSparsityPattern : public Subscriptor { public: + /** + * Typedef an iterator class that allows to walk over all nonzero elements + * of a sparsity pattern. + */ + typedef ChunkSparsityPatternIterators::Iterator const_iterator; + + /** + * Typedef an iterator class that allows to walk over all nonzero elements + * of a sparsity pattern. + * + * Since the iterator does not allow to modify the sparsity pattern, this + * type is the same as that for @p const_iterator. + */ + typedef ChunkSparsityPatternIterators::Iterator iterator; /** - * Define a value which is used - * to indicate that a certain - * value in the colnums array - * is unused, i.e. does not - * represent a certain column - * number index. + * Define a value which is used to indicate that a certain value in the + * colnums array is unused, i.e. does not represent a certain column number + * index. * - * Indices with this invalid - * value are used to insert new - * entries to the sparsity - * pattern using the add() member - * function, and are removed when + * Indices with this invalid value are used to insert new entries to the + * sparsity pattern using the add() member function, and are removed when * calling compress(). * - * You should not assume that the - * variable declared here has a - * certain value. The - * initialization is given here - * only to enable the compiler to - * perform some optimizations, - * but the actual value of the - * variable may change over time. + * You should not assume that the variable declared here has a certain + * value. The initialization is given here only to enable the compiler to + * perform some optimizations, but the actual value of the variable may + * change over time. */ static const unsigned int invalid_entry = SparsityPattern::invalid_entry; /** - * Initialize the matrix empty, - * that is with no memory - * allocated. This is useful if - * you want such objects as - * member variables in other - * classes. You can make the - * structure usable by calling - * the reinit() function. + * Initialize the matrix empty, that is with no memory allocated. This is + * useful if you want such objects as member variables in other classes. You + * can make the structure usable by calling the reinit() function. */ ChunkSparsityPattern (); /** - * Copy constructor. This - * constructor is only allowed to - * be called if the matrix - * structure to be copied is - * empty. This is so in order to - * prevent involuntary copies of - * objects for temporaries, which - * can use large amounts of - * computing time. However, copy - * constructors are needed if yo - * want to use the STL data types - * on classes like this, e.g. to - * write such statements like - * v.push_back - * (ChunkSparsityPattern());, - * with v a vector of - * ChunkSparsityPattern objects. - * - * Usually, it is sufficient to - * use the explicit keyword to - * disallow unwanted temporaries, - * but for the STL vectors, this - * does not work. Since copying a - * structure like this is not - * useful anyway because multiple - * matrices can use the same - * sparsity structure, copies are - * only allowed for empty - * objects, as described above. + * Copy constructor. This constructor is only allowed to be called if the + * matrix structure to be copied is empty. This is so in order to prevent + * involuntary copies of objects for temporaries, which can use large + * amounts of computing time. However, copy constructors are needed if yo + * want to use the STL data types on classes like this, e.g. to write such + * statements like v.push_back (ChunkSparsityPattern());, with + * v a vector of ChunkSparsityPattern objects. + * + * Usually, it is sufficient to use the explicit keyword to disallow + * unwanted temporaries, but for the STL vectors, this does not work. Since + * copying a structure like this is not useful anyway because multiple + * matrices can use the same sparsity structure, copies are only allowed for + * empty objects, as described above. */ ChunkSparsityPattern (const ChunkSparsityPattern &); /** - * Initialize a rectangular - * matrix. + * Initialize a rectangular matrix. * * @arg m number of rows * @arg n number of columns - * @arg max_per_row maximum - * number of nonzero entries per row + * @arg max_per_row maximum number of nonzero entries per row */ ChunkSparsityPattern (const unsigned int m, const unsigned int n, @@ -143,16 +299,12 @@ public: const bool optimize_diagonal) DEAL_II_DEPRECATED; /** - * Initialize a rectangular - * matrix. + * Initialize a rectangular matrix. * * @arg m number of rows * @arg n number of columns - * - * @arg row_lengths possible - * number of nonzero entries for - * each row. This vector must - * have one entry for each row. + * @arg row_lengths possible number of nonzero entries for each row. This + * vector must have one entry for each row. */ ChunkSparsityPattern (const unsigned int m, const unsigned int n, @@ -170,17 +322,12 @@ public: const bool optimize_diagonal) DEAL_II_DEPRECATED; /** - * Initialize a quadratic matrix - * of dimension n with - * at most max_per_row - * nonzero entries per row. + * Initialize a quadratic matrix of dimension n with at most + * max_per_row nonzero entries per row. * - * This constructor automatically - * enables optimized storage of - * diagonal elements. To avoid - * this, use the constructor - * taking row and column numbers - * separately. + * This constructor automatically enables optimized storage of diagonal + * elements. To avoid this, use the constructor taking row and column + * numbers separately. */ ChunkSparsityPattern (const unsigned int n, const unsigned int max_per_row, @@ -190,11 +337,8 @@ public: * Initialize a quadratic matrix. * * @arg m number of rows and columns - * - * @arg row_lengths possible - * number of nonzero entries for - * each row. This vector must - * have one entry for each row. + * @arg row_lengths possible number of nonzero entries for each row. This + * vector must have one entry for each row. */ ChunkSparsityPattern (const unsigned int m, const std::vector &row_lengths, @@ -215,25 +359,19 @@ public: ~ChunkSparsityPattern (); /** - * Copy operator. For this the - * same holds as for the copy - * constructor: it is declared, - * defined and fine to be called, - * but the latter only for empty + * Copy operator. For this the same holds as for the copy constructor: it is + * declared, defined and fine to be called, but the latter only for empty * objects. */ ChunkSparsityPattern &operator = (const ChunkSparsityPattern &); /** - * Reallocate memory and set up data - * structures for a new matrix with - * m rows and n columns, - * with at most max_per_row + * Reallocate memory and set up data structures for a new matrix with m + * rows and n columns, with at most max_per_row * nonzero entries per row. * - * This function simply maps its - * operations to the other - * reinit function. + * This function simply maps its operations to the other reinit + * function. */ void reinit (const unsigned int m, const unsigned int n, @@ -251,21 +389,14 @@ public: const bool optimize_diagonal) DEAL_II_DEPRECATED; /** - * Reallocate memory for a matrix - * of size m x n. The - * number of entries for each row - * is taken from the array - * row_lengths which has to - * give this number of each row - * i=1...m. + * Reallocate memory for a matrix of size m x n. The number of + * entries for each row is taken from the array row_lengths which + * has to give this number of each row i=1...m. * - * If m*n==0 all memory is freed, - * resulting in a total reinitialization - * of the object. If it is nonzero, new - * memory is only allocated if the new - * size extends the old one. This is done - * to save time and to avoid fragmentation - * of the heap. + * If m*n==0 all memory is freed, resulting in a total + * reinitialization of the object. If it is nonzero, new memory is only + * allocated if the new size extends the old one. This is done to save time + * and to avoid fragmentation of the heap. * * If the number of rows equals * the number of columns then @@ -290,8 +421,7 @@ public: const bool optimize_diagonal) DEAL_II_DEPRECATED; /** - * Same as above, but with a - * VectorSlice argument instead. + * Same as above, but with a VectorSlice argument instead. */ void reinit (const unsigned int m, const unsigned int n, @@ -309,103 +439,52 @@ public: const bool optimize_diagonal) DEAL_II_DEPRECATED; /** - * This function compresses the sparsity - * structure that this object represents. - * It does so by eliminating unused - * entries and sorting the remaining ones - * to allow faster access by usage of - * binary search algorithms. A special - * sorting scheme is used for the - * diagonal entry of quadratic matrices, - * which is always the first entry of - * each row. + * This function compresses the sparsity structure that this object + * represents. It does so by eliminating unused entries and sorting the + * remaining ones to allow faster access by usage of binary search + * algorithms. A special sorting scheme is used for the diagonal entry of + * quadratic matrices, which is always the first entry of each row. * - * The memory which is no more - * needed is released. + * The memory which is no more needed is released. * - * SparseMatrix objects require the - * ChunkSparsityPattern objects they are - * initialized with to be compressed, to - * reduce memory requirements. + * SparseMatrix objects require the ChunkSparsityPattern objects they are + * initialized with to be compressed, to reduce memory requirements. */ void compress (); /** - * This function can be used as a - * replacement for reinit(), - * subsequent calls to add() and - * a final call to close() if you - * know exactly in advance the - * entries that will form the - * matrix sparsity pattern. - * - * The first two parameters - * determine the size of the - * matrix. For the two last ones, - * note that a sparse matrix can - * be described by a sequence of - * rows, each of which is - * represented by a sequence of - * pairs of column indices and - * values. In the present - * context, the begin() and - * end() parameters designate - * iterators (of forward iterator - * type) into a container, one - * representing one row. The - * distance between begin() - * and end() should therefore - * be equal to - * n_rows(). These iterators - * may be iterators of - * std::vector, - * std::list, pointers into a - * C-style array, or any other - * iterator satisfying the - * requirements of a forward - * iterator. The objects pointed - * to by these iterators - * (i.e. what we get after - * applying operator* or - * operator-> to one of these - * iterators) must be a container - * itself that provides functions - * begin and end - * designating a range of - * iterators that describe the - * contents of one - * line. Dereferencing these - * inner iterators must either - * yield a pair of an unsigned - * integer as column index and a - * value of arbitrary type (such - * a type would be used if we - * wanted to describe a sparse - * matrix with one such object), - * or simply an unsigned integer - * (of we only wanted to describe - * a sparsity pattern). The - * function is able to determine - * itself whether an unsigned - * integer or a pair is what we - * get after dereferencing the - * inner iterators, through some - * template magic. - * - * While the order of the outer - * iterators denotes the - * different rows of the matrix, - * the order of the inner - * iterator denoting the columns - * does not matter, as they are - * sorted internal to this - * function anyway. - * - * Since that all sounds very - * complicated, consider the - * following example code, which - * may be used to fill a sparsity - * pattern: + * This function can be used as a replacement for reinit(), subsequent calls + * to add() and a final call to close() if you know exactly in advance the + * entries that will form the matrix sparsity pattern. + * + * The first two parameters determine the size of the matrix. For the two + * last ones, note that a sparse matrix can be described by a sequence of + * rows, each of which is represented by a sequence of pairs of column + * indices and values. In the present context, the begin() and end() + * parameters designate iterators (of forward iterator type) into a + * container, one representing one row. The distance between begin() and + * end() should therefore be equal to n_rows(). These iterators may be + * iterators of std::vector, std::list, pointers into a + * C-style array, or any other iterator satisfying the requirements of a + * forward iterator. The objects pointed to by these iterators (i.e. what we + * get after applying operator* or operator-> to one of + * these iterators) must be a container itself that provides functions + * begin and end designating a range of iterators that + * describe the contents of one line. Dereferencing these inner iterators + * must either yield a pair of an unsigned integer as column index and a + * value of arbitrary type (such a type would be used if we wanted to + * describe a sparse matrix with one such object), or simply an unsigned + * integer (of we only wanted to describe a sparsity pattern). The function + * is able to determine itself whether an unsigned integer or a pair is what + * we get after dereferencing the inner iterators, through some template + * magic. + * + * While the order of the outer iterators denotes the different rows of the + * matrix, the order of the inner iterator denoting the columns does not + * matter, as they are sorted internal to this function anyway. + * + * Since that all sounds very complicated, consider the following example + * code, which may be used to fill a sparsity pattern: * @code * std::vector > column_indices (n_rows); * for (unsigned int row=0; rowbegin and - * end (namely - * std::vectors), and the - * inner iterators dereferenced - * yield unsigned integers as - * column indices. Note that we - * could have replaced each of - * the two std::vector - * occurrences by std::list, - * and the inner one by - * std::set as well. - * - * Another example would be as - * follows, where we initialize a - * whole matrix, not only a - * sparsity pattern: + * Note that this example works since the iterators dereferenced yield + * containers with functions begin and end (namely + * std::vectors), and the inner iterators dereferenced yield + * unsigned integers as column indices. Note that we could have replaced + * each of the two std::vector occurrences by std::list, + * and the inner one by std::set as well. + * + * Another example would be as follows, where we initialize a whole matrix, + * not only a sparsity pattern: * @code * std::vector > entries (n_rows); * for (unsigned int row=0; rowstd::vector - * could be replaced by - * std::list, and the inner - * std::map - * could be replaced by - * std::vector >, - * or a list or set of such - * pairs, as they all return - * iterators that point to such - * pairs. + * This example works because dereferencing iterators of the inner type + * yields a pair of unsigned integers and a value, the first of which we + * take as column index. As previously, the outer std::vector could + * be replaced by std::list, and the inner std::map could be replaced by std::vector >, or a list or set of such pairs, as they all return + * iterators that point to such pairs. */ template void copy_from (const unsigned int n_rows, @@ -488,12 +548,9 @@ public: const bool optimize_diagonal) DEAL_II_DEPRECATED; /** - * Copy data from an object of type - * CompressedSparsityPattern, - * CompressedSetSparsityPattern or - * CompressedSimpleSparsityPattern. - * Previous content of this object is - * lost, and the sparsity pattern is in + * Copy data from an object of type CompressedSparsityPattern, + * CompressedSetSparsityPattern or CompressedSimpleSparsityPattern. + * Previous content of this object is lost, and the sparsity pattern is in * compressed mode afterwards. */ template @@ -510,14 +567,10 @@ public: const bool optimize_diagonal) DEAL_II_DEPRECATED; /** - * Take a full matrix and use its - * nonzero entries to generate a - * sparse matrix entry pattern - * for this object. + * Take a full matrix and use its nonzero entries to generate a sparse + * matrix entry pattern for this object. * - * Previous content of this - * object is lost, and the - * sparsity pattern is in + * Previous content of this object is lost, and the sparsity pattern is in * compressed mode afterwards. */ template @@ -534,71 +587,79 @@ public: const bool optimize_diagonal) DEAL_II_DEPRECATED; /** - * Return whether the object is empty. It - * is empty if no memory is allocated, - * which is the same as that both - * dimensions are zero. + * Set the sparsity pattern of the chunk sparsity pattern to be given by + * chunk_size*chunksize blocks of the sparsity pattern for chunks + * specified. Note that the final number of rows m of the sparsity + * pattern will be approximately sparsity_pattern_for_chunks.n_rows() * + * chunk_size (modulo padding elements in the last chunk) and similarly + * for the number of columns n. + * + * This is a special initialization option in case you can tell the position + * of the chunk already from the beginning without generating the sparsity + * pattern using make_sparsity_pattern calls. This bypasses the + * search for chunks but of course needs to be handled with care in order to + * give a correct sparsity pattern. + * + * Previous content of this object is lost, and the sparsity pattern is in + * compressed mode afterwards. + */ + template + void create_from (const unsigned int m, + const unsigned int n, + const Sparsity &sparsity_pattern_for_chunks, + const unsigned int chunk_size, + const bool optimize_diagonal = true); + + /** + * Return whether the object is empty. It is empty if no memory is + * allocated, which is the same as that both dimensions are zero. */ bool empty () const; /** - * Return the chunk size given as - * argument when constructing this - * object. + * Return the chunk size given as argument when constructing this object. */ unsigned int get_chunk_size () const; /** - * Return the maximum number of entries per - * row. Before compression, this equals the - * number given to the constructor, while - * after compression, it equals the maximum - * number of entries actually allocated by - * the user. + * Return the maximum number of entries per row. Before compression, this + * equals the number given to the constructor, while after compression, it + * equals the maximum number of entries actually allocated by the user. */ unsigned int max_entries_per_row () const; /** - * Add a nonzero entry to the matrix. - * This function may only be called - * for non-compressed sparsity patterns. + * Add a nonzero entry to the matrix. This function may only be called for + * non-compressed sparsity patterns. * - * If the entry already exists, nothing - * bad happens. + * If the entry already exists, nothing bad happens. */ void add (const unsigned int i, const unsigned int j); /** - * Make the sparsity pattern - * symmetric by adding the - * sparsity pattern of the + * Make the sparsity pattern symmetric by adding the sparsity pattern of the * transpose object. * - * This function throws an - * exception if the sparsity - * pattern does not represent a - * quadratic matrix. + * This function throws an exception if the sparsity pattern does not + * represent a quadratic matrix. */ void symmetrize (); /** - * Return number of rows of this - * matrix, which equals the dimension - * of the image space. + * Return number of rows of this matrix, which equals the dimension of the + * image space. */ - inline unsigned int n_rows () const; + unsigned int n_rows () const; /** - * Return number of columns of this - * matrix, which equals the dimension - * of the range space. + * Return number of columns of this matrix, which equals the dimension of + * the range space. */ - inline unsigned int n_cols () const; + unsigned int n_cols () const; /** - * Check if a value at a certain - * position may be non-zero. + * Check if a value at a certain position may be non-zero. */ bool exists (const unsigned int i, const unsigned int j) const; @@ -609,34 +670,25 @@ public: unsigned int row_length (const unsigned int row) const; /** - * Compute the bandwidth of the matrix - * represented by this structure. The - * bandwidth is the maximum of $|i-j|$ - * for which the index pair $(i,j)$ - * represents a nonzero entry of the - * matrix. Consequently, the maximum - * bandwidth a $n\times m$ matrix can - * have is $\max\{n-1,m-1\}$. + * Compute the bandwidth of the matrix represented by this structure. The + * bandwidth is the maximum of $|i-j|$ for which the index pair $(i,j)$ + * represents a nonzero entry of the matrix. Consequently, the maximum + * bandwidth a $n\times m$ matrix can have is $\max\{n-1,m-1\}$. */ unsigned int bandwidth () const; /** - * Return the number of nonzero elements of - * this matrix. Actually, it returns the - * number of entries in the sparsity - * pattern; if any of the entries should - * happen to be zero, it is counted - * anyway. + * Return the number of nonzero elements of this matrix. Actually, it + * returns the number of entries in the sparsity pattern; if any of the + * entries should happen to be zero, it is counted anyway. * - * This function may only be called if the - * matrix struct is compressed. It does not - * make too much sense otherwise anyway. + * This function may only be called if the matrix struct is compressed. It + * does not make too much sense otherwise anyway. */ unsigned int n_nonzero_elements () const; /** - * Return whether the structure is - * compressed or not. + * Return whether the structure is compressed or not. */ bool is_compressed () const; @@ -663,94 +715,89 @@ public: bool stores_only_added_elements () const; /** - * Write the data of this object - * en bloc to a file. This is - * done in a binary mode, so the - * output is neither readable by - * humans nor (probably) by other - * computers using a different - * operating system of number - * format. - * - * The purpose of this function - * is that you can swap out - * matrices and sparsity pattern - * if you are short of memory, - * want to communicate between - * different programs, or allow - * objects to be persistent - * across different runs of the - * program. + * STL-like iterator with the first entry of the matrix. The resulting + * iterator can be used to walk over all nonzero entries of the sparsity + * pattern. + */ + iterator begin () const; + + /** + * Final iterator. + */ + iterator end () const; + + /** + * STL-like iterator with the first entry of row r. + * + * Note that if the given row is empty, i.e. does not contain any nonzero + * entries, then the iterator returned by this function equals + * end(r). Note also that the iterator may not be dereferencable in + * that case. + */ + iterator begin (const unsigned int r) const; + + /** + * Final iterator of row r. It points to the first element past the + * end of line @p r, or past the end of the entire sparsity pattern. + * + * Note that the end iterator is not necessarily dereferencable. This is in + * particular the case if it is the end iterator for the last row of a + * matrix. + */ + iterator end (const unsigned int r) const; + + /** + * Write the data of this object en bloc to a file. This is done in a binary + * mode, so the output is neither readable by humans nor (probably) by other + * computers using a different operating system of number format. + * + * The purpose of this function is that you can swap out matrices and + * sparsity pattern if you are short of memory, want to communicate between + * different programs, or allow objects to be persistent across different + * runs of the program. */ void block_write (std::ostream &out) const; /** - * Read data that has previously - * been written by block_write() - * from a file. This is done - * using the inverse operations - * to the above function, so it - * is reasonably fast because the - * bitstream is not interpreted - * except for a few numbers up - * front. - * - * The object is resized on this - * operation, and all previous - * contents are lost. - * - * A primitive form of error - * checking is performed which - * will recognize the bluntest - * attempts to interpret some - * data as a vector stored - * bitwise to a file, but not - * more. + * Read data that has previously been written by block_write() from a + * file. This is done using the inverse operations to the above function, so + * it is reasonably fast because the bitstream is not interpreted except for + * a few numbers up front. + * + * The object is resized on this operation, and all previous contents are + * lost. + * + * A primitive form of error checking is performed which will recognize the + * bluntest attempts to interpret some data as a vector stored bitwise to a + * file, but not more. */ void block_read (std::istream &in); /** - * Print the sparsity of the - * matrix. The output consists of - * one line per row of the format - * [i,j1,j2,j3,...]. i - * is the row number and - * jn are the allocated - * columns in this row. + * Print the sparsity of the matrix. The output consists of one line per row + * of the format [i,j1,j2,j3,...]. i is the row number and + * jn are the allocated columns in this row. */ void print (std::ostream &out) const; /** - * Print the sparsity of the matrix - * in a format that gnuplot understands - * and which can be used to plot the - * sparsity pattern in a graphical - * way. The format consists of pairs - * i j of nonzero elements, each - * representing one entry of this - * matrix, one per line of the output - * file. Indices are counted from - * zero on, as usual. Since sparsity - * patterns are printed in the same - * way as matrices are displayed, we - * print the negative of the column - * index, which means that the - * (0,0) element is in the top left - * rather than in the bottom left - * corner. - * - * Print the sparsity pattern in - * gnuplot by setting the data style - * to dots or points and use the - * plot command. + * Print the sparsity of the matrix in a format that gnuplot + * understands and which can be used to plot the sparsity pattern in a + * graphical way. The format consists of pairs i j of nonzero + * elements, each representing one entry of this matrix, one per line of the + * output file. Indices are counted from zero on, as usual. Since sparsity + * patterns are printed in the same way as matrices are displayed, we print + * the negative of the column index, which means that the (0,0) + * element is in the top left rather than in the bottom left corner. + * + * Print the sparsity pattern in gnuplot by setting the data style to dots + * or points and use the plot command. */ void print_gnuplot (std::ostream &out) const; /** - * Determine an estimate for the - * memory consumption (in bytes) - * of this object. See - * MemoryConsumption. + * Determine an estimate for the memory consumption (in bytes) of this + * object. See MemoryConsumption. */ std::size_t memory_consumption () const; @@ -822,14 +869,12 @@ public: //@} private: /** - * Number of rows that this sparsity - * structure shall represent. + * Number of rows that this sparsity structure shall represent. */ unsigned int rows; /** - * Number of columns that this sparsity - * structure shall represent. + * Number of columns that this sparsity structure shall represent. */ unsigned int cols; @@ -839,18 +884,20 @@ private: unsigned int chunk_size; /** - * The reduced sparsity pattern. We store - * only which chunks exist, with each - * chunk a block in the matrix of size - * chunk_size by chunk_size. + * The reduced sparsity pattern. We store only which chunks exist, with each + * chunk a block in the matrix of size chunk_size by chunk_size. */ SparsityPattern sparsity_pattern; /** - * Make all the chunk sparse matrix kinds - * friends. + * Make all the chunk sparse matrix kinds friends. */ template friend class ChunkSparseMatrix; + + /** + * Make the accessor class a friend. + */ + friend class ChunkSparsityPatternIterators::Accessor; }; @@ -859,6 +906,309 @@ private: #ifndef DOXYGEN +namespace ChunkSparsityPatternIterators +{ + inline + Accessor:: + Accessor (const ChunkSparsityPattern *sparsity_pattern, + const unsigned int row) + : + sparsity_pattern(sparsity_pattern), + reduced_accessor(row==sparsity_pattern->n_rows() ? + *sparsity_pattern->sparsity_pattern.end() : + *sparsity_pattern->sparsity_pattern. + begin(row/sparsity_pattern->get_chunk_size())), + chunk_row (row==sparsity_pattern->n_rows() ? 0 : + row%sparsity_pattern->get_chunk_size()), + chunk_col (0) + {} + + + + inline + Accessor:: + Accessor (const ChunkSparsityPattern *sparsity_pattern) + : + sparsity_pattern(sparsity_pattern), + reduced_accessor(*sparsity_pattern->sparsity_pattern.end()), + chunk_row (0), + chunk_col (0) + {} + + + + inline + bool + Accessor::is_valid_entry () const + { + return reduced_accessor.is_valid_entry() + && + sparsity_pattern->get_chunk_size()*reduced_accessor.row()+chunk_row < + sparsity_pattern->n_rows() + && + sparsity_pattern->get_chunk_size()*reduced_accessor.column()+chunk_col < + sparsity_pattern->n_cols(); + } + + + + inline + unsigned int + Accessor::row() const + { + Assert (is_valid_entry() == true, ExcInvalidIterator()); + + return sparsity_pattern->get_chunk_size()*reduced_accessor.row() + + chunk_row; + } + + + + inline + unsigned int + Accessor::column() const + { + Assert (is_valid_entry() == true, ExcInvalidIterator()); + + return sparsity_pattern->get_chunk_size()*reduced_accessor.column() + + chunk_col; + } + + + + inline + std::size_t + Accessor::reduced_index() const + { + Assert (is_valid_entry() == true, ExcInvalidIterator()); + + return reduced_accessor.index_within_sparsity; + } + + + + + inline + bool + Accessor::operator == (const Accessor &other) const + { + // no need to check for equality of sparsity patterns as this is done in + // the reduced case already and every ChunkSparsityPattern has its own + // reduced sparsity pattern + return (reduced_accessor == other.reduced_accessor && + chunk_row == other.chunk_row && + chunk_col == other.chunk_col); + } + + + + inline + bool + Accessor::operator < (const Accessor &other) const + { + Assert (sparsity_pattern == other.sparsity_pattern, + ExcInternalError()); + + if (chunk_row != other.chunk_row) + { + if (reduced_accessor.index_within_sparsity == + reduced_accessor.sparsity_pattern->n_nonzero_elements()) + return false; + if (other.reduced_accessor.index_within_sparsity == + reduced_accessor.sparsity_pattern->n_nonzero_elements()) + return true; + + const unsigned int + global_row = sparsity_pattern->get_chunk_size()*reduced_accessor.row() + +chunk_row, + other_global_row = sparsity_pattern->get_chunk_size()* + other.reduced_accessor.row()+other.chunk_row; + if (global_row < other_global_row) + return true; + else if (global_row > other_global_row) + return false; + } + + return (reduced_accessor.index_within_sparsity < + other.reduced_accessor.index_within_sparsity || + (reduced_accessor.index_within_sparsity == + other.reduced_accessor.index_within_sparsity && + chunk_col < other.chunk_col)); + } + + + inline + void + Accessor::advance () + { + const unsigned int chunk_size = sparsity_pattern->get_chunk_size(); + Assert (chunk_row < chunk_size && chunk_col < chunk_size, + ExcIteratorPastEnd()); + Assert (reduced_accessor.row() * chunk_size + chunk_row < + sparsity_pattern->n_rows() + && + reduced_accessor.column() * chunk_size + chunk_col < + sparsity_pattern->n_cols(), + ExcIteratorPastEnd()); + if (chunk_size == 1) + { + reduced_accessor.advance(); + return; + } + + ++chunk_col; + + // end of chunk + if (chunk_col == chunk_size + || + reduced_accessor.column() * chunk_size + chunk_col == + sparsity_pattern->n_cols()) + { + const unsigned int reduced_row = reduced_accessor.row(); + // end of row + if (reduced_accessor.index_within_sparsity + 1 == + reduced_accessor.sparsity_pattern->rowstart[reduced_row+1]) + { + ++chunk_row; + + chunk_col = 0; + + // end of chunk rows or end of matrix + if (chunk_row == chunk_size || + (reduced_row * chunk_size + chunk_row == + sparsity_pattern->n_rows())) + { + chunk_row = 0; + reduced_accessor.advance(); + } + // go back to the beginning of the same reduced row but with + // chunk_row increased by one + else + reduced_accessor.index_within_sparsity = + reduced_accessor.sparsity_pattern->rowstart[reduced_row]; + } + // advance within chunk + else + { + reduced_accessor.advance(); + chunk_col = 0; + } + } + } + + + + inline + Iterator::Iterator (const ChunkSparsityPattern *sparsity_pattern, + const unsigned int row) + : + accessor(sparsity_pattern, row) + {} + + + + inline + Iterator & + Iterator::operator++ () + { + accessor.advance (); + return *this; + } + + + + inline + Iterator + Iterator::operator++ (int) + { + const Iterator iter = *this; + accessor.advance (); + return iter; + } + + + + inline + const Accessor & + Iterator::operator* () const + { + return accessor; + } + + + + inline + const Accessor * + Iterator::operator-> () const + { + return &accessor; + } + + + inline + bool + Iterator::operator == (const Iterator &other) const + { + return (accessor == other.accessor); + } + + + + inline + bool + Iterator::operator != (const Iterator &other) const + { + return ! (accessor == other.accessor); + } + + + inline + bool + Iterator::operator < (const Iterator &other) const + { + return accessor < other.accessor; + } + +} + + + +inline +ChunkSparsityPattern::iterator +ChunkSparsityPattern::begin () const +{ + return iterator(this, 0); +} + + +inline +ChunkSparsityPattern::iterator +ChunkSparsityPattern::end () const +{ + return iterator(this, n_rows()); +} + + + +inline +ChunkSparsityPattern::iterator +ChunkSparsityPattern::begin (const unsigned int r) const +{ + Assert (r +inline void ChunkSparsityPattern::copy_from (const unsigned int n_rows, const unsigned int n_cols, @@ -928,16 +1280,10 @@ ChunkSparsityPattern::copy_from (const unsigned int n_rows, Assert (static_cast(std::distance (begin, end)) == n_rows, ExcIteratorRange (std::distance (begin, end), n_rows)); - // first determine row lengths for - // each row. if the matrix is - // quadratic, then we might have to - // add an additional entry for the - // diagonal, if that is not yet - // present. as we have to call - // compress anyway later on, don't - // bother to check whether that - // diagonal entry is in a certain - // row or not + // first determine row lengths for each row. if the matrix is quadratic, + // then we might have to add an additional entry for the diagonal, if that + // is not yet present. as we have to call compress anyway later on, don't + // bother to check whether that diagonal entry is in a certain row or not const bool is_square = (n_rows == n_cols); std::vector row_lengths; row_lengths.reserve(n_rows); @@ -947,8 +1293,7 @@ ChunkSparsityPattern::copy_from (const unsigned int n_rows, (is_square ? 1 : 0)); reinit (n_rows, n_cols, row_lengths, chunk_size); - // now enter all the elements into - // the matrix + // now enter all the elements into the matrix unsigned int row = 0; typedef typename std::iterator_traits::value_type::const_iterator inner_iterator; for (ForwardIterator i=begin; i!=end; ++i, ++row) @@ -957,16 +1302,14 @@ ChunkSparsityPattern::copy_from (const unsigned int n_rows, for (inner_iterator j=i->begin(); j!=end_of_row; ++j) { const unsigned int col - = internal::SparsityPatternTools::get_column_index_from_iterator(*j); + = internal::SparsityPatternTools::get_column_index_from_iterator(*j); Assert (col < n_cols, ExcInvalidIndex(col,n_cols)); add (row, col); } } - // finally compress - // everything. this also sorts the - // entries within each row + // finally compress everything. this also sorts the entries within each row compress (); } diff --git a/deal.II/include/deal.II/lac/constraint_matrix.h b/deal.II/include/deal.II/lac/constraint_matrix.h index 40d824c3d5..ce286f57c6 100644 --- a/deal.II/include/deal.II/lac/constraint_matrix.h +++ b/deal.II/include/deal.II/lac/constraint_matrix.h @@ -1507,6 +1507,24 @@ public: << " should not be stored by this object, but a constraint " << "is being added."); + /** + * Exception + * + * @ingroup Exceptions + */ + DeclException2 (ExcIncorrectConstraint, + int, int, + << "While distributing the constraint for DoF " + << arg1 << ", it turns out that one of the processors " + << "who own the " << arg2 + << " degrees of freedom that x_" << arg1 + << " is constrained against does not know about " + << "the constraint on x_" << arg1 + << ". Did you not initialize the ConstraintMatrix " + << "with the appropriate locally_relevant set so " + << "that every processor who owns a DoF that constrains " + << "another DoF also knows about this constraint?"); + private: /** diff --git a/deal.II/include/deal.II/lac/constraint_matrix.templates.h b/deal.II/include/deal.II/lac/constraint_matrix.templates.h index 76268dcdcb..037b2f9535 100644 --- a/deal.II/include/deal.II/lac/constraint_matrix.templates.h +++ b/deal.II/include/deal.II/lac/constraint_matrix.templates.h @@ -982,22 +982,98 @@ template void ConstraintMatrix::distribute (VectorType &vec) const { - Assert (sorted == true, ExcMatrixNotClosed()); + Assert (sorted==true, ExcMatrixIsClosed()); + + // if the vector type supports parallel storage and if the + // vector actually does it, we need to be a bit more + // careful about how we do things + if ((vec.supports_distributed_data == true) + && + (vec.locally_owned_elements() != complete_index_set(vec.size()))) + { + const IndexSet vec_owned_elements = vec.locally_owned_elements(); - std::vector::const_iterator next_constraint = lines.begin(); - for (; next_constraint != lines.end(); ++next_constraint) + typedef std::vector::const_iterator constraint_iterator; + +#ifdef DEBUG + { + // the algorithm below pushes contributions c_ij x_j to a constrained + // DoF x_j *from the side of the owner of x_j*, as opposed to pulling + // it from the owner of the target side x_i. this relies on the + // assumption that both target and source know about the constraint + // on x_i. + // + // the disadvantage is that it is incredibly difficult to find out what + // is happening if the assumption is not satisfied. to help debug + // problems of this kind, we do the following in a first step if + // debugging is enabled: + // - every processor who owns an x_j where c_ij!=0 sends a one + // to the owner of x_i to add up + // - the owner of x_i knows how many nonzero entries exist, so can + // verify that the correct number of ones has been added + // - if the sum is not correct, then apparently one of the owners + // of the x_j's did not know to send its one and, consequently, + // will also not know to send the correct c_ij*x_j later on, + // leading to a bug + set_zero (vec); + for (constraint_iterator it = lines.begin(); + it != lines.end(); ++it) + for (unsigned int i=0; ientries.size(); ++i) + if (vec_owned_elements.is_element (it->entries[i].first)) + vec(it->line) += 1; + vec.compress (VectorOperation::add); + + for (constraint_iterator it = lines.begin(); + it != lines.end(); ++it) + if (vec_owned_elements.is_element(it->line)) + Assert (vec(it->line) == it->entries.size(), + ExcIncorrectConstraint(it->line, it->entries.size())); + } +#endif + + // set the values of all constrained DoFs to zero, then add the + // contributions for each constrained DoF of all the ones we + // own locally + set_zero (vec); + + for (constraint_iterator it = lines.begin(); + it != lines.end(); ++it) + for (unsigned int i=0; ientries.size(); ++i) + if (vec_owned_elements.is_element(it->entries[i].first)) + vec(it->line) += it->entries[i].second * + vec(it->entries[i].first); + + // in a final step, add the inhomogeneities to the elements we + // own locally + for (constraint_iterator it = lines.begin(); + it != lines.end(); ++it) + if (vec_owned_elements.is_element(it->line)) + vec(it->line) += it->inhomogeneity; + + // now compress to communicate the entries that we added to + // and that weren't to local processors to the owner + vec.compress (VectorOperation::add); + } + else + // purely sequential vector (either because the type doesn't + // support anything else or because it's completely stored + // locally { - // fill entry in line - // next_constraint.line by adding the - // different contributions - typename VectorType::value_type - new_value = next_constraint->inhomogeneity; - for (unsigned int i=0; ientries.size(); ++i) - new_value += (static_cast - (vec(next_constraint->entries[i].first)) * - next_constraint->entries[i].second); - Assert(numbers::is_finite(new_value), ExcNumberNotFinite()); - vec(next_constraint->line) = new_value; + std::vector::const_iterator next_constraint = lines.begin(); + for (; next_constraint != lines.end(); ++next_constraint) + { + // fill entry in line + // next_constraint.line by adding the + // different contributions + typename VectorType::value_type + new_value = next_constraint->inhomogeneity; + for (unsigned int i=0; ientries.size(); ++i) + new_value += (static_cast + (vec(next_constraint->entries[i].first)) * + next_constraint->entries[i].second); + Assert(numbers::is_finite(new_value), ExcNumberNotFinite()); + vec(next_constraint->line) = new_value; + } } } diff --git a/deal.II/include/deal.II/lac/full_matrix.templates.h b/deal.II/include/deal.II/lac/full_matrix.templates.h index 8a9a61b5b6..41febef428 100644 --- a/deal.II/include/deal.II/lac/full_matrix.templates.h +++ b/deal.II/include/deal.II/lac/full_matrix.templates.h @@ -539,10 +539,8 @@ void FullMatrix::mmult (FullMatrix &dst, Assert (dst.n() == src.n(), ExcDimensionMismatch(dst.n(), src.n())); Assert (dst.m() == m(), ExcDimensionMismatch(m(), dst.m())); - // see if we can use BLAS algorithms - // for this and if the type for 'number' - // works for us (it is usually not - // efficient to use BLAS for very small + // see if we can use BLAS algorithms for this and if the type for 'number' + // works for us (it is usually not efficient to use BLAS for very small // matrices): #if defined(HAVE_DGEMM_) && defined (HAVE_SGEMM_) if ((types_are_equal::value @@ -552,28 +550,17 @@ void FullMatrix::mmult (FullMatrix &dst, types_are_equal::value) if (this->n()*this->m()*src.n() > 300) { - // In case we have the BLAS - // function gemm detected at - // configure, we use that algorithm - // for matrix-matrix multiplication - // since it provides better - // performance than the deal.II - // native function (it uses cache - // and register blocking in order to - // access local data). + // In case we have the BLAS function gemm detected at configure, we + // use that algorithm for matrix-matrix multiplication since it + // provides better performance than the deal.II native function (it + // uses cache and register blocking in order to access local data). // - // Note that BLAS/LAPACK stores - // matrix elements column-wise (i.e., - // all values in one column, then all - // in the next, etc.), whereas the - // FullMatrix stores them row-wise. - // We ignore that difference, and - // give our row-wise data to BLAS, - // let BLAS build the product of - // transpose matrices, and read the - // result as if it were row-wise - // again. In other words, we calculate - // (B^T A^T)^T, which is AB. + // Note that BLAS/LAPACK stores matrix elements column-wise (i.e., all + // values in one column, then all in the next, etc.), whereas the + // FullMatrix stores them row-wise. We ignore that difference, and + // give our row-wise data to BLAS, let BLAS build the product of + // transpose matrices, and read the result as if it were row-wise + // again. In other words, we calculate (B^T A^T)^T, which is AB. const int m = src.n(); const int n = this->m(); @@ -596,12 +583,9 @@ void FullMatrix::mmult (FullMatrix &dst, const unsigned int m = this->m(), n = src.n(), l = this->n(); - // arrange the loops in a way that - // we keep write operations low, - // (writing is usually more costly - // than reading), even though we - // need to access the data in src - // not in a contiguous way. + // arrange the loops in a way that we keep write operations low, (writing is + // usually more costly than reading), even though we need to access the data + // in src not in a contiguous way. for (unsigned int i=0; i::Tmmult (FullMatrix &dst, Assert (src.n() == dst.n(), ExcDimensionMismatch(src.n(), dst.n())); - // see if we can use BLAS algorithms - // for this and if the type for 'number' - // works for us (it is usually not - // efficient to use BLAS for very small + // see if we can use BLAS algorithms for this and if the type for 'number' + // works for us (it is usually not efficient to use BLAS for very small // matrices): #if defined(HAVE_DGEMM_) && defined (HAVE_SGEMM_) if ((types_are_equal::value @@ -639,28 +621,17 @@ void FullMatrix::Tmmult (FullMatrix &dst, types_are_equal::value) if (this->n()*this->m()*src.n() > 300) { - // In case we have the BLAS - // function gemm detected at - // configure, we use that algorithm - // for matrix-matrix multiplication - // since it provides better - // performance than the deal.II - // native function (it uses cache - // and register blocking in order to - // access local data). + // In case we have the BLAS function gemm detected at configure, we + // use that algorithm for matrix-matrix multiplication since it + // provides better performance than the deal.II native function (it + // uses cache and register blocking in order to access local data). // - // Note that BLAS/LAPACK stores - // matrix elements column-wise (i.e., - // all values in one column, then all - // in the next, etc.), whereas the - // FullMatrix stores them row-wise. - // We ignore that difference, and - // give our row-wise data to BLAS, - // let BLAS build the product of - // transpose matrices, and read the - // result as if it were row-wise - // again. In other words, we calculate - // (B^T A)^T, which is A^T B. + // Note that BLAS/LAPACK stores matrix elements column-wise (i.e., all + // values in one column, then all in the next, etc.), whereas the + // FullMatrix stores them row-wise. We ignore that difference, and + // give our row-wise data to BLAS, let BLAS build the product of + // transpose matrices, and read the result as if it were row-wise + // again. In other words, we calculate (B^T A)^T, which is A^T B. const int m = src.n(); const int n = this->n(); @@ -671,8 +642,7 @@ void FullMatrix::Tmmult (FullMatrix &dst, const number alpha = 1.; const number beta = (adding == true) ? 1. : 0.; - // Use the BLAS function gemm for - // calculating the matrix-matrix + // Use the BLAS function gemm for calculating the matrix-matrix // product. gemm(notrans, trans, &m, &n, &k, &alpha, &src(0,0), &m, &this->values[0], &n, &beta, &dst(0,0), &m); @@ -684,24 +654,37 @@ void FullMatrix::Tmmult (FullMatrix &dst, const unsigned int m = n(), n = src.n(), l = this->m(); - // arrange the loops in a way that - // we keep write operations low, - // (writing is usually more costly - // than reading), even though we - // need to access the data in src - // not in a contiguous way. However, - // we should usually end up in the - // optimized gemm operation in case - // the matrix is big, so this - // shouldn't be too bad. - for (unsigned int i=0; i::mTmult (FullMatrix &dst, Assert (dst.n() == src.m(), ExcDimensionMismatch(dst.n(), src.m())); Assert (dst.m() == m(), ExcDimensionMismatch(m(), dst.m())); - // see if we can use BLAS algorithms - // for this and if the type for 'number' - // works for us (it is usually not - // efficient to use BLAS for very small + // see if we can use BLAS algorithms for this and if the type for 'number' + // works for us (it is usually not efficient to use BLAS for very small // matrices): #if defined(HAVE_DGEMM_) && defined (HAVE_SGEMM_) if ((types_are_equal::value @@ -730,28 +711,17 @@ void FullMatrix::mTmult (FullMatrix &dst, types_are_equal::value) if (this->n()*this->m()*src.m() > 300) { - // In case we have the BLAS - // function gemm detected at - // configure, we use that algorithm - // for matrix-matrix multiplication - // since it provides better - // performance than the deal.II - // native function (it uses cache - // and register blocking in order to - // access local data). + // In case we have the BLAS function gemm detected at configure, we + // use that algorithm for matrix-matrix multiplication since it + // provides better performance than the deal.II native function (it + // uses cache and register blocking in order to access local data). // - // Note that BLAS/LAPACK stores - // matrix elements column-wise (i.e., - // all values in one column, then all - // in the next, etc.), whereas the - // FullMatrix stores them row-wise. - // We ignore that difference, and - // give our row-wise data to BLAS, - // let BLAS build the product of - // transpose matrices, and read the - // result as if it were row-wise - // again. In other words, we calculate - // (B A^T)^T, which is AB^T. + // Note that BLAS/LAPACK stores matrix elements column-wise (i.e., all + // values in one column, then all in the next, etc.), whereas the + // FullMatrix stores them row-wise. We ignore that difference, and + // give our row-wise data to BLAS, let BLAS build the product of + // transpose matrices, and read the result as if it were row-wise + // again. In other words, we calculate (B A^T)^T, which is AB^T. const int m = src.m(); const int n = this->m(); @@ -762,8 +732,7 @@ void FullMatrix::mTmult (FullMatrix &dst, const number alpha = 1.; const number beta = (adding == true) ? 1. : 0.; - // Use the BLAS function gemm for - // calculating the matrix-matrix + // Use the BLAS function gemm for calculating the matrix-matrix // product. gemm(trans, notrans, &m, &n, &k, &alpha, &src(0,0), &k, &this->values[0], &k, &beta, &dst(0,0), &m); @@ -775,18 +744,34 @@ void FullMatrix::mTmult (FullMatrix &dst, const unsigned int m = this->m(), n = src.m(), l = this->n(); - // arrange the loops in a way that - // we keep write operations low, - // (writing is usually more costly - // than reading). - for (unsigned int i=0; i::TmTmult (FullMatrix &dst, Assert (src.m() == dst.n(), ExcDimensionMismatch(src.m(), dst.n())); - // see if we can use BLAS algorithms - // for this and if the type for 'number' - // works for us (it is usually not - // efficient to use BLAS for very small + // see if we can use BLAS algorithms for this and if the type for 'number' + // works for us (it is usually not efficient to use BLAS for very small // matrices): #if defined(HAVE_DGEMM_) && defined (HAVE_SGEMM_) if ((types_are_equal::value @@ -816,28 +799,17 @@ void FullMatrix::TmTmult (FullMatrix &dst, types_are_equal::value) if (this->n()*this->m()*src.m() > 300) { - // In case we have the BLAS - // function gemm detected at - // configure, we use that algorithm - // for matrix-matrix multiplication - // since it provides better - // performance than the deal.II - // native function (it uses cache - // and register blocking in order to - // access local data). + // In case we have the BLAS function gemm detected at configure, we + // use that algorithm for matrix-matrix multiplication since it + // provides better performance than the deal.II native function (it + // uses cache and register blocking in order to access local data). // - // Note that BLAS/LAPACK stores - // matrix elements column-wise (i.e., - // all values in one column, then all - // in the next, etc.), whereas the - // FullMatrix stores them row-wise. - // We ignore that difference, and - // give our row-wise data to BLAS, - // let BLAS build the product of - // transpose matrices, and read the - // result as if it were row-wise - // again. In other words, we calculate - // (B A)^T, which is A^T B^T. + // Note that BLAS/LAPACK stores matrix elements column-wise (i.e., all + // values in one column, then all in the next, etc.), whereas the + // FullMatrix stores them row-wise. We ignore that difference, and + // give our row-wise data to BLAS, let BLAS build the product of + // transpose matrices, and read the result as if it were row-wise + // again. In other words, we calculate (B A)^T, which is A^T B^T. const int m = src.n(); const int n = this->n(); @@ -847,8 +819,7 @@ void FullMatrix::TmTmult (FullMatrix &dst, const number alpha = 1.; const number beta = (adding == true) ? 1. : 0.; - // Use the BLAS function gemm for - // calculating the matrix-matrix + // Use the BLAS function gemm for calculating the matrix-matrix // product. gemm(trans, trans, &m, &n, &k, &alpha, &src(0,0), &k, &this->values[0], &n, &beta, &dst(0,0), &m); @@ -860,17 +831,11 @@ void FullMatrix::TmTmult (FullMatrix &dst, const unsigned int m = n(), n = src.m(), l = this->m(); - // arrange the loops in a way that - // we keep write operations low, - // (writing is usually more costly - // than reading), even though we - // need to access the data in the - // calling matrix not in a - // contiguous way. However, we - // should usually end up in the - // optimized gemm operation in case - // the matrix is big, so this - // shouldn't be too bad. + // arrange the loops in a way that we keep write operations low, (writing is + // usually more costly than reading), even though we need to access the data + // in the calling matrix in a non-contiguous way, possibly leading to cache + // misses. However, we should usually end up in the optimized gemm operation + // in case the matrix is big, so this shouldn't be too bad. for (unsigned int i=0; i::initialize (const MATRIX &m, const PRECONDITION matrix = new PointerMatrix(&m); if (precondition != 0) delete precondition; - precondition = new PointerMatrix(&p);; + precondition = new PointerMatrix(&p); } diff --git a/deal.II/include/deal.II/lac/parallel_block_vector.h b/deal.II/include/deal.II/lac/parallel_block_vector.h index 221570efd0..63b2395979 100644 --- a/deal.II/include/deal.II/lac/parallel_block_vector.h +++ b/deal.II/include/deal.II/lac/parallel_block_vector.h @@ -1,7 +1,7 @@ //--------------------------------------------------------------------------- // $Id$ // -// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 by the deal.II authors +// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -24,9 +25,6 @@ DEAL_II_NAMESPACE_OPEN -// TODO: global reduction operations (operator *, {l1,l2,lp,linfty}norm, mean -// value) should use one MPI communication with several Number values, not use -// the parallel::distributed::Vector operation directly. namespace parallel { @@ -56,20 +54,17 @@ namespace parallel { public: /** - * Typedef the base class for simpler - * access to its own typedefs. + * Typedef the base class for simpler access to its own typedefs. */ typedef BlockVectorBase > BaseClass; /** - * Typedef the type of the underlying - * vector. + * Typedef the type of the underlying vector. */ typedef typename BaseClass::BlockType BlockType; /** - * Import the typedefs from the base - * class. + * Import the typedefs from the base class. */ typedef typename BaseClass::value_type value_type; typedef typename BaseClass::real_type real_type; @@ -82,55 +77,37 @@ namespace parallel typedef typename BaseClass::const_iterator const_iterator; /** - * Constructor. There are three - * ways to use this - * constructor. First, without - * any arguments, it generates - * an object with no - * blocks. Given one argument, - * it initializes num_blocks - * blocks, but these blocks have - * size zero. The third variant - * finally initializes all - * blocks to the same size - * block_size. + * Constructor. There are three ways to use this constructor. First, + * without any arguments, it generates an object with no blocks. Given + * one argument, it initializes num_blocks blocks, but these + * blocks have size zero. The third variant finally initializes all + * blocks to the same size block_size. * - * Confer the other constructor - * further down if you intend to - * use blocks of different - * sizes. + * Confer the other constructor further down if you intend to use + * blocks of different sizes. */ explicit BlockVector (const unsigned int num_blocks = 0, const unsigned int block_size = 0); /** - * Copy-Constructor. Dimension set to - * that of V, all components are copied - * from V + * Copy-Constructor. Dimension set to that of V, all components are + * copied from V */ BlockVector (const BlockVector &V); #ifndef DEAL_II_EXPLICIT_CONSTRUCTOR_BUG /** - * Copy constructor taking a BlockVector of - * another data type. This will fail if - * there is no conversion path from - * OtherNumber to Number. Note that - * you may lose accuracy when copying - * to a BlockVector with data elements with - * less accuracy. + * Copy constructor taking a BlockVector of another data type. This will + * fail if there is no conversion path from OtherNumber to + * Number. Note that you may lose accuracy when copying to a + * BlockVector with data elements with less accuracy. * - * Older versions of gcc did not honor - * the @p explicit keyword on template - * constructors. In such cases, it is - * easy to accidentally write code that - * can be very inefficient, since the - * compiler starts performing hidden - * conversions. To avoid this, this - * function is disabled if we have - * detected a broken compiler during - * configuration. + * Older versions of gcc did not honor the @p explicit keyword on + * template constructors. In such cases, it is easy to accidentally + * write code that can be very inefficient, since the compiler starts + * performing hidden conversions. To avoid this, this function is + * disabled if we have detected a broken compiler during configuration. */ template explicit @@ -138,38 +115,31 @@ namespace parallel #endif /** - * Constructor. Set the number of - * blocks to - * block_sizes.size() and - * initialize each block with - * block_sizes[i] zero - * elements. + * Constructor. Set the number of blocks to block_sizes.size() + * and initialize each block with block_sizes[i] zero elements. */ BlockVector (const std::vector &block_sizes); /** - * Destructor. Clears memory + * Destructor. Clears memory. */ ~BlockVector (); /** - * Copy operator: fill all components of - * the vector with the given scalar - * value. + * Copy operator: fill all components of the vector with the given + * scalar value. */ BlockVector &operator = (const value_type s); /** - * Copy operator for arguments of the - * same type. Resize the - * present vector if necessary. + * Copy operator for arguments of the same type. Resize the present + * vector if necessary. */ BlockVector & operator= (const BlockVector &V); /** - * Copy operator for template arguments - * of different types. Resize the + * Copy operator for template arguments of different types. Resize the * present vector if necessary. */ template @@ -177,152 +147,173 @@ namespace parallel operator= (const BlockVector &V); /** - * Copy a regular vector into a - * block vector. + * Copy a regular vector into a block vector. */ BlockVector & operator= (const Vector &V); /** - * Reinitialize the BlockVector to - * contain num_blocks blocks of + * Reinitialize the BlockVector to contain num_blocks blocks of * size block_size each. * - * If the second argument is left - * at its default value, then the - * block vector allocates the - * specified number of blocks but - * leaves them at zero size. You - * then need to later - * reinitialize the individual - * blocks, and call - * collect_sizes() to update the - * block system's knowledge of + * If the second argument is left at its default value, then the block + * vector allocates the specified number of blocks but leaves them at + * zero size. You then need to later reinitialize the individual blocks, + * and call collect_sizes() to update the block system's knowledge of * its individual block's sizes. * - * If fast==false, the vector - * is filled with zeros. + * If fast==false, the vector is filled with zeros. */ void reinit (const unsigned int num_blocks, const unsigned int block_size = 0, const bool fast = false); /** - * Reinitialize the BlockVector such that - * it contains - * block_sizes.size() - * blocks. Each block is reinitialized to + * Reinitialize the BlockVector such that it contains + * block_sizes.size() blocks. Each block is reinitialized to * dimension block_sizes[i]. * - * If the number of blocks is the - * same as before this function - * was called, all vectors remain - * the same and reinit() is - * called for each vector. + * If the number of blocks is the same as before this function was + * called, all vectors remain the same and reinit() is called for each + * vector. * - * If fast==false, the vector - * is filled with zeros. + * If fast==false, the vector is filled with zeros. * - * Note that you must call this - * (or the other reinit() - * functions) function, rather - * than calling the reinit() - * functions of an individual - * block, to allow the block - * vector to update its caches of - * vector sizes. If you call - * reinit() on one of the - * blocks, then subsequent - * actions on this object may - * yield unpredictable results - * since they may be routed to - * the wrong block. + * Note that you must call this (or the other reinit() functions) + * function, rather than calling the reinit() functions of an individual + * block, to allow the block vector to update its caches of vector + * sizes. If you call reinit() on one of the blocks, then subsequent + * actions on this object may yield unpredictable results since they may + * be routed to the wrong block. */ void reinit (const std::vector &N, const bool fast=false); /** - * Change the dimension to that - * of the vector V. The same - * applies as for the other - * reinit() function. + * Change the dimension to that of the vector V. The same + * applies as for the other reinit() function. * - * The elements of V are not - * copied, i.e. this function is - * the same as calling reinit - * (V.size(), fast). + * The elements of V are not copied, i.e. this function is the + * same as calling reinit (V.size(), fast). * - * Note that you must call this - * (or the other reinit() - * functions) function, rather - * than calling the reinit() - * functions of an individual - * block, to allow the block - * vector to update its caches of - * vector sizes. If you call - * reinit() of one of the - * blocks, then subsequent - * actions of this object may - * yield unpredictable results - * since they may be routed to - * the wrong block. + * Note that you must call this (or the other reinit() functions) + * function, rather than calling the reinit() functions of an individual + * block, to allow the block vector to update its caches of vector + * sizes. If you call reinit() of one of the blocks, then subsequent + * actions of this object may yield unpredictable results since they may + * be routed to the wrong block. */ template void reinit (const BlockVector &V, const bool fast=false); /** - * Scale each element of the - * vector by the given factor. + * Return whether the vector contains only elements with value + * zero. This function is mainly for internal consistency checks and + * should seldom be used when not in debug mode since it uses quite some + * time. + */ + bool all_zero () const; + + /** + * Return @p true if the vector has no negative entries, i.e. all + * entries are zero or positive. This function is used, for example, to + * check whether refinement indicators are really all positive (or + * zero). + * + * The function obviously only makes sense if the template argument of + * this class is a real type. If it is a complex type, then an exception + * is thrown. + */ + bool is_non_negative () const; + + /** + * Checks for equality of the two vectors. + */ + template + bool operator == (const BlockVector &v) const; + + /** + * Checks for inequality of the two vectors. + */ + template + bool operator != (const BlockVector &v) const; + + /** + * Perform the inner product of two vectors. + */ + template + Number operator * (const BlockVector &V) const; + + /** + * Computes the square of the l2 norm of the vector (i.e., + * the sum of the squares of all entries among all processors). + */ + real_type norm_sqr () const; + + /** + * Computes the mean value of all the entries in the vector. + */ + Number mean_value () const; + + /** + * Returns the l1 norm of the vector (i.e., the sum of the + * absolute values of all entries among all processors). + */ + real_type l1_norm () const; + + /** + * Returns the l2 norm of the vector (i.e., square root of + * the sum of the square of all entries among all processors). + */ + real_type l2_norm () const; + + /** + * Returns the lp norm with real @p p of the vector (i.e., + * the pth root of sum of the pth power of all entries among all + * processors). + */ + real_type lp_norm (const real_type p) const; + + /** + * Returns the maximum norm of the vector (i.e., maximum absolute value + * among all entries among all processors). + */ + real_type linfty_norm () const; + + /** + * Scale each element of the vector by the given factor. * - * This function is deprecated - * and will be removed in a - * future version. Use - * operator *= and - * operator /= instead. + * This function is deprecated and will be removed in a future + * version. Use operator *= and operator /= instead. * - * @deprecated Use operator*= - * instead. + * @deprecated Use operator*= instead. */ void scale (const value_type factor) DEAL_II_DEPRECATED; /** - * Multiply each element of this - * vector by the corresponding - * element of v. + * Multiply each element of this vector by the corresponding element of + * v. */ template void scale (const BlockVector2 &v); /** - * Swap the contents of this - * vector and the other vector - * v. One could do this - * operation with a temporary - * variable and copying over the - * data elements, but this - * function is significantly more - * efficient since it only swaps - * the pointers to the data of - * the two vectors and therefore - * does not need to allocate - * temporary storage and move - * data around. + * Swap the contents of this vector and the other vector v. One + * could do this operation with a temporary variable and copying over + * the data elements, but this function is significantly more efficient + * since it only swaps the pointers to the data of the two vectors and + * therefore does not need to allocate temporary storage and move data + * around. * - * Limitation: right now this - * function only works if both - * vectors have the same number - * of blocks. If needed, the - * numbers of blocks should be + * Limitation: right now this function only works if both vectors have + * the same number of blocks. If needed, the numbers of blocks should be * exchanged, too. * - * This function is analog to the - * the swap() function of all C++ - * standard containers. Also, - * there is a global function - * swap(u,v) that simply calls - * u.swap(v), again in analogy - * to standard functions. + * This function is analog to the the swap() function of all C++ + * standard containers. Also, there is a global function swap(u,v) that + * simply calls u.swap(v), again in analogy to standard + * functions. */ void swap (BlockVector &v); @@ -488,6 +479,221 @@ namespace parallel } + template + inline + bool + BlockVector::all_zero () const + { + Assert (this->n_blocks() > 0, ExcEmptyObject()); + + // use int instead of bool. in order to make global reduction operations + // work also when MPI_Init was not called, only call MPI_Allreduce + // commands when there is more than one processor (note that reinit() + // functions handle this case correctly through the job_supports_mpi() + // query). this is the same in all the functions below + int local_result = -1; + for (unsigned int i=0; in_blocks(); ++i) + local_result = std::max(local_result, + -static_cast(this->block(i).all_zero_local())); + + if (this->block(0).partitioner->n_mpi_processes() > 1) + return -Utilities::MPI::max(local_result, + this->block(0).partitioner->get_communicator()); + else + return local_result; + } + + + + template + inline + bool + BlockVector::is_non_negative () const + { + Assert (this->n_blocks() > 0, ExcEmptyObject()); + int local_result = -1; + for (unsigned int i=0; in_blocks(); ++i) + local_result = std::max(local_result, + -static_cast(this->block(i).is_non_negative_local())); + if (this->block(0).partitioner->n_mpi_processes() > 1) + return Utilities::MPI::max(local_result, + this->block(0).partitioner->get_communicator()); + else + return local_result; + } + + + + template + template + inline + bool + BlockVector::operator == (const BlockVector &v) const + { + Assert (this->n_blocks() > 0, ExcEmptyObject()); + AssertDimension (this->n_blocks(), v.n_blocks()); + + // MPI does not support bools, so use unsigned int instead. Two vectors + // are equal if the check for non-equal fails on all processors + unsigned int local_result = 0; + for (unsigned int i=0; in_blocks(); ++i) + local_result = std::max(local_result, + static_cast(!this->block(i).vectors_equal_local(v.block(i)))); + unsigned int result = + this->block(0).partitioner->n_mpi_processes() > 1 + ? + Utilities::MPI::max(local_result, this->block(0).partitioner->get_communicator()) + : + local_result; + return result==0; + } + + + + template + template + inline + bool + BlockVector::operator != (const BlockVector &v) const + { + return !(operator == (v)); + } + + + + template + template + inline + Number + BlockVector::operator * (const BlockVector &v) const + { + Assert (this->n_blocks() > 0, ExcEmptyObject()); + AssertDimension (this->n_blocks(), v.n_blocks()); + + Number local_result = Number(); + for (unsigned int i=0; in_blocks(); ++i) + local_result += this->block(i).inner_product_local(v.block(i)); + + if (this->block(0).partitioner->n_mpi_processes() > 1) + return Utilities::MPI::sum (local_result, + this->block(0).partitioner->get_communicator()); + else + return local_result; + } + + + + template + inline + typename BlockVector::real_type + BlockVector::norm_sqr () const + { + Assert (this->n_blocks() > 0, ExcEmptyObject()); + + real_type local_result = real_type(); + for (unsigned int i=0; in_blocks(); ++i) + local_result += this->block(i).norm_sqr_local(); + + if (this->block(0).partitioner->n_mpi_processes() > 1) + return Utilities::MPI::sum (local_result, + this->block(0).partitioner->get_communicator()); + else + return local_result; + } + + + + template + inline + Number + BlockVector::mean_value () const + { + Assert (this->n_blocks() > 0, ExcEmptyObject()); + + Number local_result = Number(); + for (unsigned int i=0; in_blocks(); ++i) + local_result += this->block(i).mean_value_local()*(real_type)this->block(i).partitioner->local_size(); + + if (this->block(0).partitioner->n_mpi_processes() > 1) + return Utilities::MPI::sum (local_result, + this->block(0).partitioner->get_communicator())/ + (real_type)this->size(); + else + return local_result/(real_type)this->size(); + } + + + + template + inline + typename BlockVector::real_type + BlockVector::l1_norm () const + { + Assert (this->n_blocks() > 0, ExcEmptyObject()); + + real_type local_result = real_type(); + for (unsigned int i=0; in_blocks(); ++i) + local_result += this->block(i).l1_norm_local(); + + if (this->block(0).partitioner->n_mpi_processes() > 1) + return Utilities::MPI::sum (local_result, + this->block(0).partitioner->get_communicator()); + else + return local_result; + } + + + + template + inline + typename BlockVector::real_type + BlockVector::l2_norm () const + { + return std::sqrt(norm_sqr()); + } + + + + template + inline + typename BlockVector::real_type + BlockVector::lp_norm (const real_type p) const + { + Assert (this->n_blocks() > 0, ExcEmptyObject()); + + real_type local_result = real_type(); + for (unsigned int i=0; in_blocks(); ++i) + local_result += std::pow(this->block(i).lp_norm_local(p), p); + + if (this->block(0).partitioner->n_mpi_processes() > 1) + return std::pow (Utilities::MPI::sum(local_result, + this->block(0).partitioner->get_communicator()), + static_cast(1.0/p)); + else + return std::pow (local_result, static_cast(1.0/p)); + } + + + + template + inline + typename BlockVector::real_type + BlockVector::linfty_norm () const + { + Assert (this->n_blocks() > 0, ExcEmptyObject()); + + real_type local_result = real_type(); + for (unsigned int i=0; in_blocks(); ++i) + local_result = std::max(local_result, this->block(i).linfty_norm_local()); + + if (this->block(0).partitioner->n_mpi_processes() > 1) + return Utilities::MPI::max (local_result, + this->block(0).partitioner->get_communicator()); + else + return local_result; + } + + template inline diff --git a/deal.II/include/deal.II/lac/parallel_vector.h b/deal.II/include/deal.II/lac/parallel_vector.h index 088d0528e0..29d0b864b6 100644 --- a/deal.II/include/deal.II/lac/parallel_vector.h +++ b/deal.II/include/deal.II/lac/parallel_vector.h @@ -33,6 +33,7 @@ namespace parallel { namespace distributed { + template class BlockVector; /*! @addtogroup Vectors *@{ @@ -61,20 +62,20 @@ namespace parallel * access vector entries in the local index space with the function @p * local_element(). Locally owned indices are placed first, [0, * local_size()), and then all ghost indices follow after them - * contiguously, [local_size(), local_size()+n_ghost_indices()). + * contiguously, [local_size(), local_size()+n_ghost_entries()). * * Functions related to parallel functionality: * - The function compress() goes through the data associated * with ghost indices and communicates it to the owner process, which can * then add/set it to the correct position. This can be used e.g. after * having run an assembly routine involving ghosts that fill this vector. - * - The update_ghost_values() function imports the data from the owning - * processor to the ghost indices in order to provide read access to the - * data associated with ghosts. - * - It is possible to split the above functions into two phases, where the first - * initiates the communication and the second one finishes it. These - * functions can be used to overlap communication with computations in - * other parts of the code. + * - The update_ghost_values() function imports the data from + * the owning processor to the ghost indices in order to provide read + * access to the data associated with ghosts. + * - It is possible to split the above functions into two phases, where + * the first initiates the communication and the second one finishes + * it. These functions can be used to overlap communication with + * computations in other parts of the code. * - Of course, reduction operations (like norms) make use of collective * all-to-all MPI communications. * @@ -85,10 +86,9 @@ namespace parallel { public: /** - * Declare standard types used in all - * containers. These types parallel those in - * the C++ standard libraries - * vector<...> class. + * Declare standard types used in all containers. These types parallel + * those in the C++ standard libraries vector<...> + * class. */ typedef Number value_type; typedef value_type *pointer; @@ -100,6 +100,19 @@ namespace parallel typedef size_t size_type; typedef typename numbers::NumberTraits::real_type real_type; + /** + * A variable that indicates whether this vector + * supports distributed data storage. If true, then + * this vector also needs an appropriate compress() + * function that allows communicating recent set or + * add operations to individual elements to be communicated + * to other processors. + * + * For the current class, the variable equals + * true, since it does support parallel data storage. + */ + static const bool supports_distributed_data = true; + /** * @name 1: Basic Object-handling */ @@ -110,35 +123,27 @@ namespace parallel Vector (); /** - * Copy constructor. Uses the parallel - * partitioning of @p in_vector. + * Copy constructor. Uses the parallel partitioning of @p in_vector. */ Vector (const Vector &in_vector); /** - * Constructs a parallel vector of the given - * global size without any actual parallel - * distribution. + * Constructs a parallel vector of the given global size without any + * actual parallel distribution. */ Vector (const unsigned int size); /** - * Constructs a parallel vector. The local - * range is specified by @p locally_owned_set - * (note that this must be a contiguous - * interval, multiple intervals are not - * possible). The IndexSet @p ghost_indices - * specifies ghost indices, i.e., indices - * which one might need to read data from or - * accumulate data from. It is allowed that - * the set of ghost indices also contains the - * local range, but it does not need to. + * Constructs a parallel vector. The local range is specified by @p + * locally_owned_set (note that this must be a contiguous interval, + * multiple intervals are not possible). The IndexSet @p ghost_indices + * specifies ghost indices, i.e., indices which one might need to read + * data from or accumulate data from. It is allowed that the set of + * ghost indices also contains the local range, but it does not need to. * - * This function involves global - * communication, so it should only be called - * once for a given layout. Use the - * constructor with Vector argument to - * create additional vectors with the same + * This function involves global communication, so it should only be + * called once for a given layout. Use the constructor with + * Vector argument to create additional vectors with the same * parallel layout. */ Vector (const IndexSet &local_range, @@ -146,12 +151,10 @@ namespace parallel const MPI_Comm communicator); /** - * Create the vector based on the parallel - * partitioning described in @p - * partitioner. The input argument is a shared - * pointer, which store the partitioner data - * only once and share it between several - * vectors with the same layout. + * Create the vector based on the parallel partitioning described in @p + * partitioner. The input argument is a shared pointer, which store the + * partitioner data only once and share it between several vectors with + * the same layout. */ Vector (const std_cxx1x::shared_ptr &partitioner); @@ -161,47 +164,36 @@ namespace parallel ~Vector (); /** - * Sets the global size of the vector to @p - * size without any actual parallel - * distribution. + * Sets the global size of the vector to @p size without any actual + * parallel distribution. */ void reinit (const unsigned int size, const bool fast = false); /** - * Uses the parallel layout of the input - * vector @p in_vector and allocates memory - * for this vector. Recommended initialization - * function when several vectors with the same - * layout should be created. + * Uses the parallel layout of the input vector @p in_vector and + * allocates memory for this vector. Recommended initialization function + * when several vectors with the same layout should be created. * - * If the flag @p fast is set to false, the - * memory will be initialized with zero, - * otherwise the memory will be untouched (and - * the user must make sure to fill it with - * reasonable data before using it). + * If the flag @p fast is set to false, the memory will be initialized + * with zero, otherwise the memory will be untouched (and the user must + * make sure to fill it with reasonable data before using it). */ template void reinit(const Vector &in_vector, const bool fast = false); /** - * Initialize the vector. The local range is - * specified by @p locally_owned_set (note - * that this must be a contiguous interval, - * multiple intervals are not possible). The - * IndexSet @p ghost_indices specifies ghost - * indices, i.e., indices which one might need - * to read data from or accumulate data - * from. It is allowed that the set of ghost - * indices also contains the local range, but - * it does not need to. + * Initialize the vector. The local range is specified by @p + * locally_owned_set (note that this must be a contiguous interval, + * multiple intervals are not possible). The IndexSet @p ghost_indices + * specifies ghost indices, i.e., indices which one might need to read + * data from or accumulate data from. It is allowed that the set of + * ghost indices also contains the local range, but it does not need to. * - * This function involves global - * communication, so it should only be called - * once for a given layout. Use the @p reinit - * function with Vector argument to - * create additional vectors with the same + * This function involves global communication, so it should only be + * called once for a given layout. Use the @p reinit function with + * Vector argument to create additional vectors with the same * parallel layout. */ void reinit (const IndexSet &local_range, @@ -209,89 +201,81 @@ namespace parallel const MPI_Comm communicator); /** - * Initialize the vector given to the parallel - * partitioning described in @p - * partitioner. The input argument is a shared - * pointer, which store the partitioner data - * only once and share it between several - * vectors with the same layout. + * Initialize the vector given to the parallel partitioning described in + * @p partitioner. The input argument is a shared pointer, which store + * the partitioner data only once and share it between several vectors + * with the same layout. */ void reinit (const std_cxx1x::shared_ptr &partitioner); /** - * Swap the contents of this - * vector and the other vector - * @p v. One could do this - * operation with a temporary - * variable and copying over the - * data elements, but this - * function is significantly more - * efficient since it only swaps - * the pointers to the data of - * the two vectors and therefore - * does not need to allocate - * temporary storage and move - * data around. + * Swap the contents of this vector and the other vector @p v. One could + * do this operation with a temporary variable and copying over the data + * elements, but this function is significantly more efficient since it + * only swaps the pointers to the data of the two vectors and therefore + * does not need to allocate temporary storage and move data around. * - * This function is analog to the - * the @p swap function of all C++ - * standard containers. Also, - * there is a global function - * swap(u,v) that simply calls - * u.swap(v), again in analogy - * to standard functions. + * This function is analog to the the @p swap function of all C++ + * standard containers. Also, there is a global function + * swap(u,v) that simply calls u.swap(v), again in + * analogy to standard functions. * - * This function is virtual in - * order to allow for derived - * classes to handle memory - * separately. + * This function is virtual in order to allow for derived classes to + * handle memory separately. */ void swap (Vector &v); /** - * Assigns the vector to the parallel - * partitioning of the input vector @p - * in_vector, and copies all the data. + * Assigns the vector to the parallel partitioning of the input vector + * @p in_vector, and copies all the data. */ Vector & operator = (const Vector &in_vector); /** - * Assigns the vector to the parallel - * partitioning of the input vector @p - * in_vector, and copies all the data. + * Assigns the vector to the parallel partitioning of the input vector + * @p in_vector, and copies all the data. */ template Vector & operator = (const Vector &in_vector); /** - * This method copies the local range from - * another vector with the same local range, - * but possibly different layout of ghost - * indices. + * This method copies the local range from another vector with the same + * local range, but possibly different layout of ghost indices. */ void copy_from (const Vector &in_vector, const bool call_update_ghost_values = false); /** - * Sets all elements of the vector to the - * scalar @p s. If the scalar is zero, also - * ghost elements are set to zero, otherwise - * they remain unchanged. + * Sets all elements of the vector to the scalar @p s. If the scalar is + * zero, also ghost elements are set to zero, otherwise they remain + * unchanged. */ Vector &operator = (const Number s); /** - * This function copies the data that has - * accumulated in the data buffer for ghost - * indices to the owning processor. + * This function copies the data that has accumulated in the data buffer + * for ghost indices to the owning processor. For the meaning of the + * argument @p operation, see the entry on @ref GlossCompress + * "Compressing distributed vectors and matrices" in the glossary. + * + * There are two variants for this function. If called with argument @p + * VectorOperation::add adds all the data accumulated in ghost elements + * to the respective elements on the owning processor and clears the + * ghost array afterwards. If called with argument @p + * VectorOperation::insert, a set operation is performed. Since setting + * elements in a vector with ghost elements is ambiguous (as one can set + * both the element on the ghost site as well as the owning site), this + * operation makes the assumption that all data is set correctly on the + * owning processor. Upon call of compress(VectorOperation::insert), all + * ghost entries are therefore simply zeroed out (using + * zero_ghost_values()). In debug mode, a check is performed that makes + * sure that the data set is actually consistent between processors, + * i.e., whenever a non-zero ghost element is found, it is compared to + * the value on the owning processor and an exception is thrown if these + * elements do not agree. * - * For the meaning of this argument, - * see the entry on @ref - * GlossCompress "Compressing - * distributed vectors and matrices" - * in the glossary. */ void compress (::dealii::VectorOperation::values operation); @@ -301,121 +285,97 @@ namespace parallel void compress () DEAL_II_DEPRECATED; /** - * Fills the data field for ghost indices with - * the values stored in the respective - * positions of the owning processor. This - * function is needed before reading from - * ghosts. The function is @p const even - * though ghost data is changed. This is - * needed to allow functions with a @p const - * vector to perform the data exchange without - * creating temporaries. + * Fills the data field for ghost indices with the values stored in the + * respective positions of the owning processor. This function is needed + * before reading from ghosts. The function is @p const even though + * ghost data is changed. This is needed to allow functions with a @p + * const vector to perform the data exchange without creating + * temporaries. */ void update_ghost_values () const; /** - * Initiates communication for the @p - * compress() function with non-blocking - * communication. This function does not wait - * for the transfer to finish, in order to - * allow for other computations during the - * time it takes until all data arrives. + * Initiates communication for the @p compress() function with + * non-blocking communication. This function does not wait for the + * transfer to finish, in order to allow for other computations during + * the time it takes until all data arrives. * - * Before the data is actually exchanged, the - * function must be followed by a call to @p - * compress_finish(). + * Before the data is actually exchanged, the function must be followed + * by a call to @p compress_finish(). * - * In case this function is called for more - * than one vector before @p - * compress_finish() is invoked, it is - * mandatory to specify a unique - * communication channel to each such call, in - * order to avoid several messages with the - * same ID that will corrupt this operation. - */ - void compress_start (const unsigned int communication_channel = 0); - - /** - * For all requests that have been initiated - * in compress_start, wait for the - * communication to finish. Once it is - * finished, add or set the data (depending on - * whether @p add_ghost_data is @p true or @p - * false) to the respective positions in the - * owning processor, and clear the contents in - * the ghost data fields. The meaning of - * this argument is the same as in compress(). + * In case this function is called for more than one vector before @p + * compress_finish() is invoked, it is mandatory to specify a unique + * communication channel to each such call, in order to avoid several + * messages with the same ID that will corrupt this operation. + */ + void compress_start (const unsigned int communication_channel = 0, + ::dealii::VectorOperation::values operation = VectorOperation::add); + + /** + * For all requests that have been initiated in compress_start, wait for + * the communication to finish. Once it is finished, add or set the data + * (depending on the flag operation) to the respective positions in the + * owning processor, and clear the contents in the ghost data + * fields. The meaning of this argument is the same as in compress(). * - * Must follow a call to the @p compress_start - * function. + * Must follow a call to the @p compress_start function. */ - void compress_finish (const bool add_ghost_data = true); + void compress_finish (::dealii::VectorOperation::values operation); + /** + * @deprecated: use compress_finish(VectorOperation::values) instead. + */ + void compress_finish (const bool add_ghost_data = true) DEAL_II_DEPRECATED; /** - * Initiates communication for the @p - * update_ghost_values() function with non-blocking - * communication. This function does not wait - * for the transfer to finish, in order to - * allow for other computations during the - * time it takes until all data arrives. + * Initiates communication for the @p update_ghost_values() function + * with non-blocking communication. This function does not wait for the + * transfer to finish, in order to allow for other computations during + * the time it takes until all data arrives. * - * Before the data is actually exchanged, the - * function must be followed by a call to @p - * update_ghost_values_finish(). + * Before the data is actually exchanged, the function must be followed + * by a call to @p update_ghost_values_finish(). * - * In case this function is called for more - * than one vector before @p - * update_ghost_values_finish() is invoked, it is - * mandatory to specify a unique communication - * channel to each such call, in order to - * avoid several messages with the same ID - * that will corrupt this operation. + * In case this function is called for more than one vector before @p + * update_ghost_values_finish() is invoked, it is mandatory to specify a + * unique communication channel to each such call, in order to avoid + * several messages with the same ID that will corrupt this operation. */ void update_ghost_values_start (const unsigned int communication_channel = 0) const; /** - * For all requests that have been started in - * update_ghost_values_start, wait for the communication - * to finish. + * For all requests that have been started in update_ghost_values_start, + * wait for the communication to finish. * - * Must follow a call to the @p - * update_ghost_values_start function before reading - * data from ghost indices. + * Must follow a call to the @p update_ghost_values_start function + * before reading data from ghost indices. */ void update_ghost_values_finish () const; /** - * This method zeros the entries on ghost - * dofs, but does not touch locally owned - * DoFs. + * This method zeros the entries on ghost dofs, but does not touch + * locally owned DoFs. */ void zero_out_ghosts (); /** - * Return whether the vector contains only - * elements with value zero. This function - * is mainly for internal consistency - * checks and should seldom be used when - * not in debug mode since it uses quite - * some time. + * Return whether the vector contains only elements with value + * zero. This function is mainly for internal consistency checks and + * should seldom be used when not in debug mode since it uses quite some + * time. */ bool all_zero () const; /** - * Return @p true if the vector has no - * negative entries, i.e. all entries are - * zero or positive. This function is - * used, for example, to check whether - * refinement indicators are really all - * positive (or zero). + * Return @p true if the vector has no negative entries, i.e. all + * entries are zero or positive. This function is used, for example, to + * check whether refinement indicators are really all positive (or + * zero). * - * The function obviously only makes - * sense if the template argument of this - * class is a real type. If it is a - * complex type, then an exception is - * thrown. + * The function obviously only makes sense if the template argument of + * this class is a real type. If it is a complex type, then an exception + * is thrown. */ bool is_non_negative () const; @@ -438,119 +398,118 @@ namespace parallel Number operator * (const Vector &V) const; /** - * Computes the square of the l2 - * norm of the vector (i.e., the sum of the - * squares of all entries among all - * processors). + * Computes the square of the l2 norm of the vector (i.e., + * the sum of the squares of all entries among all processors). */ real_type norm_sqr () const; /** - * Computes the mean value of all the entries - * in the vector. + * Computes the mean value of all the entries in the vector. */ Number mean_value () const; /** - * Returns the l1 norm of the - * vector (i.e., the sum of the absolute - * values of all entries among all - * processors). + * Returns the l1 norm of the vector (i.e., the sum of the + * absolute values of all entries among all processors). */ real_type l1_norm () const; /** - * Returns the l2 norm of the - * vector (i.e., square root of the sum of the - * square of all entries among all - * processors). + * Returns the l2 norm of the vector (i.e., square root of + * the sum of the square of all entries among all processors). */ real_type l2_norm () const; /** - * Returns the lp norm with real @p - * p of the vector (i.e., the pth root of sum - * of the pth power of all entries among all + * Returns the lp norm with real @p p of the vector (i.e., + * the pth root of sum of the pth power of all entries among all * processors). */ real_type lp_norm (const real_type p) const; /** - * Returns the maximum norm of the vector - * (i.e., maximum absolute value among all - * entries among all processors). + * Returns the maximum norm of the vector (i.e., maximum absolute value + * among all entries among all processors). */ real_type linfty_norm () const; /** - * Returns the global size of the vector, - * equal to the sum of the number of locally - * owned indices among all the processors. + * Returns the global size of the vector, equal to the sum of the number + * of locally owned indices among all the processors. */ types::global_dof_index size () const; /** - * Returns the local size of the vector, i.e., - * the number of indices owned locally. + * Returns the local size of the vector, i.e., the number of indices + * owned locally. */ unsigned int local_size() const; /** - * Returns the half-open interval that - * specifies the locally owned range of the - * vector. Note that local_size() == - * local_range().second - + * Returns the half-open interval that specifies the locally owned range + * of the vector. Note that local_size() == local_range().second - * local_range().first. */ std::pair local_range () const; /** - * Returns true if the given global index is - * in the local range of this processor. + * 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 number of ghost elements - * present on the vector. + * Return an index set that describes which elements of this vector + * are owned by the current processor. Note that this index set does + * not include elements this vector may store locally as ghost + * elements but that are in fact owned by another processor. + * As a consequence, the index sets returned on different + * processors if this is a distributed vector will form disjoint + * sets that add up to the complete index set. + * Obviously, if a vector is created on only one processor, then + * the result would satisfy + * @code + * vec.locally_owned_elements() == complete_index_set (vec.size()) + * @endcode + */ + IndexSet locally_owned_elements () const; + + /** + * Returns the number of ghost elements present on the vector. */ unsigned int n_ghost_entries () 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. + * 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; /** - * Make the @p Vector class a bit like - * the vector<> class of the C++ - * standard library by returning - * iterators to the start and end of the - * locally owned elements of this vector. + * Make the @p Vector class a bit like the vector<> class of + * the C++ standard library by returning iterators to the start and end + * of the locally owned elements of this vector. + * + * It holds that end() - begin() == local_size(). */ iterator begin (); /** - * Return constant iterator to the start of - * the vector. + * Return constant iterator to the start of the locally owned elements + * of the vector. */ const_iterator begin () const; /** - * Return an iterator pointing to the - * element past the end of the array of - * locally owned entries. + * Return an iterator pointing to the element past the end of the array + * of locally owned entries. */ iterator end (); /** - * Return a constant iterator pointing to - * the element past the end of the array - * of the locally owned entries. + * Return a constant iterator pointing to the element past the end of + * the array of the locally owned entries. */ const_iterator end () const; //@} @@ -562,69 +521,62 @@ namespace parallel //@{ /** - * Read access to the data in the - * position corresponding to @p - * global_index. The index must be - * either in the local range of the - * vector or be specified as a ghost - * index at construction. + * Read access to the data in the position corresponding to @p + * global_index. The index must be either in the local range of the + * vector or be specified as a ghost index at construction. + * + * Performance: O(1) for locally owned elements that represent + * a contiguous range and O(log(nranges)) for ghost + * elements (quite fast, but slower than local_element()). */ Number operator () (const types::global_dof_index global_index) const; /** - * Read and write access to the data - * in the position corresponding to - * @p global_index. The index must be - * either in the local range of the - * vector or be specified as a ghost - * index at construction. + * Read and write access to the data in the position corresponding to @p + * global_index. The index must be either in the local range of the + * vector or be specified as a ghost index at construction. + * + * Performance: O(1) for locally owned elements that represent + * a contiguous range and O(log(nranges)) for ghost + * elements (quite fast, but slower than local_element()). */ Number &operator () (const types::global_dof_index global_index); /** - * Read access to the data in the - * position corresponding to @p - * global_index. The index must be - * either in the local range of the - * vector or be specified as a ghost - * index at construction. + * Read access to the data in the position corresponding to @p + * global_index. The index must be either in the local range of the + * vector or be specified as a ghost index at construction. * - * This function does the same thing - * as operator(). + * This function does the same thing as operator(). */ Number operator [] (const types::global_dof_index global_index) const; /** - * Read and write access to the data - * in the position corresponding to - * @p global_index. The index must be - * either in the local range of the - * vector or be specified as a ghost - * index at construction. + * Read and write access to the data in the position corresponding to @p + * global_index. The index must be either in the local range of the + * vector or be specified as a ghost index at construction. * - * This function does the same thing - * as operator(). + * This function does the same thing as operator(). */ Number &operator [] (const types::global_dof_index global_index); /** - * Read access to the data field specified by - * @p local_index. Locally owned indices can - * be accessed with indices - * [0,local_size), and ghost - * indices with indices - * [local_size,local_size+ - * n_ghost_entries]. + * Read access to the data field specified by @p local_index. Locally + * owned indices can be accessed with indices + * [0,local_size), and ghost indices with indices + * [local_size,local_size+ n_ghost_entries]. + * + * Performance: Direct array access (fast). */ Number local_element (const unsigned int local_index) const; /** - * Read and write access to the data field - * specified by @p local_index. Locally owned - * indices can be accessed with indices - * [0,local_size), and ghost - * indices with indices + * Read and write access to the data field specified by @p + * local_index. Locally owned indices can be accessed with indices + * [0,local_size), and ghost indices with indices * [local_size,local_size+n_ghosts]. + * + * Performance: Direct array access (fast). */ Number &local_element (const unsigned int local_index); //@} @@ -636,48 +588,35 @@ namespace parallel //@{ /** - * Add the given vector to the present - * one. + * Add the given vector to the present one. */ Vector &operator += (const Vector &V); /** - * Subtract the given vector from the - * present one. + * Subtract the given vector from the present one. */ Vector &operator -= (const Vector &V); /** - * A collective add operation: - * This funnction adds a whole - * set of values stored in @p - * values to the vector - * components specified by @p - * indices. + * A collective add operation: This funnction adds a whole set of values + * stored in @p values to the vector components specified by @p indices. */ template void add (const std::vector &indices, const std::vector &values); /** - * This is a second collective - * add operation. As a - * difference, this function - * takes a deal.II vector of - * values. + * This is a second collective add operation. As a difference, this + * function takes a deal.II vector of values. */ template void add (const std::vector &indices, const ::dealii::Vector &values); /** - * Take an address where - * n_elements are stored - * contiguously and add them into - * the vector. Handles all cases - * which are not covered by the - * other two add() - * functions above. + * Take an address where n_elements are stored contiguously and + * add them into the vector. Handles all cases which are not covered by + * the other two add() functions above. */ template void add (const unsigned int n_elements, @@ -685,42 +624,37 @@ namespace parallel const OtherNumber *values); /** - * Addition of @p s to all - * components. Note that @p s is a - * scalar and not a vector. + * Addition of @p s to all components. Note that @p s is a scalar and + * not a vector. */ void add (const Number s); /** - * Simple vector addition, equal to the - * operator +=. + * Simple vector addition, equal to the operator +=. */ void add (const Vector &V); /** - * Simple addition of a multiple of a - * vector, i.e. *this += a*V. + * Simple addition of a multiple of a vector, i.e. *this += + * a*V. */ void add (const Number a, const Vector &V); /** - * Multiple addition of scaled vectors, - * i.e. *this += a*V+b*W. + * Multiple addition of scaled vectors, i.e. *this += a*V+b*W. */ void add (const Number a, const Vector &V, const Number b, const Vector &W); /** - * Scaling and simple vector addition, - * i.e. - * *this = s*(*this)+V. + * Scaling and simple vector addition, i.e. *this = + * s*(*this)+V. */ void sadd (const Number s, const Vector &V); /** - * Scaling and simple addition, i.e. - * *this = s*(*this)+a*V. + * Scaling and simple addition, i.e. *this = s*(*this)+a*V. */ void sadd (const Number s, const Number a, @@ -736,8 +670,8 @@ namespace parallel const Vector &W); /** - * Scaling and multiple addition. - * *this = s*(*this)+a*V + b*W + c*X. + * Scaling and multiple addition. *this = s*(*this)+a*V + b*W + + * c*X. */ void sadd (const Number s, const Number a, @@ -748,51 +682,35 @@ namespace parallel const Vector &X); /** - * Scale each element of the - * vector by the given factor. + * Scale each element of the vector by the given factor. * - * @deprecated This function is deprecated - * and will be removed in a - * future version. Use - * operator *= and - * operator /= instead. + * @deprecated This function is deprecated and will be removed in a + * future version. Use operator *= and operator /= + * instead. */ void scale (const Number factor) DEAL_II_DEPRECATED; - /** - * Scale each element of the - * vector by a constant - * value. + * Scale each element of the vector by a constant value. */ Vector &operator *= (const Number factor); /** - * Scale each element of the - * vector by the inverse of the - * given value. + * Scale each element of the vector by the inverse of the given value. */ Vector &operator /= (const Number factor); /** - * Scale each element of this - * vector by the corresponding - * element in the argument. This - * function is mostly meant to - * simulate multiplication (and - * immediate re-assignment) by a - * diagonal scaling matrix. + * Scale each element of this vector by the corresponding element in the + * argument. This function is mostly meant to simulate multiplication + * (and immediate re-assignment) by a diagonal scaling matrix. */ void scale (const Vector &scaling_factors); /** - * Scale each element of this - * vector by the corresponding - * element in the argument. This - * function is mostly meant to - * simulate multiplication (and - * immediate re-assignment) by a - * diagonal scaling matrix. + * Scale each element of this vector by the corresponding element in the + * argument. This function is mostly meant to simulate multiplication + * (and immediate re-assignment) by a diagonal scaling matrix. */ template void scale (const Vector &scaling_factors); @@ -822,20 +740,14 @@ namespace parallel const Number c, const Vector &w); /** - * Compute the elementwise ratio of the - * two given vectors, that is let - * this[i] = a[i]/b[i]. This is - * useful for example if you want to - * compute the cellwise ratio of true to - * estimated error. + * Compute the elementwise ratio of the two given vectors, that is let + * this[i] = a[i]/b[i]. This is useful for example if you want + * to compute the cellwise ratio of true to estimated error. * - * This vector is appropriately - * scaled to hold the result. + * This vector is appropriately scaled to hold the result. * - * If any of the b[i] is - * zero, the result is - * undefined. No attempt is made - * to catch such situations. + * If any of the b[i] is zero, the result is undefined. No + * attempt is made to catch such situations. */ void ratio (const Vector &a, const Vector &b); @@ -847,29 +759,26 @@ namespace parallel */ //@{ /** - * 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. + * Return a reference to the MPI communicator object in use with this + * vector. + */ + const MPI_Comm &get_mpi_communicator () 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 partitioners_are_compatible (const Utilities::MPI::Partitioner &part) const; /** - * Prints the vector to the output stream @p - * out. + * Prints the vector to the output stream @p out. */ void print (std::ostream &out, const unsigned int precision = 3, @@ -877,99 +786,134 @@ namespace parallel const bool across = true) const; /** - * Returns the memory consumption of this - * class in bytes. + * Returns the memory consumption of this class in bytes. */ std::size_t memory_consumption () const; //@} private: /** - * Shared pointer to store the parallel - * partitioning information. This information - * can be shared between several vectors that - * have the same partitioning. + * Local part of all_zero(). + */ + bool all_zero_local () const; + + /** + * Local part of is_non_negative(). + */ + bool is_non_negative_local () const; + + /** + * Local part of operator==. + */ + template + bool vectors_equal_local (const Vector &v) const; + + /** + * Local part of the inner product of two vectors. + */ + template + Number inner_product_local (const Vector &V) const; + + /** + * Local part of norm_sqr(). + */ + real_type norm_sqr_local () const; + + /** + * Local part of mean_value(). + */ + Number mean_value_local () const; + + /** + * Local part of l1_norm(). + */ + real_type l1_norm_local () const; + + /** + * Local part of lp_norm(). + */ + real_type lp_norm_local (const real_type p) const; + + /** + * Local part of linfty_norm(). + */ + real_type linfty_norm_local () const; + + /** + * Shared pointer to store the parallel partitioning information. This + * information can be shared between several vectors that have the same + * partitioning. */ std_cxx1x::shared_ptr partitioner; /** - * The size that is currently allocated in the - * val array. + * The size that is currently allocated in the val array. */ unsigned int allocated_size; /** - * Pointer to the array of - * local elements of this vector. + * Pointer to the array of local elements of this vector. */ Number *val; /** - * Temporary storage that holds the data that - * is sent to this processor in @p compress() - * or sent from this processor in @p + * Temporary storage that holds the data that is sent to this processor + * in @p compress() or sent from this processor in @p * update_ghost_values. */ mutable Number *import_data; /** - * Provide this class with all functionality - * of ::dealii::Vector by creating a - * VectorView object. + * Provide this class with all functionality of ::dealii::Vector by + * creating a VectorView object. */ VectorView vector_view; #ifdef DEAL_II_WITH_MPI /** - * A vector that collects all requests from @p - * compress() operations. This class uses - * persistent MPI communicators, i.e., the - * communication channels are stored during - * successive calls to a given function. This - * reduces the overhead involved with setting - * up the MPI machinery, but it does not - * remove the need for a receive operation to - * be posted before the data can actually be - * sent. + * A vector that collects all requests from @p compress() + * operations. This class uses persistent MPI communicators, i.e., the + * communication channels are stored during successive calls to a given + * function. This reduces the overhead involved with setting up the MPI + * machinery, but it does not remove the need for a receive operation to + * be posted before the data can actually be sent. */ std::vector compress_requests; /** - * A vector that collects all requests from @p - * update_ghost_values() operations. This class uses - * persistent MPI communicators. + * A vector that collects all requests from @p update_ghost_values() + * operations. This class uses persistent MPI communicators. */ mutable std::vector update_ghost_values_requests; #endif /** - * A lock that makes sure that - * the @p compress and @p - * update_ghost_values functions - * give reasonable results also - * when used with several - * threads. + * A lock that makes sure that the @p compress and @p + * update_ghost_values functions give reasonable results also when used + * with several threads. */ mutable Threads::Mutex mutex; /** - * A helper function that clears the - * compress_requests and update_ghost_values_requests - * field. Used in reinit functions. + * A helper function that clears the compress_requests and + * update_ghost_values_requests field. Used in reinit functions. */ void clear_mpi_requests (); /** - * A helper function that is used to resize - * the val array. + * A helper function that is used to resize the val array. */ void resize_val (const unsigned int new_allocated_size); /* - * Make all other vector types - * friends. + * Make all other vector types friends. */ template friend class Vector; + + /** + * Make BlockVector type friends. + */ + template friend class BlockVector; }; /*@}*/ @@ -1140,13 +1084,12 @@ namespace parallel void Vector::compress (::dealii::VectorOperation::values operation) { - compress_start (); - if (operation == ::dealii::VectorOperation::insert) - compress_finish (false); - else - compress_finish (true); + compress_start (0, operation); + compress_finish(operation); } + + template inline void @@ -1156,6 +1099,20 @@ namespace parallel } + + template + inline + void + Vector::compress_finish (const bool add_value) + { + if (add_value) + compress_finish(VectorOperation::add); + else + compress_finish(VectorOperation::insert); + } + + + template inline void @@ -1179,19 +1136,42 @@ namespace parallel + template + inline + bool + Vector::all_zero_local () const + { + return partitioner->local_size()>0 ? vector_view.all_zero () : true; + } + + + template inline bool Vector::all_zero () const { - // use int instead of bool - int local_result = (partitioner->local_size()>0 ? - -vector_view.all_zero () : -1); + // use int instead of bool. in order to make global reduction operations + // work also when MPI_Init was not called, only call MPI_Allreduce + // commands when there is more than one processor (note that reinit() + // functions handle this case correctly through the job_supports_mpi() + // query). this is the same in all the functions below + int local_result = -static_cast(all_zero_local()); if (partitioner->n_mpi_processes() > 1) return -Utilities::MPI::max(local_result, partitioner->get_communicator()); else - return local_result; + return -local_result; + } + + + + template + inline + bool + Vector::is_non_negative_local () const + { + return partitioner->local_size()>0 ? vector_view.is_non_negative () : true; } @@ -1201,17 +1181,12 @@ namespace parallel bool Vector::is_non_negative () const { - // use int instead of bool. in order to make this operation work also - // when MPI_Init was not called, only call MPI_Allreduce commands when - // there is more than one processor (note that reinit() functions handle - // this case correctly through the job_supports_mpi() query) - int local_result = (partitioner->local_size()>0 ? - -vector_view.is_non_negative () : -1); + int local_result = -static_cast(is_non_negative_local()); if (partitioner->n_mpi_processes() > 1) - return -Utilities::MPI::max(local_result, - partitioner->get_communicator()); + return -Utilities::MPI::max(local_result, + partitioner->get_communicator()); else - return local_result; + return -local_result; } @@ -1220,16 +1195,24 @@ namespace parallel template inline bool - Vector::operator == (const Vector &v) const + Vector::vectors_equal_local (const Vector &v) const { - AssertDimension (local_size(), v.local_size()); + return partitioner->local_size()>0 ? + vector_view.template operator == (v.vector_view) + : true; + } + + + template + template + inline + bool + Vector::operator == (const Vector &v) const + { // MPI does not support bools, so use unsigned int instead. Two vectors // are equal if the check for non-equal fails on all processors - unsigned int local_result = (partitioner->local_size()>0 ? - vector_view.template operator != - (v.vector_view) - : 0 ); + unsigned int local_result = static_cast(!vectors_equal_local(v)); unsigned int result = partitioner->n_mpi_processes() > 1 ? @@ -1252,15 +1235,28 @@ namespace parallel + template + template + inline + Number + Vector::inner_product_local(const Vector &V) const + { + // on some processors, the size might be zero, which is not allowed by + // the dealii::Vector class. Therefore, insert a check here + return (partitioner->local_size()>0 ? + vector_view.operator* (V.vector_view) + : Number()); + } + + + template template inline Number Vector::operator * (const Vector &V) const { - Number local_result = (partitioner->local_size()>0 ? - vector_view.operator* (V.vector_view) - : 0); + Number local_result = inner_product_local(V); if (partitioner->n_mpi_processes() > 1) return Utilities::MPI::sum (local_result, partitioner->get_communicator()); @@ -1270,17 +1266,22 @@ namespace parallel + template + inline + typename Vector::real_type + Vector::norm_sqr_local () const + { + return partitioner->local_size()>0 ? vector_view.norm_sqr() : real_type(); + } + + + template inline typename Vector::real_type Vector::norm_sqr () const { - // on some processors, the size might be zero, - // which is not allowed by the base - // class. Therefore, insert a check here - real_type local_result = (partitioner->local_size()>0 ? - vector_view.norm_sqr() - : 0); + real_type local_result = norm_sqr_local(); if (partitioner->n_mpi_processes() > 1) return Utilities::MPI::sum(local_result, partitioner->get_communicator()); @@ -1290,33 +1291,52 @@ namespace parallel + template + inline + Number + Vector::mean_value_local () const + { + Assert (partitioner->size()!=0, ExcEmptyObject()); + return (partitioner->local_size()>0 ? + vector_view.mean_value() + : Number()); + } + + + template inline Number Vector::mean_value () const { - Number local_result = - (partitioner->local_size()>0 ? - vector_view.mean_value() - : 0) - *((real_type)partitioner->local_size()/(real_type)partitioner->size()); + Number local_result = mean_value_local(); if (partitioner->n_mpi_processes() > 1) - return Utilities::MPI::sum (local_result, - partitioner->get_communicator()); + return Utilities::MPI::sum (local_result * + (real_type)partitioner->local_size(), + partitioner->get_communicator()) + /(real_type)partitioner->size(); else return local_result; } + template + inline + typename Vector::real_type + Vector::l1_norm_local () const + { + return partitioner->local_size()>0 ? vector_view.l1_norm() : real_type(); + } + + + template inline typename Vector::real_type Vector::l1_norm () const { - real_type local_result = (partitioner->local_size()>0 ? - vector_view.l1_norm() - : 0); + real_type local_result = l1_norm_local(); if (partitioner->n_mpi_processes() > 1) return Utilities::MPI::sum(local_result, partitioner->get_communicator()); @@ -1336,14 +1356,22 @@ namespace parallel + template + inline + typename Vector::real_type + Vector::lp_norm_local (const real_type p) const + { + return partitioner->local_size()>0 ? vector_view.lp_norm(p) : real_type(); + } + + + template inline typename Vector::real_type Vector::lp_norm (const real_type p) const { - const real_type local_result = (partitioner->local_size()>0 ? - vector_view.lp_norm(p) - : 0); + const real_type local_result = lp_norm_local(p); if (partitioner->n_mpi_processes() > 1) return std::pow (Utilities::MPI::sum(std::pow(local_result,p), partitioner->get_communicator()), @@ -1354,14 +1382,22 @@ namespace parallel + template + inline + typename Vector::real_type + Vector::linfty_norm_local () const + { + return partitioner->local_size()>0 ? vector_view.linfty_norm() : real_type(); + } + + + template inline typename Vector::real_type Vector::linfty_norm () const { - const real_type local_result = (partitioner->local_size()>0 ? - vector_view.linfty_norm() - : 0); + const real_type local_result = linfty_norm_local(); if (partitioner->n_mpi_processes() > 1) return Utilities::MPI::max (local_result, partitioner->get_communicator()); @@ -1409,6 +1445,20 @@ namespace parallel } + template + inline + IndexSet + Vector::locally_owned_elements() const + { + IndexSet is (size()); + + const std::pair x = local_range(); + is.add_range (x.first, x.second); + + return is; + } + + template inline @@ -1931,6 +1981,16 @@ namespace parallel + template + inline + const MPI_Comm& + Vector::get_mpi_communicator() const + { + return partitioner->get_communicator(); + } + + + template inline bool diff --git a/deal.II/include/deal.II/lac/parallel_vector.templates.h b/deal.II/include/deal.II/lac/parallel_vector.templates.h index 5622d55ba2..d71c786ea6 100644 --- a/deal.II/include/deal.II/lac/parallel_vector.templates.h +++ b/deal.II/include/deal.II/lac/parallel_vector.templates.h @@ -1,7 +1,7 @@ //----------%----------------------------------------------------------------- // $Id$ // -// Copyright (C) 2011, 2012 by the deal.II authors +// Copyright (C) 2011, 2012, 2013 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -123,12 +123,10 @@ namespace parallel { delete [] import_data; - // do not reallocate import_data directly, but - // only upon request. It is only used as - // temporary storage for compress() and - // update_ghost_values, and we might have vectors where - // we never call these methods and hence do - // not need to have the storage. + // do not reallocate import_data directly, but only upon request. It + // is only used as temporary storage for compress() and + // update_ghost_values, and we might have vectors where we never + // call these methods and hence do not need to have the storage. import_data = 0; } } @@ -171,12 +169,10 @@ namespace parallel { delete [] import_data; - // do not reallocate import_data directly, but - // only upon request. It is only used as - // temporary storage for compress() and - // update_ghost_values, and we might have vectors where - // we never call these methods and hence do - // not need to have the storage. + // do not reallocate import_data directly, but only upon request. It + // is only used as temporary storage for compress() and + // update_ghost_values, and we might have vectors where we never + // call these methods and hence do not need to have the storage. import_data = 0; } } @@ -200,9 +196,20 @@ namespace parallel template void - Vector::compress_start (const unsigned int counter) + Vector::compress_start (const unsigned int counter, + ::dealii::VectorOperation::values operation) { #ifdef DEAL_II_WITH_MPI + + // nothing to do for insert (only need to zero ghost entries in + // compress_finish(). in debug mode we still want to check consistency + // of the inserted data, therefore the communication is still + // initialized +#ifndef DEBUG + if (operation == VectorOperation::insert) + return; +#endif + const Utilities::MPI::Partitioner &part = *partitioner; // nothing to do when we neither have import @@ -272,18 +279,22 @@ namespace parallel ierr = MPI_Startall(compress_requests.size(),&compress_requests[0]); Assert (ierr == MPI_SUCCESS, ExcInternalError()); } -#else + +#else // ifdef DEAL_II_WITH_MPI (void)counter; + (void)operation; #endif + } template void - Vector::compress_finish (const bool add_ghost_data) + Vector::compress_finish (::dealii::VectorOperation::values operation) { #ifdef DEAL_II_WITH_MPI + const Utilities::MPI::Partitioner &part = *partitioner; // nothing to do when we neither have import @@ -297,11 +308,12 @@ namespace parallel const unsigned int n_import_targets = part.import_targets().size(); const unsigned int n_ghost_targets = part.ghost_targets().size(); - AssertDimension (n_ghost_targets+n_import_targets, - compress_requests.size()); + if (operation != dealii::VectorOperation::insert) + AssertDimension (n_ghost_targets+n_import_targets, + compress_requests.size()); // first wait for the receive to complete - if (n_import_targets > 0) + if (compress_requests.size() > 0 && n_import_targets > 0) { int ierr; ierr = MPI_Waitall (n_import_targets, &compress_requests[0], @@ -312,33 +324,40 @@ namespace parallel std::vector >::const_iterator my_imports = part.import_indices().begin(); - // If add_ghost_data is set, add the imported - // data to the local values. If not, set the - // vector entries. - if (add_ghost_data == true) + // If the operation is no insertion, add the imported data to the + // local values. For insert, nothing is done here (but in debug mode + // we assert that the specified value is either zero or matches with + // the ones already present + if (operation != dealii::VectorOperation::insert) for ( ; my_imports!=part.import_indices().end(); ++my_imports) for (unsigned int j=my_imports->first; jsecond; j++) local_element(j) += *read_position++; else for ( ; my_imports!=part.import_indices().end(); ++my_imports) - for (unsigned int j=my_imports->first; jsecond; j++) - local_element(j) = *read_position++; + for (unsigned int j=my_imports->first; jsecond; + j++, read_position++) + Assert(*read_position == 0. || + std::abs(local_element(j) - *read_position) < + std::abs(local_element(j)) * 100. * + std::numeric_limits::epsilon(), + ExcMessage("Inserted elements do not match.")); AssertDimension(read_position-import_data,part.n_import_indices()); } - if (n_ghost_targets > 0) + if (compress_requests.size() > 0 && n_ghost_targets > 0) { int ierr; ierr = MPI_Waitall (n_ghost_targets, &compress_requests[n_import_targets], MPI_STATUSES_IGNORE); Assert (ierr == MPI_SUCCESS, ExcInternalError()); - zero_out_ghosts (); } else AssertDimension (part.n_ghost_indices(), 0); + + zero_out_ghosts (); #else - (void)add_ghost_data; + (void)operation; #endif } diff --git a/deal.II/include/deal.II/lac/petsc_parallel_vector.h b/deal.II/include/deal.II/lac/petsc_parallel_vector.h index f2a3203eca..0ace189356 100644 --- a/deal.II/include/deal.II/lac/petsc_parallel_vector.h +++ b/deal.II/include/deal.II/lac/petsc_parallel_vector.h @@ -154,6 +154,19 @@ namespace PETScWrappers { public: /** + * A variable that indicates whether this vector + * supports distributed data storage. If true, then + * this vector also needs an appropriate compress() + * function that allows communicating recent set or + * add operations to individual elements to be communicated + * to other processors. + * + * For the current class, the variable equals + * true, since it does support parallel data storage. + */ + static const bool supports_distributed_data = true; + + /** * Default constructor. Initialize the * vector as empty. */ diff --git a/deal.II/include/deal.II/lac/petsc_vector.h b/deal.II/include/deal.II/lac/petsc_vector.h index c7909f1f0c..7193166e27 100644 --- a/deal.II/include/deal.II/lac/petsc_vector.h +++ b/deal.II/include/deal.II/lac/petsc_vector.h @@ -47,6 +47,21 @@ namespace PETScWrappers class Vector : public VectorBase { public: + /** + * A variable that indicates whether this vector + * supports distributed data storage. If true, then + * this vector also needs an appropriate compress() + * function that allows communicating recent set or + * add operations to individual elements to be communicated + * to other processors. + * + * For the current class, the variable equals + * false, since it does not support parallel data storage. + * If you do need parallel data storage, use + * PETScWrappers::MPI::Vector. + */ + static const bool supports_distributed_data = false; + /** * Default constructor. Initialize the * vector as empty. diff --git a/deal.II/include/deal.II/lac/petsc_vector_base.h b/deal.II/include/deal.II/lac/petsc_vector_base.h index 49ad8674ad..4222be10e1 100644 --- a/deal.II/include/deal.II/lac/petsc_vector_base.h +++ b/deal.II/include/deal.II/lac/petsc_vector_base.h @@ -392,6 +392,22 @@ namespace PETScWrappers */ bool in_local_range (const unsigned int index) const; + /** + * Return an index set that describes which elements of this vector + * are owned by the current processor. Note that this index set does + * not include elements this vector may store locally as ghost + * elements but that are in fact owned by another processor. + * As a consequence, the index sets returned on different + * processors if this is a distributed vector will form disjoint + * sets that add up to the complete index set. + * Obviously, if a vector is created on only one processor, then + * the result would satisfy + * @code + * vec.locally_owned_elements() == complete_index_set (vec.size()) + * @endcode + */ + IndexSet locally_owned_elements () const; + /** * Return if the vector contains ghost * elements. @@ -1127,6 +1143,21 @@ namespace PETScWrappers (index < static_cast(end))); } + + inline + IndexSet + VectorBase::locally_owned_elements() const + { + IndexSet is (size()); + + // PETSc only allows for contiguous local ranges, so this is simple + const std::pair x = local_range(); + is.add_range (x.first, x.second); + return is; + } + + + inline bool VectorBase::has_ghost_elements() const diff --git a/deal.II/include/deal.II/lac/precondition.h b/deal.II/include/deal.II/lac/precondition.h index e1d7f3dd2d..9d34b5958a 100644 --- a/deal.II/include/deal.II/lac/precondition.h +++ b/deal.II/include/deal.II/lac/precondition.h @@ -20,10 +20,13 @@ #include #include #include +#include #include DEAL_II_NAMESPACE_OPEN +// forward declarations + template class Vector; template class SparseMatrix; namespace parallel @@ -33,6 +36,9 @@ namespace parallel template class Vector; } } +template class SolverCG; + + /*! @addtogroup Preconditioners *@{ @@ -893,9 +899,8 @@ class PreconditionChebyshev : public Subscriptor { public: /** - * Standardized data struct to - * pipe additional parameters - * to the preconditioner. + * Standardized data struct to pipe additional parameters to the + * preconditioner. */ struct AdditionalData { @@ -906,80 +911,63 @@ public: const double smoothing_range = 0., const bool nonzero_starting = false, const unsigned int eig_cg_n_iterations = 8, - const double eig_cg_residual = 1e-2); + const double eig_cg_residual = 1e-2, + const double max_eigenvalue = 1); /** - * This determines the degree of the - * Chebyshev polynomial. The degree - * of the polynomial gives the number - * of matrix-vector products to be - * performed for one application of - * the vmult() operation. Degree zero - * corresponds to a damped Jacobi - * method. + * This determines the degree of the Chebyshev polynomial. The degree of + * the polynomial gives the number of matrix-vector products to be + * performed for one application of the vmult() operation. Degree zero + * corresponds to a damped Jacobi method. */ unsigned int degree; /** - * This sets the range between the - * largest eigenvalue in the matrix - * and the smallest eigenvalue to be - * treated. If the parameter is zero, - * an estimate for the largest and - * for the smallest eigenvalue will - * be calculated - * internally. Otherwise, the - * Chebyshev polynomial will act in - * the interval - * $[\lambda_\mathrm{max}/ - * \tt{smoothing\_range}, - * \lambda_\mathrm{max}]$, where - * $\lambda_\mathrm{max}$ is an - * estimate of the maximum eigenvalue - * of the matrix. A choice of - * smoothing_range between 5 - * and 20 is useful in case the - * preconditioner is used as a - * smoother in multigrid. + * This sets the range between the largest eigenvalue in the matrix and + * the smallest eigenvalue to be treated. If the parameter is zero, an + * estimate for the largest and for the smallest eigenvalue will be + * calculated internally. Otherwise, the Chebyshev polynomial will act in + * the interval $[\lambda_\mathrm{max}/ \tt{smoothing\_range}, + * \lambda_\mathrm{max}]$, where $\lambda_\mathrm{max}$ is an estimate of + * the maximum eigenvalue of the matrix. A choice of + * smoothing_range between 5 and 20 is useful in case the + * preconditioner is used as a smoother in multigrid. */ double smoothing_range; /** - * When this flag is set to - * true, it enables the - * method vmult(dst, src) to - * use non-zero data in the vector - * dst, appending to it the - * Chebyshev corrections. This can be - * useful in some situations - * (e.g. when used for high-frequency - * error smoothing in a multigrid - * algorithm), but not the way the - * solver classes expect a - * preconditioner to work (where one - * ignores the content in - * dst for the - * preconditioner application). + * When this flag is set to true, it enables the method + * vmult(dst, src) to use non-zero data in the vector + * dst, appending to it the Chebyshev corrections. This can be + * useful in some situations (e.g. when used for high-frequency error + * smoothing in a multigrid algorithm), but not the way the solver classes + * expect a preconditioner to work (where one ignores the content in + * dst for the preconditioner application). */ bool nonzero_starting; /** - * Maximum number of CG iterations - * performed for finding the maximum - * eigenvalue. + * Maximum number of CG iterations performed for finding the maximum + * eigenvalue. If set to zero, no computations are performed and the + * eigenvalues according to the given input are used instead. */ unsigned int eig_cg_n_iterations; /** - * Tolerance for CG iterations - * performed for finding the maximum + * Tolerance for CG iterations performed for finding the maximum * eigenvalue. */ double eig_cg_residual; /** - * Stores the inverse of the diagonal - * of the underlying matrix. + * Maximum eigenvalue to work with. Only in effect if @p + * eig_cg_n_iterations is set to zero, otherwise this parameter is + * ignored. + */ + double max_eigenvalue; + + /** + * Stores the inverse of the diagonal of the underlying matrix. */ VECTOR matrix_diagonal_inverse; }; @@ -987,41 +975,29 @@ public: PreconditionChebyshev (); /** - * Initialize function. Takes the - * matrix which is used to form the - * preconditioner, and additional - * flags if there are any. This - * function works only if the input - * matrix has an operator - * el(i,i) for accessing all - * the elements in the - * diagonal. Alternatively, the - * diagonal can be supplied with the - * help of the AdditionalData field. + * Initialize function. Takes the matrix which is used to form the + * preconditioner, and additional flags if there are any. This function + * works only if the input matrix has an operator el(i,i) for + * accessing all the elements in the diagonal. Alternatively, the diagonal + * can be supplied with the help of the AdditionalData field. * - * This function calculates an - * estimate of the eigenvalue range - * of the matrix weighted by its - * diagonal using a modified CG - * iteration. + * This function calculates an estimate of the eigenvalue range of the + * matrix weighted by its diagonal using a modified CG iteration in case the + * given number of iterations is positive. */ void initialize (const MATRIX &matrix, const AdditionalData &additional_data = AdditionalData()); /** - * Computes the action of the - * preconditioner on src, - * storing the result in - * dst. + * Computes the action of the preconditioner on src, storing the + * result in dst. */ void vmult (VECTOR &dst, const VECTOR &src) const; /** - * Computes the action of the - * transposed preconditioner on - * src, storing the result - * in dst. + * Computes the action of the transposed preconditioner on src, + * storing the result in dst. */ void Tvmult (VECTOR &dst, const VECTOR &src) const; @@ -1034,47 +1010,38 @@ public: private: /** - * A pointer to the underlying - * matrix. + * A pointer to the underlying matrix. */ SmartPointer > matrix_ptr; /** - * Internal vector used for the - * vmult operation. + * Internal vector used for the vmult operation. */ mutable VECTOR update1; /** - * Internal vector used for the - * vmult operation. + * Internal vector used for the vmult operation. */ mutable VECTOR update2; /** - * Stores the additional data - * provided to the initialize - * function. + * Stores the additional data provided to the initialize function. */ AdditionalData data; /** - * Average of the largest and - * smallest eigenvalue under - * consideration. + * Average of the largest and smallest eigenvalue under consideration. */ double theta; /** - * Half the interval length between - * the largest and smallest - * eigenvalue under consideration. + * Half the interval length between the largest and smallest eigenvalue + * under consideration. */ double delta; /** - * Stores whether the preconditioner - * has been set up. + * Stores whether the preconditioner has been set up. */ bool is_initialized; }; @@ -1558,144 +1525,6 @@ PreconditionedMatrix //--------------------------------------------------------------------------- -template -inline -PreconditionChebyshev::AdditionalData:: -AdditionalData (const unsigned int degree, - const double smoothing_range, - const bool nonzero_starting, - const unsigned int eig_cg_n_iterations, - const double eig_cg_residual) - : - degree (degree), - smoothing_range (smoothing_range), - nonzero_starting (nonzero_starting), - eig_cg_n_iterations (eig_cg_n_iterations), - eig_cg_residual (eig_cg_residual) -{} - - - -template -inline -PreconditionChebyshev::PreconditionChebyshev () - : - is_initialized (false) -{} - - - -template -inline -void -PreconditionChebyshev::initialize (const MATRIX &matrix, - const AdditionalData &additional_data) -{ - Assert (matrix.m() == matrix.n(), ExcMessage("Matrix not quadratic.")); - Assert (additional_data.eig_cg_n_iterations > 2, - ExcMessage ("Need to set at least two iterations to find eigenvalues.")); - matrix_ptr = &matrix; - data = additional_data; - if (data.matrix_diagonal_inverse.size() != matrix.m()) - { - data.matrix_diagonal_inverse.reinit(matrix.m()); - for (unsigned int i=0; i diagonal; - std::vector offdiagonal; - - VECTOR rhs, g; - rhs.reinit(data.matrix_diagonal_inverse, true); - rhs = 1./std::sqrt(static_cast(matrix.m())); - g.reinit(data.matrix_diagonal_inverse, true); - - unsigned int it=0; - double res,gh,alpha,beta; - - g.equ(-1.,rhs); - res = g.l2_norm(); - update2.equ (-1., g); - gh = res*res; - - while (true) - { - it++; - matrix.vmult (update1, update2); - update1.scale (data.matrix_diagonal_inverse); - alpha = update2 * update1; - alpha = gh/alpha; - g.add (alpha, update1); - res = g.l2_norm(); - - // need at least two iterations to have - // maximum and minimum eigenvalue - if (res == 0. || - it > data.eig_cg_n_iterations || (it > 2 && - res < data.eig_cg_residual)) - break; - - beta = gh; - gh = res*res; - beta = gh/beta; - update2.sadd (beta, -1., g); - - diagonal.push_back (1./alpha + eigen_beta_alpha); - eigen_beta_alpha = beta/alpha; - offdiagonal.push_back(std::sqrt(beta)/alpha); - } - - if (diagonal.size() == 0) - min_eigenvalue = max_eigenvalue = 1.; - else - { - TridiagonalMatrix T(diagonal.size(), true); - for (unsigned int i=0; i 1) - max_eigenvalue = T.eigenvalue(T.n()-1); - else - max_eigenvalue = min_eigenvalue; - } - } - - // include a safety factor since the CG - // method will in general not be converged - const double beta = 1.2 * max_eigenvalue; - const double alpha = (data.smoothing_range > 0 ? - max_eigenvalue / data.smoothing_range : - max_eigenvalue / min_eigenvalue); - delta = (beta-alpha)*0.5; - theta = (beta+alpha)*0.5; - is_initialized = true; -} - - - namespace internal { namespace PreconditionChebyshev @@ -1846,11 +1675,162 @@ namespace internal start_zero, factor1, factor2, update1.begin(), update2.begin(), dst.begin()); } + + template + struct DiagonalPreconditioner + { + DiagonalPreconditioner (const VECTOR &vector) + : + diagonal_vector(vector) + {} + + void vmult (VECTOR &dst, + const VECTOR &src) const + { + dst = src; + dst.scale(diagonal_vector); + } + + const VECTOR &diagonal_vector; + }; } } +template +inline +PreconditionChebyshev::AdditionalData:: +AdditionalData (const unsigned int degree, + const double smoothing_range, + const bool nonzero_starting, + const unsigned int eig_cg_n_iterations, + const double eig_cg_residual, + const double max_eigenvalue) + : + degree (degree), + smoothing_range (smoothing_range), + nonzero_starting (nonzero_starting), + eig_cg_n_iterations (eig_cg_n_iterations), + eig_cg_residual (eig_cg_residual), + max_eigenvalue (max_eigenvalue) +{} + + + +template +inline +PreconditionChebyshev::PreconditionChebyshev () + : + is_initialized (false) +{} + + + +template +inline +void +PreconditionChebyshev::initialize (const MATRIX &matrix, + const AdditionalData &additional_data) +{ + matrix_ptr = &matrix; + data = additional_data; + if (data.matrix_diagonal_inverse.size() != matrix.m()) + { + Assert(data.matrix_diagonal_inverse.size() == 0, + ExcMessage("Matrix diagonal vector set but not sized correctly")); + data.matrix_diagonal_inverse.reinit(matrix.m()); + for (unsigned int i=0; i 0) + { + Assert (additional_data.eig_cg_n_iterations > 2, + ExcMessage ("Need to set at least two iterations to find eigenvalues.")); + + // attach stream to SolverCG, run it with log report for eigenvalues + std::ostream *old_stream = deallog.has_file() ? &deallog.get_file_stream() : + static_cast(0); + if (old_stream) + deallog.detach(); + + std::ostringstream log_msg; + deallog.attach(log_msg); + + // set a very strict tolerance to force at least two iterations + ReductionControl control (data.eig_cg_n_iterations, 1e-20, 1e-20); + GrowingVectorMemory memory; + VECTOR *rhs = memory.alloc(); + VECTOR *dummy = memory.alloc(); + rhs->reinit(data.matrix_diagonal_inverse, true); + dummy->reinit(data.matrix_diagonal_inverse); + *rhs = 1./std::sqrt(static_cast(matrix.m())); + + typename SolverCG::AdditionalData cg_data; + cg_data.compute_eigenvalues = true; + SolverCG solver (control, memory, cg_data); + internal::PreconditionChebyshev::DiagonalPreconditioner + preconditioner(data.matrix_diagonal_inverse); + try + { + solver.solve(matrix, *dummy, *rhs, preconditioner); + } + catch (SolverControl::NoConvergence &) + { + } + Assert(control.last_step() >= 2, + ExcMessage("Could not find eigenvalues")); + + memory.free(dummy); + memory.free(rhs); + + // read the log stream: grab the first and last eigenvalue + std::string cg_message = log_msg.str(); + const std::size_t pos = cg_message.find("cg:: "); + Assert(pos < std::string::npos, ExcInternalError()); + cg_message.erase(0, pos+5); + std::istringstream os1(cg_message); + os1 >> min_eigenvalue; + for (unsigned int i=0; i> max_eigenvalue; + + // reset deal.II stream + deallog.detach(); + if (old_stream) + deallog.attach(*old_stream, false); + + // include a safety factor since the CG method will in general not be + // converged + max_eigenvalue *= 1.2; + } + else + { + max_eigenvalue = data.max_eigenvalue; + min_eigenvalue = data.max_eigenvalue/data.smoothing_range; + } + + const double alpha = (data.smoothing_range > 0 ? + max_eigenvalue / data.smoothing_range : + max_eigenvalue / min_eigenvalue); + delta = (max_eigenvalue-alpha)*0.5; + theta = (max_eigenvalue+alpha)*0.5; + + update1.reinit (data.matrix_diagonal_inverse, true); + update2.reinit (data.matrix_diagonal_inverse, true); + + is_initialized = true; +} + + + template inline void diff --git a/deal.II/include/deal.II/lac/slepc_solver.h b/deal.II/include/deal.II/lac/slepc_solver.h index 595843a5d0..d363702a98 100644 --- a/deal.II/include/deal.II/lac/slepc_solver.h +++ b/deal.II/include/deal.II/lac/slepc_solver.h @@ -76,15 +76,15 @@ DEAL_II_NAMESPACE_OPEN * calling sequence requires calling several of SolverBase functions * rather than just one. This freedom is intended for use of the * SLEPcWrappers that require a greater handle on the eigenvalue - * problem solver context. See also the API of: + * problem solver context. See also the API of, for example: @code template void SolverBase::solve (const PETScWrappers::MatrixBase &A, const PETScWrappers::MatrixBase &B, - std::vector &kr, - std::vector &vr, - const unsigned int n_eigenvectors) + std::vector &eigenvalues, + std::vector &eigenvectors, + const unsigned int n_eigenpairs) { ... } @endcode * as an example on how to do this. @@ -134,11 +134,11 @@ namespace SLEPcWrappers * that we can use as a template when resizing, since we do not * know the parameters of the specific vector class used * (i.e. local_dofs for MPI vectors). However, while copying - * eigenvectors, at least twice the memory size of vr is - * being used (and can be more). To avoid doing this, the fairly - * standard calling sequence executed here is used: Initialise; - * Set up matrices for solving; Actually solve the system; Gather - * the solution(s); and reset. + * eigenvectors, at least twice the memory size of + * eigenvectors is being used (and can be more). To avoid + * doing this, the fairly standard calling sequence executed here + * is used: Initialise; Set up matrices for solving; Actually + * solve the system; Gather the solution(s); and reset. * * @note Note that the number of converged eigenvectors can be * larger than the number of eigenvectors requested; this is due @@ -151,21 +151,37 @@ namespace SLEPcWrappers template void solve (const PETScWrappers::MatrixBase &A, - std::vector &kr, - std::vector &vr, - const unsigned int n_eigenvectors = 1); + std::vector &eigenvalues, + std::vector &eigenvectors, + const unsigned int n_eigenpairs = 1); /** * Same as above, but here a composite method for solving the - * system $A x=\lambda B x$. + * system $A x=\lambda B x$, for real matrices, vectors, and + * values $A, B, x, \lambda$. */ template void solve (const PETScWrappers::MatrixBase &A, const PETScWrappers::MatrixBase &B, - std::vector &kr, - std::vector &vr, - const unsigned int n_eigenvectors = 1); + std::vector &eigenvalues, + std::vector &eigenvectors, + const unsigned int n_eigenpairs = 1); + + /** + * Same as above, but here a composite method for solving the + * system $A x=\lambda B x$ with real matrices $A, B$ and + * imaginary eigenpairs $x, \lamda$. + */ + template + void + solve (const PETScWrappers::MatrixBase &A, + const PETScWrappers::MatrixBase &B, + std::vector &real_eigenvalues, + std::vector &imag_eigenvalues, + std::vector &real_eigenvectors, + std::vector &imag_eigenvectors, + const unsigned int n_eigenpairs = 1); /** * Set the initial vector for the solver. @@ -273,28 +289,36 @@ namespace SLEPcWrappers EPS *get_eps (); /** - * Solve the linear system for n_eigenvectors + * Solve the linear system for n_eigenpairs * eigenstates. Parameter n_converged contains the * actual number of eigenstates that have . converged; this can - * be both fewer or more than n_eigenvectors, depending on the + * be both fewer or more than n_eigenpairs, depending on the * SLEPc eigensolver used. */ void - solve (const unsigned int n_eigenvectors, unsigned int *n_converged); + solve (const unsigned int n_eigenpairs, unsigned int *n_converged); /** - * Access the solutions for a solved eigenvector problem, pair - * index solutions, $\text{index}\,\in\,0\hdots + * Access the real parts of solutions for a solved eigenvector + * problem, pair index solutions, $\text{index}\,\in\,0\hdots * \text{n\_converged}-1$. */ void - get_eigenpair (const unsigned int index, -#ifndef PETSC_USE_COMPLEX - double &kr, -#else - std::complex &kr, -#endif - PETScWrappers::VectorBase &vr); + get_eigenpair (const unsigned int index, + double &eigenvalues, + PETScWrappers::VectorBase &eigenvectors); + + /** + * Access the real and imaginary parts of solutions for a solved + * eigenvector problem, pair index solutions, + * $\text{index}\,\in\,0\hdots \text{n\_converged}-1$. + */ + void + get_eigenpair (const unsigned int index, + double &real_eigenvalues, + double &imag_eigenvalues, + PETScWrappers::VectorBase &real_eigenvectors, + PETScWrappers::VectorBase &imag_eigenvectors); /** * Initialize solver for the linear system $Ax=\lambda x$. (Note: @@ -361,8 +385,8 @@ namespace SLEPcWrappers static int convergence_test (EPS eps, - PetscScalar kr, - PetscScalar ki, + PetscScalar real_eigenvalue, + PetscScalar imag_eigenvalue, PetscReal residual_norm, PetscReal *estimated_error, void *solver_control); @@ -715,70 +739,126 @@ namespace SLEPcWrappers * This is declared here to make it possible to take a std::vector * of different PETScWrappers vector types */ + // todo: The logic of these functions can be simplified without breaking backward compatibility... template - void - SolverBase::solve (const PETScWrappers::MatrixBase &A, - std::vector &kr, - std::vector &vr, - const unsigned int n_eigenvectors) - { - // Panic if no eigenpairs are wanted. - AssertThrow (n_eigenvectors != 0, ExcSLEPcWrappersUsageError()); - - unsigned int n_converged = 0; - - // Set the matrices of the problem - set_matrices (A); - - // and solve - solve (n_eigenvectors, &n_converged); - - if (n_converged > n_eigenvectors) - n_converged = n_eigenvectors; - AssertThrow (n_converged == n_eigenvectors, - ExcSLEPcEigenvectorConvergenceMismatchError(n_converged, n_eigenvectors)); - - AssertThrow (vr.size() != 0, ExcSLEPcWrappersUsageError()); - vr.resize (n_converged, vr.front()); - kr.resize (n_converged); - - for (unsigned int index=0; index &eigenvalues, + std::vector &eigenvectors, + const unsigned int n_eigenpairs) + { + // Panic if the number of eigenpairs wanted is out of bounds. + AssertThrow ((n_eigenpairs > 0) && (n_eigenpairs <= A.m ()), + ExcSLEPcWrappersUsageError()); + + // Set the matrices of the problem + set_matrices (A); + + // and solve + unsigned int n_converged = 0; + solve (n_eigenpairs, &n_converged); + + if (n_converged > n_eigenpairs) + n_converged = n_eigenpairs; + AssertThrow (n_converged == n_eigenpairs, + ExcSLEPcEigenvectorConvergenceMismatchError(n_converged, n_eigenpairs)); + + AssertThrow (eigenvectors.size() != 0, ExcSLEPcWrappersUsageError()); + eigenvectors.resize (n_converged, eigenvectors.front()); + eigenvalues.resize (n_converged); + + for (unsigned int index=0; index - void + void SolverBase::solve (const PETScWrappers::MatrixBase &A, const PETScWrappers::MatrixBase &B, - std::vector &kr, - std::vector &vr, - const unsigned int n_eigenvectors) - { - // Panic if no eigenpairs are wanted. - AssertThrow (n_eigenvectors != 0, ExcSLEPcWrappersUsageError()); + std::vector &eigenvalues, + std::vector &eigenvectors, + const unsigned int n_eigenpairs) + { + // Guard against incompatible matrix sizes: + AssertThrow (A.m() == B.m (), ExcDimensionMismatch(A.m(), B.m())); + AssertThrow (A.n() == B.n (), ExcDimensionMismatch(A.n(), B.n())); + + // Panic if the number of eigenpairs wanted is out of bounds. + AssertThrow ((n_eigenpairs>0) && (n_eigenpairs<=A.m ()), + ExcSLEPcWrappersUsageError()); + + // Set the matrices of the problem + set_matrices (A, B); + + // and solve + unsigned int n_converged = 0; + solve (n_eigenpairs, &n_converged); + + if (n_converged>=n_eigenpairs) + n_converged = n_eigenpairs; + + AssertThrow (n_converged==n_eigenpairs, + ExcSLEPcEigenvectorConvergenceMismatchError(n_converged, n_eigenpairs)); + AssertThrow (eigenvectors.size() != 0, ExcSLEPcWrappersUsageError()); + + eigenvectors.resize (n_converged, eigenvectors.front()); + eigenvalues.resize (n_converged); + + for (unsigned int index=0; index= n_eigenvectors) - n_converged = n_eigenvectors; - - AssertThrow (n_converged == n_eigenvectors, - ExcSLEPcEigenvectorConvergenceMismatchError(n_converged, n_eigenvectors)); - AssertThrow (vr.size() != 0, ExcSLEPcWrappersUsageError()); - - vr.resize (n_converged, vr.front()); - kr.resize (n_converged); + template + void + SolverBase::solve (const PETScWrappers::MatrixBase &A, + const PETScWrappers::MatrixBase &B, + std::vector &real_eigenvalues, + std::vector &imag_eigenvalues, + std::vector &real_eigenvectors, + std::vector &imag_eigenvectors, + const unsigned int n_eigenpairs) + { + // Guard against incompatible matrix sizes: + AssertThrow (A.m() == B.m (), ExcDimensionMismatch(A.m(), B.m())); + AssertThrow (A.n() == B.n (), ExcDimensionMismatch(A.n(), B.n())); + + // and incompatible eigenvalue/eigenvector sizes + AssertThrow (real_eigenvalues.size() == imag_eigenvalues.size(), + ExcDimensionMismatch(real_eigenvalues.size(), imag_eigenvalues.size())); + AssertThrow (real_eigenvectors.size() == imag_eigenvectors.n (), + ExcDimensionMismatch(real_eigenvectors.size(), imag_eigenvectors.size())); + + // Panic if the number of eigenpairs wanted is out of bounds. + AssertThrow ((n_eigenpairs>0) && (n_eigenpairs<=A.m ()), + ExcSLEPcWrappersUsageError()); + + // Set the matrices of the problem + set_matrices (A, B); + + // and solve + unsigned int n_converged = 0; + solve (n_eigenpairs, &n_converged); + + if (n_converged>=n_eigenpairs) + n_converged = n_eigenpairs; + + AssertThrow (n_converged==n_eigenpairs, + ExcSLEPcEigenvectorConvergenceMismatchError(n_converged, n_eigenpairs)); + AssertThrow ((real_eigenvectors.size()!=0) && (imag_eigenvectors.size()!=0), + ExcSLEPcWrappersUsageError()); + + real_eigenvectors.resize (n_converged, real_eigenvectors.front()); + imag_eigenvectors.resize (n_converged, imag_eigenvectors.front()); + real_eigenvalues.resize (n_converged); + imag_eigenvalues.resize (n_converged); + + for (unsigned int index=0; index { public: /** - * There are two possibilities to - * compute the residual: one is an - * estimate using the computed value @p - * tau. The other is exact computation - * using another matrix vector - * multiplication. This increases the - * costs of the algorithm, so it is - * should be set to false whenever the - * problem allows it. + * There are two possibilities to compute the residual: one is an estimate + * using the computed value @p tau. The other is exact computation using + * another matrix vector multiplication. This increases the costs of the + * algorithm, so it is should be set to false whenever the problem allows + * it. * - * Bicgstab is susceptible to breakdowns, so - * we need a parameter telling us, which - * numbers are considered zero. + * Bicgstab is susceptible to breakdowns, so we need a parameter telling us, + * which numbers are considered zero. */ struct AdditionalData { /** * Constructor. * - * The default is to perform an - * exact residual computation and - * breakdown parameter 1e-10. + * The default is to perform an exact residual computation and breakdown + * parameter 1e-10. */ AdditionalData(const bool exact_residual = true, const double breakdown = 1.e-10) : @@ -109,9 +103,8 @@ public: const AdditionalData &data=AdditionalData()); /** - * Constructor. Use an object of - * type GrowingVectorMemory as - * a default to allocate memory. + * Constructor. Use an object of type GrowingVectorMemory as a default to + * allocate memory. */ SolverBicgstab (SolverControl &cn, const AdditionalData &data=AdditionalData()); @@ -139,13 +132,9 @@ protected: double criterion (const MATRIX &A, const VECTOR &x, const VECTOR &b); /** - * Interface for derived class. - * This function gets the current - * iteration vector, the residual - * and the update vector in each - * step. It can be used for a - * graphical output of the - * convergence history. + * Interface for derived class. This function gets the current iteration + * vector, the residual and the update vector in each step. It can be used + * for a graphical output of the convergence history. */ virtual void print_vectors(const unsigned int step, const VECTOR &x, @@ -176,10 +165,6 @@ protected: * Auxiliary vector. */ VECTOR *Vz; - /** - * Auxiliary vector. - */ - VECTOR *Vs; /** * Auxiliary vector. */ @@ -283,7 +268,7 @@ double SolverBicgstab::criterion (const MATRIX &A, const VECTOR &x, const VECTOR &b) { A.vmult(*Vt, x); - Vt->sadd(-1.,1.,b); + Vt->add(-1.,b); res = Vt->l2_norm(); return res; @@ -300,9 +285,6 @@ SolverBicgstab::start(const MATRIX &A) Vr->sadd(-1.,1.,*Vb); res = Vr->l2_norm(); - Vp->reinit(*Vx); - Vv->reinit(*Vx); - *Vrbar = *Vr; return this->control().check(step, res); } @@ -333,10 +315,12 @@ SolverBicgstab::iterate(const MATRIX &A, VECTOR &p = *Vp; VECTOR &y = *Vy; VECTOR &z = *Vz; - VECTOR &s = *Vs; VECTOR &t = *Vt; VECTOR &v = *Vv; + rbar = r; + bool startup = true; + do { ++step; @@ -344,7 +328,14 @@ SolverBicgstab::iterate(const MATRIX &A, rhobar = r*rbar; beta = rhobar * alpha / (rho * omega); rho = rhobar; - p.sadd(beta, 1., r, -beta*omega, v); + if (startup == true) + { + p = r; + startup = false; + } + else + p.sadd(beta, 1., r, -beta*omega, v); + precondition.vmult(y,p); A.vmult(v,y); rhobar = rbar * v; @@ -356,26 +347,23 @@ SolverBicgstab::iterate(const MATRIX &A, if (std::fabs(alpha) > 1.e10) return true; - s.equ(1., r, -alpha, v); + r.add(-alpha, v); - // check for early success, see - // the lac/bicgstab_early - // testcase as to why this is - // necessary - if (this->control().check(step, s.l2_norm()/Vb->l2_norm()) - == SolverControl::success) + // check for early success, see the lac/bicgstab_early testcase as to + // why this is necessary + if (this->control().check(step, r.l2_norm()) == SolverControl::success) { Vx->add(alpha, y); print_vectors(step, *Vx, r, y); return false; } - precondition.vmult(z,s); + precondition.vmult(z,r); A.vmult(t,z); - rhobar = t*s; + rhobar = t*r; omega = rhobar/(t*t); Vx->add(alpha, y, omega, z); - r.equ(1., s, -omega, t); + r.add(-omega, t); if (additional_data.exact_residual) res = criterion(A, *Vx, *Vb); @@ -400,19 +388,19 @@ SolverBicgstab::solve(const MATRIX &A, { deallog.push("Bicgstab"); Vr = this->memory.alloc(); - Vr->reinit(x); + Vr->reinit(x, true); Vrbar = this->memory.alloc(); - Vrbar->reinit(x); + Vrbar->reinit(x, true); Vp = this->memory.alloc(); + Vp->reinit(x, true); Vy = this->memory.alloc(); - Vy->reinit(x); + Vy->reinit(x, true); Vz = this->memory.alloc(); - Vz->reinit(x); - Vs = this->memory.alloc(); - Vs->reinit(x); + Vz->reinit(x, true); Vt = this->memory.alloc(); - Vt->reinit(x); + Vt->reinit(x, true); Vv = this->memory.alloc(); + Vv->reinit(x, true); Vx = &x; Vb = &b; @@ -436,14 +424,12 @@ SolverBicgstab::solve(const MATRIX &A, this->memory.free(Vp); this->memory.free(Vy); this->memory.free(Vz); - this->memory.free(Vs); this->memory.free(Vt); this->memory.free(Vv); deallog.pop(); - // in case of failure: throw - // exception + // in case of failure: throw exception if (this->control().last_check() != SolverControl::success) throw SolverControl::NoConvergence (this->control().last_step(), this->control().last_value()); diff --git a/deal.II/include/deal.II/lac/solver_gmres.h b/deal.II/include/deal.II/lac/solver_gmres.h index f60a5b0ff5..936b9a1a02 100644 --- a/deal.II/include/deal.II/lac/solver_gmres.h +++ b/deal.II/include/deal.II/lac/solver_gmres.h @@ -34,21 +34,16 @@ DEAL_II_NAMESPACE_OPEN namespace internal { /** - * A namespace for a helper class - * to the GMRES solver. + * A namespace for a helper class to the GMRES solver. */ namespace SolverGMRES { /** - * Class to hold temporary - * vectors. This class - * automatically allocates a new - * vector, once it is needed. + * Class to hold temporary vectors. This class automatically allocates a + * new vector, once it is needed. * - * A future version should also - * be able to shift through - * vectors automatically, - * avoiding restart. + * A future version should also be able to shift through vectors + * automatically, avoiding restart. */ template @@ -56,9 +51,7 @@ namespace internal { public: /** - * Constructor. Prepares an - * array of @p VECTOR of - * length @p max_size. + * Constructor. Prepares an array of @p VECTOR of length @p max_size. */ TmpVectors(const unsigned int max_size, VectorMemory &vmem); @@ -69,21 +62,15 @@ namespace internal ~TmpVectors(); /** - * Get vector number - * @p i. If this vector was - * unused before, an error + * Get vector number @p i. If this vector was unused before, an error * occurs. */ VECTOR &operator[] (const unsigned int i) const; /** - * Get vector number - * @p i. Allocate it if - * necessary. + * Get vector number @p i. Allocate it if necessary. * - * If a vector must be - * allocated, @p temp is - * used to reinit it to the + * If a vector must be allocated, @p temp is used to reinit it to the * proper dimensions. */ VECTOR &operator() (const unsigned int i, @@ -91,22 +78,18 @@ namespace internal private: /** - * Pool were vectors are - * obtained from. + * Pool were vectors are obtained from. */ VectorMemory &mem; /** - * Field for storing the - * vectors. + * Field for storing the vectors. */ std::vector data; /** - * Offset of the first - * vector. This is for later - * when vector rotation will - * be implemented. + * Offset of the first vector. This is for later when vector rotation + * will be implemented. */ unsigned int offset; }; @@ -169,56 +152,49 @@ class SolverGMRES : public Solver { public: /** - * Standardized data struct to - * pipe additional data to the - * solver. + * Standardized data struct to pipe additional data to the solver. */ struct AdditionalData { /** - * Constructor. By default, set the - * number of temporary vectors to 30, - * i.e. do a restart every - * 28 iterations. Also - * set preconditioning from left and - * the residual of the stopping - * criterion to the default residual. + * Constructor. By default, set the number of temporary vectors to 30, + * i.e. do a restart every 28 iterations. Also set preconditioning from + * left, the residual of the stopping criterion to the default + * residual, and re-orthogonalization only if necessary. */ AdditionalData (const unsigned int max_n_tmp_vectors = 30, const bool right_preconditioning = false, - const bool use_default_residual = true); + const bool use_default_residual = true, + const bool force_re_orthogonalization = false); /** - * Maximum number of - * temporary vectors. This - * parameter controls the - * size of the Arnoldi basis, - * which for historical - * reasons is + * Maximum number of temporary vectors. This parameter controls the size + * of the Arnoldi basis, which for historical reasons is * #max_n_tmp_vectors-2. */ unsigned int max_n_tmp_vectors; /** - * Flag for right - * preconditioning. + * Flag for right preconditioning. * - * @note Change between left - * and right preconditioning - * will also change the way - * residuals are - * evaluated. See the - * corresponding section in - * the SolverGMRES. + * @note Change between left and right preconditioning will also change + * the way residuals are evaluated. See the corresponding section in the + * SolverGMRES. */ bool right_preconditioning; /** - * Flag for the default - * residual that is used to - * measure convergence. + * Flag for the default residual that is used to measure convergence. */ bool use_default_residual; + + /** + * Flag to force re-orthogonalization of orthonormal basis in every + * step. If set to false, the solver automatically checks for loss of + * orthogonality every 5 iterations and enables re-orthogonalization only + * if necessary. + */ + bool force_re_orthogonalization; }; /** @@ -229,16 +205,14 @@ public: const AdditionalData &data=AdditionalData()); /** - * Constructor. Use an object of - * type GrowingVectorMemory as - * a default to allocate memory. + * Constructor. Use an object of type GrowingVectorMemory as a default to + * allocate memory. */ SolverGMRES (SolverControl &cn, const AdditionalData &data=AdditionalData()); /** - * Solve the linear system $Ax=b$ - * for x. + * Solve the linear system $Ax=b$ for x. */ template void @@ -255,35 +229,51 @@ public: protected: /** - * Includes the maximum number of - * tmp vectors. + * Includes the maximum number of tmp vectors. */ AdditionalData additional_data; /** - * Implementation of the computation of - * the norm of the residual. + * Implementation of the computation of the norm of the residual. */ virtual double criterion(); /** - * Transformation of an upper - * Hessenberg matrix into - * tridiagonal structure by givens - * rotation of the last column + * Transformation of an upper Hessenberg matrix into tridiagonal structure + * by givens rotation of the last column */ void givens_rotation (Vector &h, Vector &b, Vector &ci, Vector &si, int col) const; + + /** + * Orthogonalize the vector @p vv against the @p dim (orthogonal) vectors + * given by the first argument using the modified Gram-Schmidt + * algorithm. The factors used for orthogonalization are stored in @p h. The + * boolean @p re_orthogonalize specifies whether the modified Gram-Schmidt + * algorithm should be applied twice. The algorithm checks loss of + * orthogonality in the procedure every fifth step and sets the flag to true + * in that case. All subsequent iterations use re-orthogonalization. + */ + static double + modified_gram_schmidt (const internal::SolverGMRES::TmpVectors &orthogonal_vectors, + const unsigned int dim, + const unsigned int accumulated_iterations, + VECTOR &vv, + Vector &h, + bool &re_orthogonalize); + /** * Projected system matrix */ FullMatrix H; + /** * Auxiliary matrix for inverting @p H */ FullMatrix H1; + private: /** * No copy constructor. @@ -315,21 +305,14 @@ class SolverFGMRES : public Solver { public: /** - * Standardized data struct to - * pipe additional data to the - * solver. + * Standardized data struct to pipe additional data to the solver. */ struct AdditionalData { /** - * Constructor. By default, - * set the number of - * temporary vectors to 30, - * preconditioning from left - * and the residual of the - * stopping criterion to the - * default residual - * (cf. class documentation). + * Constructor. By default, set the number of temporary vectors to 30, + * preconditioning from left and the residual of the stopping criterion to + * the default residual (cf. class documentation). */ AdditionalData(const unsigned int max_basis_size = 30, const bool /*use_default_residual*/ = true) @@ -338,8 +321,7 @@ public: {} /** - * Maximum number of - * tmp vectors. + * Maximum number of tmp vectors. */ unsigned int max_basis_size; }; @@ -352,16 +334,14 @@ public: const AdditionalData &data=AdditionalData()); /** - * Constructor. Use an object of - * type GrowingVectorMemory as - * a default to allocate memory. + * Constructor. Use an object of type GrowingVectorMemory as a default to + * allocate memory. */ SolverFGMRES (SolverControl &cn, const AdditionalData &data=AdditionalData()); /** - * Solve the linear system $Ax=b$ - * for x. + * Solve the linear system $Ax=b$ for x. */ template void @@ -453,11 +433,13 @@ inline SolverGMRES::AdditionalData:: AdditionalData (const unsigned int max_n_tmp_vectors, const bool right_preconditioning, - const bool use_default_residual) + const bool use_default_residual, + const bool force_re_orthogonalization) : max_n_tmp_vectors(max_n_tmp_vectors), right_preconditioning(right_preconditioning), - use_default_residual(use_default_residual) + use_default_residual(use_default_residual), + force_re_orthogonalization(force_re_orthogonalization) {} @@ -509,6 +491,65 @@ SolverGMRES::givens_rotation (Vector &h, +template +inline +double +SolverGMRES::modified_gram_schmidt (const internal::SolverGMRES::TmpVectors &orthogonal_vectors, + const unsigned int dim, + const unsigned int accumulated_iterations, + VECTOR &vv, + Vector &h, + bool &re_orthogonalize) +{ + const unsigned int inner_iteration = dim - 1; + + // need initial norm for detection of re-orthogonalization, see below + double norm_vv_start = 0; + if (re_orthogonalize == false && inner_iteration % 5 == 4) + norm_vv_start = vv.l2_norm(); + + // Orthogonalization + for (unsigned int i=0 ; i 10. * norm_vv_start * + std::sqrt(std::numeric_limits::epsilon())) + return norm_vv; + + else + { + re_orthogonalize = true; + deallog << "Re-orthogonalization enabled at step " + << accumulated_iterations << std::endl; + } + } + + if (re_orthogonalize == true) + for (unsigned int i=0 ; i template void @@ -517,12 +558,9 @@ SolverGMRES::solve (const MATRIX &A, const VECTOR &b, const PRECONDITIONER &precondition) { - // this code was written a very - // long time ago by people not - // associated with deal.II. we - // don't make any guarantees to its - // optimality or that it even works - // as expected... + // this code was written a very long time ago by people not associated with + // deal.II. we don't make any guarantees to its optimality or that it even + // works as expected... //TODO:[?] Check, why there are two different start residuals. //TODO:[GK] Make sure the parameter in the constructor means maximum basis size @@ -530,8 +568,7 @@ SolverGMRES::solve (const MATRIX &A, deallog.push("GMRES"); const unsigned int n_tmp_vectors = additional_data.max_n_tmp_vectors; - // Generate an object where basis - // vectors are stored. + // Generate an object where basis vectors are stored. internal::SolverGMRES::TmpVectors tmp_vectors (n_tmp_vectors, this->memory); // number of the present iteration; this @@ -539,12 +576,10 @@ SolverGMRES::solve (const MATRIX &A, // restart unsigned int accumulated_iterations = 0; - // matrix used for the orthogonalization - // process later + // matrix used for the orthogonalization process later H.reinit(n_tmp_vectors, n_tmp_vectors-1); - // some additional vectors, also used - // in the orthogonalization + // some additional vectors, also used in the orthogonalization dealii::Vector gamma(n_tmp_vectors), ci (n_tmp_vectors-1), @@ -556,17 +591,13 @@ SolverGMRES::solve (const MATRIX &A, SolverControl::State iteration_state = SolverControl::iterate; - // switch to determine whether we want a - // left or a right preconditioner. at - // present, left is default, but both - // ways are implemented + // switch to determine whether we want a left or a right preconditioner. at + // present, left is default, but both ways are implemented const bool left_precondition = !additional_data.right_preconditioning; - // Per default the left - // preconditioned GMRes uses the - // preconditioned residual and the - // right preconditioned GMRes uses - // the unpreconditioned residual as - // stopping criterion. + + // Per default the left preconditioned GMRes uses the preconditioned + // residual and the right preconditioned GMRes uses the unpreconditioned + // residual as stopping criterion. const bool use_default_residual = additional_data.use_default_residual; // define two aliases @@ -589,16 +620,15 @@ SolverGMRES::solve (const MATRIX &A, gamma_ = new dealii::Vector (gamma.size()); } - /////////////////////////////////// - // outer iteration: loop until we - // either reach convergence or the - // maximum number of iterations is - // exceeded. each cycle of this - // loop amounts to one restart + bool re_orthogonalize = additional_data.force_re_orthogonalization; + + /////////////////////////////////////////////////////////////////////////// + // outer iteration: loop until we either reach convergence or the maximum + // number of iterations is exceeded. each cycle of this loop amounts to one + // restart do { - // reset this vector to the - // right size + // reset this vector to the right size h.reinit (n_tmp_vectors-1); if (left_precondition) @@ -615,14 +645,9 @@ SolverGMRES::solve (const MATRIX &A, double rho = v.l2_norm(); - // check the residual here as - // well since it may be that we - // got the exact (or an almost - // exact) solution vector at - // the outset. if we wouldn't - // check here, the next scaling - // operation would produce - // garbage + // check the residual here as well since it may be that we got the exact + // (or an almost exact) solution vector at the outset. if we wouldn't + // check here, the next scaling operation would produce garbage if (use_default_residual) { iteration_state = this->control().check ( @@ -661,13 +686,9 @@ SolverGMRES::solve (const MATRIX &A, v *= 1./rho; - // inner iteration doing at - // most as many steps as there - // are temporary vectors. the - // number of steps actually - // been done is propagated - // outside through the @p dim - // variable + // inner iteration doing at most as many steps as there are temporary + // vectors. the number of steps actually been done is propagated outside + // through the @p dim variable for (unsigned int inner_iteration=0; ((inner_iteration < n_tmp_vectors-2) && @@ -684,44 +705,31 @@ SolverGMRES::solve (const MATRIX &A, precondition.vmult(vv,p); } else - { + { precondition.vmult(p, tmp_vectors[inner_iteration]); A.vmult(vv,p); }; dim = inner_iteration+1; - /* Orthogonalization */ - for (unsigned int i=0 ; i::solve (const MATRIX &A, }; A.vmult(*r,*x_); r->sadd(-1.,1.,b); - // Now *r contains the - // unpreconditioned - // residual!! + // Now *r contains the unpreconditioned residual!! if (left_precondition) { const double res=r->l2_norm(); @@ -775,9 +781,8 @@ SolverGMRES::solve (const MATRIX &A, } } }; - // end of inner iteration. now - // calculate the solution from - // the temporary vectors + // end of inner iteration. now calculate the solution from the temporary + // vectors h.reinit(dim); H1.reinit(dim+1,dim); @@ -798,8 +803,7 @@ SolverGMRES::solve (const MATRIX &A, precondition.vmult(v,p); x.add(1.,v); }; - // end of outer iteration. restart if - // no convergence and the number of + // end of outer iteration. restart if no convergence and the number of // iterations is not exceeded } while (iteration_state == SolverControl::iterate); @@ -813,8 +817,7 @@ SolverGMRES::solve (const MATRIX &A, } deallog.pop(); - // in case of failure: throw - // exception + // in case of failure: throw exception if (this->control().last_check() != SolverControl::success) throw SolverControl::NoConvergence (this->control().last_step(), this->control().last_value()); @@ -827,9 +830,8 @@ template double SolverGMRES::criterion () { - // dummy implementation. this function is - // not needed for the present implementation - // of gmres + // dummy implementation. this function is not needed for the present + // implementation of gmres Assert (false, ExcInternalError()); return 0; } @@ -873,18 +875,15 @@ SolverFGMRES::solve ( const unsigned int basis_size = additional_data.max_basis_size; - // Generate an object where basis - // vectors are stored. + // Generate an object where basis vectors are stored. typename internal::SolverGMRES::TmpVectors v (basis_size, this->memory); typename internal::SolverGMRES::TmpVectors z (basis_size, this->memory); - // number of the present iteration; this - // number is not reset to zero upon a + // number of the present iteration; this number is not reset to zero upon a // restart unsigned int accumulated_iterations = 0; - // matrix used for the orthogonalization - // process later + // matrix used for the orthogonalization process later H.reinit(basis_size+1, basis_size); // Vectors for projected system @@ -953,8 +952,7 @@ SolverFGMRES::solve ( this->memory.free(aux); deallog.pop(); - // in case of failure: throw - // exception + // in case of failure: throw exception if (this->control().last_check() != SolverControl::success) throw SolverControl::NoConvergence (this->control().last_step(), this->control().last_value()); diff --git a/deal.II/include/deal.II/lac/sparsity_pattern.h b/deal.II/include/deal.II/lac/sparsity_pattern.h index 17c5599b26..212038578d 100644 --- a/deal.II/include/deal.II/lac/sparsity_pattern.h +++ b/deal.II/include/deal.II/lac/sparsity_pattern.h @@ -1419,6 +1419,7 @@ private: */ friend class SparsityPatternIterators::Iterator; friend class SparsityPatternIterators::Accessor; + friend class ChunkSparsityPatternIterators::Accessor; }; diff --git a/deal.II/include/deal.II/lac/trilinos_block_sparse_matrix.h b/deal.II/include/deal.II/lac/trilinos_block_sparse_matrix.h index f298dad0cc..1550c8a246 100644 --- a/deal.II/include/deal.II/lac/trilinos_block_sparse_matrix.h +++ b/deal.II/include/deal.II/lac/trilinos_block_sparse_matrix.h @@ -18,6 +18,7 @@ #ifdef DEAL_II_WITH_TRILINOS # include +# include # include # include # include @@ -292,161 +293,23 @@ namespace TrilinosWrappers unsigned int n_nonzero_elements () const; /** - * Matrix-vector multiplication: - * let $dst = M*src$ with $M$ - * being this matrix. + * Matrix-vector multiplication: let $dst = M*src$ with $M$ being this + * matrix. The vector types can be block vectors or non-block vectors + * (only if the matrix has only one row or column, respectively), and need + * to define TrilinosWrappers::SparseMatrix::vmult. */ - void vmult (MPI::BlockVector &dst, - const MPI::BlockVector &src) const; - - - /** - * Matrix-vector multiplication: - * let $dst = M*src$ with $M$ - * being this matrix, now applied - * to localized block vectors - * (works only when run on one - * processor). - */ - void vmult (BlockVector &dst, - const BlockVector &src) const; - - /** - * Matrix-vector - * multiplication. Just like the - * previous function, but only - * applicable if the matrix has - * only one block column. - */ - void vmult (MPI::BlockVector &dst, - const MPI::Vector &src) const; - - /** - * Matrix-vector - * multiplication. Just like the - * previous function, but only - * applicable if the matrix has - * only one block column, now - * applied to localized vectors - * (works only when run on one - * processor). - */ - void vmult (BlockVector &dst, - const Vector &src) const; - - /** - * Matrix-vector - * multiplication. Just like the - * previous function, but only - * applicable if the matrix has - * only one block row. - */ - void vmult (MPI::Vector &dst, - const MPI::BlockVector &src) const; - - /** - * Matrix-vector - * multiplication. Just like the - * previous function, but only - * applicable if the matrix has - * only one block row, now - * applied to localized vectors - * (works only when run on one - * processor). - */ - void vmult (Vector &dst, - const BlockVector &src) const; - - /** - * Matrix-vector - * multiplication. Just like the - * previous function, but only - * applicable if the matrix has - * only one block. - */ - void vmult (VectorBase &dst, - const VectorBase &src) const; - - /** - * Matrix-vector multiplication: - * let $dst = M^T*src$ with $M$ - * being this matrix. This - * function does the same as - * vmult() but takes the - * transposed matrix. - */ - void Tvmult (MPI::BlockVector &dst, - const MPI::BlockVector &src) const; - - /** - * Matrix-vector multiplication: - * let $dst = M^T*src$ with $M$ - * being this matrix. This - * function does the same as - * vmult() but takes the - * transposed matrix, now applied - * to localized Trilinos vectors - * (works only when run on one - * processor). - */ - void Tvmult (BlockVector &dst, - const BlockVector &src) const; - - /** - * Matrix-vector - * multiplication. Just like the - * previous function, but only - * applicable if the matrix has - * only one block row. - */ - void Tvmult (MPI::BlockVector &dst, - const MPI::Vector &src) const; + template + void vmult (VectorType1 &dst, + const VectorType2 &src) const; /** - * Matrix-vector - * multiplication. Just like the - * previous function, but only - * applicable if the matrix has - * only one block row, now - * applied to localized Trilinos - * vectors (works only when run - * on one processor). - */ - void Tvmult (BlockVector &dst, - const Vector &src) const; - - /** - * Matrix-vector - * multiplication. Just like the - * previous function, but only - * applicable if the matrix has - * only one block column. - */ - void Tvmult (MPI::Vector &dst, - const MPI::BlockVector &src) const; - - /** - * Matrix-vector - * multiplication. Just like the - * previous function, but only - * applicable if the matrix has - * only one block column, now - * applied to localized Trilinos - * vectors (works only when run - * on one processor). - */ - void Tvmult (Vector &dst, - const BlockVector &src) const; - - /** - * Matrix-vector - * multiplication. Just like the - * previous function, but only - * applicable if the matrix has - * only one block. + * Matrix-vector multiplication: let $dst = M^T*src$ with $M$ being this + * matrix. This function does the same as vmult() but takes the transposed + * matrix. */ - void Tvmult (VectorBase &dst, - const VectorBase &src) const; + template + void Tvmult (VectorType1 &dst, + const VectorType2 &src) const; /** * Compute the residual of an @@ -623,6 +486,51 @@ namespace TrilinosWrappers << "The blocks [" << arg1 << ',' << arg2 << "] and [" << arg3 << ',' << arg4 << "] have differing column numbers."); ///@} + + private: + /** + * Internal version of (T)vmult with two block vectors + */ + template + void vmult (VectorType1 &dst, + const VectorType2 &src, + const bool transpose, + const dealii::internal::bool2type, + const dealii::internal::bool2type) const; + + /** + * Internal version of (T)vmult where the source vector is a block vector + * but the destination vector is a non-block vector + */ + template + void vmult (VectorType1 &dst, + const VectorType2 &src, + const bool transpose, + const dealii::internal::bool2type, + const dealii::internal::bool2type) const; + + /** + * Internal version of (T)vmult where the source vector is a non-block + * vector but the destination vector is a block vector + */ + template + void vmult (VectorType1 &dst, + const VectorType2 &src, + const bool transpose, + const dealii::internal::bool2type, + const dealii::internal::bool2type) const; + + /** + * Internal version of (T)vmult where both source vector and the + * destination vector are non-block vectors (only defined if the matrix + * consists of only one block) + */ + template + void vmult (VectorType1 &dst, + const VectorType2 &src, + const bool transpose, + const dealii::internal::bool2type, + const dealii::internal::bool2type) const; }; @@ -666,142 +574,97 @@ namespace TrilinosWrappers + template inline void - BlockSparseMatrix::vmult (MPI::BlockVector &dst, - const MPI::BlockVector &src) const + BlockSparseMatrix::vmult (VectorType1 &dst, + const VectorType2 &src) const { - BaseClass::vmult_block_block (dst, src); + vmult(dst, src, false, + dealii::internal::bool2type::value>(), + dealii::internal::bool2type::value>()); } + template inline void - BlockSparseMatrix::vmult (BlockVector &dst, - const BlockVector &src) const + BlockSparseMatrix::Tvmult (VectorType1 &dst, + const VectorType2 &src) const { - BaseClass::vmult_block_block (dst, src); + vmult(dst, src, true, + dealii::internal::bool2type::value>(), + dealii::internal::bool2type::value>()); } + template inline void - BlockSparseMatrix::vmult (MPI::BlockVector &dst, - const MPI::Vector &src) const + BlockSparseMatrix::vmult (VectorType1 &dst, + const VectorType2 &src, + const bool transpose, + dealii::internal::bool2type, + dealii::internal::bool2type) const { - BaseClass::vmult_block_nonblock (dst, src); + if (transpose == true) + BaseClass::Tvmult_block_block (dst, src); + else + BaseClass::vmult_block_block (dst, src); } - inline - void - BlockSparseMatrix::vmult (BlockVector &dst, - const Vector &src) const - { - BaseClass::vmult_block_nonblock (dst, src); - } - - - - inline - void - BlockSparseMatrix::vmult (MPI::Vector &dst, - const MPI::BlockVector &src) const - { - BaseClass::vmult_nonblock_block (dst, src); - } - - - - inline - void - BlockSparseMatrix::vmult (Vector &dst, - const BlockVector &src) const - { - BaseClass::vmult_nonblock_block (dst, src); - } - - - - inline - void - BlockSparseMatrix::vmult (VectorBase &dst, - const VectorBase &src) const - { - BaseClass::vmult_nonblock_nonblock (dst, src); - } - - - - inline - void - BlockSparseMatrix::Tvmult (MPI::BlockVector &dst, - const MPI::BlockVector &src) const - { - BaseClass::Tvmult_block_block (dst, src); - } - - - - inline - void - BlockSparseMatrix::Tvmult (BlockVector &dst, - const BlockVector &src) const - { - BaseClass::Tvmult_block_block (dst, src); - } - - - - inline - void - BlockSparseMatrix::Tvmult (MPI::BlockVector &dst, - const MPI::Vector &src) const - { - BaseClass::Tvmult_block_nonblock (dst, src); - } - - - - inline - void - BlockSparseMatrix::Tvmult (BlockVector &dst, - const Vector &src) const - { - BaseClass::Tvmult_block_nonblock (dst, src); - } - - + template inline void - BlockSparseMatrix::Tvmult (MPI::Vector &dst, - const MPI::BlockVector &src) const + BlockSparseMatrix::vmult (VectorType1 &dst, + const VectorType2 &src, + const bool transpose, + dealii::internal::bool2type, + dealii::internal::bool2type) const { - BaseClass::Tvmult_nonblock_block (dst, src); + if (transpose == true) + BaseClass::Tvmult_nonblock_block (dst, src); + else + BaseClass::vmult_nonblock_block (dst, src); } + template inline void - BlockSparseMatrix::Tvmult (Vector &dst, - const BlockVector &src) const + BlockSparseMatrix::vmult (VectorType1 &dst, + const VectorType2 &src, + const bool transpose, + dealii::internal::bool2type, + dealii::internal::bool2type) const { - BaseClass::Tvmult_nonblock_block (dst, src); + if (transpose == true) + BaseClass::Tvmult_block_nonblock (dst, src); + else + BaseClass::vmult_block_nonblock (dst, src); } + template inline void - BlockSparseMatrix::Tvmult (VectorBase &dst, - const VectorBase &src) const + BlockSparseMatrix::vmult (VectorType1 &dst, + const VectorType2 &src, + const bool transpose, + dealii::internal::bool2type, + dealii::internal::bool2type) const { - BaseClass::Tvmult_nonblock_nonblock (dst, src); + if (transpose == true) + BaseClass::Tvmult_nonblock_nonblock (dst, src); + else + BaseClass::vmult_nonblock_nonblock (dst, src); } } diff --git a/deal.II/include/deal.II/lac/trilinos_sparse_matrix.h b/deal.II/include/deal.II/lac/trilinos_sparse_matrix.h index 36a7123ced..abcab431af 100644 --- a/deal.II/include/deal.II/lac/trilinos_sparse_matrix.h +++ b/deal.II/include/deal.II/lac/trilinos_sparse_matrix.h @@ -22,8 +22,8 @@ # include # include # include -# include -# include +# include +# include # include # include @@ -33,7 +33,7 @@ # include # include # include -# include +# include # ifdef DEAL_II_WITH_MPI # include # include "mpi.h" @@ -52,7 +52,6 @@ class SparsityPattern; namespace TrilinosWrappers { // forward declarations - class VectorBase; class SparseMatrix; class SparsityPattern; @@ -543,7 +542,7 @@ namespace TrilinosWrappers typedef TrilinosScalar value_type; /** - * @name Constructors and initalization. + * @name Constructors and initialization. */ //@{ /** @@ -769,7 +768,7 @@ namespace TrilinosWrappers * they might be spread out. The * second Epetra_Map is only used to * specify the number of columns and - * for internal arragements when + * for internal arrangements when * doing matrix-vector products with * vectors based on that column map. * @@ -1851,171 +1850,109 @@ namespace TrilinosWrappers //@{ /** - * Matrix-vector multiplication: - * let dst = M*src with - * M being this matrix. + * Matrix-vector multiplication: let dst = M*src with M + * being this matrix. * - * Source and destination must - * not be the same vector. + * Source and destination must not be the same vector. * - * Note that both vectors have to - * be distributed vectors - * generated using the same Map - * as was used for the matrix in - * case you work on a distributed - * memory architecture, using the - * interface in the - * TrilinosWrappers::VectorBase - * class (or one of the two - * derived classes Vector and - * MPI::Vector). + * This function can be called with several different vector objects, + * namely TrilinosWrappers::Vector, TrilinosWrappers::MPI::Vector as well + * as deal.II's own vector classes Vector and + * parallel::distributed::Vector. * - * In case of a localized Vector, - * this function will only work - * when running on one processor, - * since the matrix object is - * inherently - * distributed. Otherwise, and - * exception will be thrown. - */ - void vmult (VectorBase &dst, - const VectorBase &src) const; - - /** - * Same as before, but working with - * deal.II's own distributed vector - * class. - */ - void vmult (parallel::distributed::Vector &dst, - const parallel::distributed::Vector &src) const; - - /** - * Same as before, but working with - * deal.II's own vector - * class. + * Note that both vectors have to be distributed vectors generated using + * the same Map as was used for the matrix in case you work on a + * distributed memory architecture, using the interface in the + * TrilinosWrappers::VectorBase class (or one of the two derived classes + * Vector and MPI::Vector). + * + * In case of a localized Vector, this function will only work when + * running on one processor, since the matrix object is inherently + * distributed. Otherwise, and exception will be thrown. */ - void vmult (dealii::Vector &dst, - const dealii::Vector &src) const; + template + void vmult (VectorType &dst, + const VectorType &src) const; /** - * Matrix-vector multiplication: - * let dst = - * MT*src with - * M being this - * matrix. This function does the - * same as vmult() but takes the - * transposed matrix. + * Matrix-vector multiplication: let dst = MT*src with + * M being this matrix. This function does the same as vmult() but + * takes the transposed matrix. * - * Source and destination must - * not be the same vector. + * Source and destination must not be the same vector. * - * Note that both vectors have to - * be distributed vectors - * generated using the same Map - * as was used for the matrix in - * case you work on a distributed - * memory architecture, using the - * interface in the - * TrilinosWrappers::VectorBase - * class (or one of the two - * derived classes Vector and - * MPI::Vector). + * This function can be called with several different vector objects, + * namely TrilinosWrappers::Vector, TrilinosWrappers::MPI::Vector as well + * as deal.II's own vector classes Vector and + * parallel::distributed::Vector. * - * In case of a localized Vector, - * this function will only work - * when running on one processor, - * since the matrix object is - * inherently - * distributed. Otherwise, and - * exception will be thrown. - */ - void Tvmult (VectorBase &dst, - const VectorBase &src) const; - - /** - * Same as before, but working with - * deal.II's own distributed vector - * class. - */ - void Tvmult (parallel::distributed::Vector &dst, - const parallel::distributed::Vector &src) const; - - /** - * Same as before, but working with - * deal.II's own vector - * class. + * Note that both vectors have to be distributed vectors generated using + * the same Map as was used for the matrix in case you work on a + * distributed memory architecture, using the interface in the + * TrilinosWrappers::VectorBase class (or one of the two derived classes + * Vector and MPI::Vector). + * + * In case of a localized Vector, this function will only work when + * running on one processor, since the matrix object is inherently + * distributed. Otherwise, and exception will be thrown. */ - void Tvmult (dealii::Vector &dst, - const dealii::Vector &src) const; + template + void Tvmult (VectorType &dst, + const VectorType &src) const; /** - * Adding Matrix-vector - * multiplication. Add - * M*src on dst - * with M being this - * matrix. + * Adding matrix-vector multiplication. Add M*src on dst + * with M being this matrix. * - * Source and destination must - * not be the same vector. + * Source and destination must not be the same vector. * - * Note that both vectors have to - * be distributed vectors - * generated using the same Map - * as was used for the matrix in - * case you work on a distributed - * memory architecture, using the - * interface in the - * TrilinosWrappers::VectorBase - * class (or one of the two - * derived classes Vector and - * MPI::Vector). + * This function can be called with several different vector objects, + * namely TrilinosWrappers::Vector, TrilinosWrappers::MPI::Vector as well + * as deal.II's own vector classes Vector and + * parallel::distributed::Vector. + * + * When using a vector of type TrilinosWrappers::MPI::Vector, both vectors + * have to be distributed vectors generated using the same Map as was used + * for the matrix rows and columns in case you work on a distributed + * memory architecture, using the interface in the + * TrilinosWrappers::VectorBase class. + * + * In case of a localized Vector (i.e., TrilinosWrappers::Vector or + * Vector), this function will only work when running on one + * processor, since the matrix object is inherently + * distributed. Otherwise, and exception will be thrown. * - * In case of a localized Vector, - * this function will only work - * when running on one processor, - * since the matrix object is - * inherently - * distributed. Otherwise, and - * exception will be thrown. */ - void vmult_add (VectorBase &dst, - const VectorBase &src) const; + template + void vmult_add (VectorType &dst, + const VectorType &src) const; /** - * Adding Matrix-vector - * multiplication. Add - * MT*src to - * dst with M being - * this matrix. This function - * does the same as vmult_add() - * but takes the transposed - * matrix. + * Adding matrix-vector multiplication. Add MT*src to + * dst with M being this matrix. This function does the same + * as vmult_add() but takes the transposed matrix. * - * Source and destination must - * not be the same vector. + * Source and destination must not be the same vector. * - * Note that both vectors have to - * be distributed vectors - * generated using the same Map - * as was used for the matrix in - * case you work on a distributed - * memory architecture, using the - * interface in the - * TrilinosWrappers::VectorBase - * class (or one of the two - * derived classes Vector and - * MPI::Vector). + * This function can be called with several different vector objects, + * namely TrilinosWrappers::Vector, TrilinosWrappers::MPI::Vector as well + * as deal.II's own vector classes Vector and + * parallel::distributed::Vector. * - * In case of a localized Vector, - * this function will only work - * when running on one processor, - * since the matrix object is - * inherently - * distributed. Otherwise, and - * exception will be thrown. + * When using a vector of type TrilinosWrappers::MPI::Vector, both vectors + * have to be distributed vectors generated using the same Map as was used + * for the matrix rows and columns in case you work on a distributed + * memory architecture, using the interface in the + * TrilinosWrappers::VectorBase class. + * + * In case of a localized Vector (i.e., TrilinosWrappers::Vector or + * Vector), this function will only work when running on one + * processor, since the matrix object is inherently + * distributed. Otherwise, and exception will be thrown. */ - void Tvmult_add (VectorBase &dst, - const VectorBase &src) const; + template + void Tvmult_add (VectorType &dst, + const VectorType &src) const; /** * Return the square of the norm @@ -2585,33 +2522,6 @@ namespace TrilinosWrappers */ bool compressed; - /** - * An internal Trilinos vector that - * is used for accelerating vmult_add - * functions (in order not to need to - * recreate temporary vectors every - * time that function is called). - */ - mutable VectorBase temp_vector; - - /** - * An internal array of integer - * values that is used to store the - * column indices when - * adding/inserting local data into - * the (large) sparse matrix. - */ - std::vector column_indices; - - /** - * An internal array of double values - * that is used to store the column - * indices when adding/inserting - * local data into the (large) sparse - * matrix. - */ - std::vector column_values; - /** * To allow calling protected * prepare_add() and @@ -3032,12 +2942,31 @@ namespace TrilinosWrappers inline void - SparseMatrix::compress (::dealii::VectorOperation::values /*operation*/) + SparseMatrix::compress (::dealii::VectorOperation::values operation) { + + Epetra_CombineMode mode = last_action; + if (last_action == Zero) + { + if ((operation==::dealii::VectorOperation::add) || + (operation==::dealii::VectorOperation::unknown)) + mode = Add; + else if (operation==::dealii::VectorOperation::insert) + mode = Insert; + } + else + { + Assert( + ((last_action == Add) && (operation!=::dealii::VectorOperation::insert)) + || + ((last_action == Insert) && (operation!=::dealii::VectorOperation::add)), + ExcMessage("operation and argument to compress() do not match")); + } + // flush buffers int ierr; ierr = matrix->GlobalAssemble (*column_space_map, matrix->RowMap(), - true); + true, mode); AssertThrow (ierr == 0, ExcTrilinosError(ierr)); @@ -3168,26 +3097,39 @@ namespace TrilinosWrappers last_action = Insert; int *col_index_ptr; - TrilinosScalar const *col_value_ptr; + TrilinosScalar *col_value_ptr; int n_columns; - // If we don't elide zeros, the pointers - // are already available... + TrilinosScalar short_val_array[100]; + int short_index_array[100]; + std::vector long_val_array; + std::vector long_index_array; + + + // If we don't elide zeros, the pointers are already available... need to + // cast to non-const pointers as that is the format taken by Trilinos (but + // we will not modify const data) if (elide_zero_values == false) { col_index_ptr = (int *)col_indices; - col_value_ptr = values; + col_value_ptr = const_cast(values); n_columns = n_cols; } else { - // Otherwise, extract nonzero values in - // each row and get the respective - // indices. - if (column_indices.size() < n_cols) + // Otherwise, extract nonzero values in each row and get the + // respective indices. + if (n_cols > 100) + { + long_val_array.resize(n_cols); + long_index_array.resize(n_cols); + col_index_ptr = &long_index_array[0]; + col_value_ptr = &long_val_array[0]; + } + else { - column_indices.resize(n_cols); - column_values.resize(n_cols); + col_index_ptr = &short_index_array[0]; + col_value_ptr = &short_val_array[0]; } n_columns = 0; @@ -3197,66 +3139,51 @@ namespace TrilinosWrappers Assert (numbers::is_finite(value), ExcNumberNotFinite()); if (value != 0) { - column_indices[n_columns] = col_indices[j]; - column_values[n_columns] = value; + col_index_ptr[n_columns] = col_indices[j]; + col_value_ptr[n_columns] = value; n_columns++; } } Assert(n_columns <= (int)n_cols, ExcInternalError()); - - col_index_ptr = (int *)&column_indices[0]; - col_value_ptr = &column_values[0]; } - // If the calling matrix owns the row to - // which we want to insert values, we - // can directly call the Epetra_CrsMatrix - // input function, which is much faster - // than the Epetra_FECrsMatrix - // function. We distinguish between two - // cases: the first one is when the matrix - // is not filled (i.e., it is possible to - // add new elements to the sparsity pattern), - // and the second one is when the pattern is - // already fixed. In the former case, we - // add the possibility to insert new values, - // and in the second we just replace + // If the calling matrix owns the row to which we want to insert values, + // we can directly call the Epetra_CrsMatrix input function, which is much + // faster than the Epetra_FECrsMatrix function. We distinguish between two + // cases: the first one is when the matrix is not filled (i.e., it is + // possible to add new elements to the sparsity pattern), and the second + // one is when the pattern is already fixed. In the former case, we add + // the possibility to insert new values, and in the second we just replace // data. if (row_partitioner().MyGID(static_cast(row)) == true) { if (matrix->Filled() == false) { ierr = matrix->Epetra_CrsMatrix::InsertGlobalValues(row, n_columns, - const_cast(col_value_ptr), + col_value_ptr, col_index_ptr); - // When inserting elements, we do - // not want to create exceptions in - // the case when inserting non-local - // data (since that's what we want - // to do right now). + // When inserting elements, we do not want to create exceptions in + // the case when inserting non-local data (since that's what we + // want to do right now). if (ierr > 0) ierr = 0; } else ierr = matrix->Epetra_CrsMatrix::ReplaceGlobalValues(row, n_columns, - const_cast(col_value_ptr), + col_value_ptr, col_index_ptr); } else { - // When we're at off-processor data, we - // have to stick with the standard - // Insert/ReplaceGlobalValues - // function. Nevertheless, the way we - // call it is the fastest one (any other - // will lead to repeated allocation and - // deallocation of memory in order to - // call the function we already use, - // which is very unefficient if writing - // one element at a time). + // When we're at off-processor data, we have to stick with the + // standard Insert/ReplaceGlobalValues function. Nevertheless, the way + // we call it is the fastest one (any other will lead to repeated + // allocation and deallocation of memory in order to call the function + // we already use, which is very unefficient if writing one element at + // a time). compressed = false; if (matrix->Filled() == false) @@ -3291,16 +3218,13 @@ namespace TrilinosWrappers if (value == 0) { - // we have to do checkings on Insert/Add - // in any case - // to be consistent with the MPI - // communication model (see the comments - // in the documentation of - // TrilinosWrappers::Vector), but we can - // save some work if the addend is - // zero. However, these actions are done - // in case we pass on to the other - // function. + // we have to do checkings on Insert/Add in any case to be consistent + // with the MPI communication model (see the comments in the + // documentation of TrilinosWrappers::Vector), but we can save some + // work if the addend is zero. However, these actions are done in case + // we pass on to the other function. + + // TODO: fix this (do not run compress here, but fail) if (last_action == Insert) { int ierr; @@ -3396,15 +3320,21 @@ namespace TrilinosWrappers last_action = Add; int *col_index_ptr; - TrilinosScalar const *col_value_ptr; + TrilinosScalar *col_value_ptr; int n_columns; - // If we don't elide zeros, the pointers - // are already available... + double short_val_array[100]; + int short_index_array[100]; + std::vector long_val_array; + std::vector long_index_array; + + // If we don't elide zeros, the pointers are already available... need to + // cast to non-const pointers as that is the format taken by Trilinos (but + // we will not modify const data) if (elide_zero_values == false) { col_index_ptr = (int *)col_indices; - col_value_ptr = values; + col_value_ptr = const_cast(values); n_columns = n_cols; #ifdef DEBUG for (unsigned int j=0; j 100) + { + long_val_array.resize(n_cols); + long_index_array.resize(n_cols); + col_index_ptr = &long_index_array[0]; + col_value_ptr = &long_val_array[0]; + } + else { - column_indices.resize(n_cols); - column_values.resize(n_cols); + col_index_ptr = &short_index_array[0]; + col_value_ptr = &short_val_array[0]; } n_columns = 0; for (unsigned int j=0; j(row)) == true) { ierr = matrix->Epetra_CrsMatrix::SumIntoGlobalValues(row, n_columns, - const_cast(col_value_ptr), + col_value_ptr, col_index_ptr); } else { - // When we're at off-processor data, we - // have to stick with the standard - // SumIntoGlobalValues - // function. Nevertheless, the way we - // call it is the fastest one (any other - // will lead to repeated allocation and - // deallocation of memory in order to - // call the function we already use, - // which is very unefficient if writing - // one element at a time). + // When we're at off-processor data, we have to stick with the + // standard SumIntoGlobalValues function. Nevertheless, the way we + // call it is the fastest one (any other will lead to repeated + // allocation and deallocation of memory in order to call the function + // we already use, which is very inefficient if writing one element at + // a time). compressed = false; ierr = matrix->SumIntoGlobalValues (1, (int *)&row, n_columns, @@ -3677,65 +3607,52 @@ namespace TrilinosWrappers - inline - void - SparseMatrix::vmult (VectorBase &dst, - const VectorBase &src) const - { - Assert (&src != &dst, ExcSourceEqualsDestination()); - Assert (matrix->Filled(), ExcMatrixNotCompressed()); - - Assert (src.vector_partitioner().SameAs(matrix->DomainMap()) == true, - ExcMessage ("Column map of matrix does not fit with vector map!")); - Assert (dst.vector_partitioner().SameAs(matrix->RangeMap()) == true, - ExcMessage ("Row map of matrix does not fit with vector map!")); - - const int ierr = matrix->Multiply (false, src.trilinos_vector(), - dst.trilinos_vector()); - Assert (ierr == 0, ExcTrilinosError(ierr)); - (void)ierr; // removes -Wunused-variable in optimized mode - } - - - - inline - void - SparseMatrix::vmult (parallel::distributed::Vector &dst, - const parallel::distributed::Vector &src) const + namespace internal { - Assert (&src != &dst, ExcSourceEqualsDestination()); - Assert (matrix->Filled(), ExcMatrixNotCompressed()); - - AssertDimension (dst.local_size(), static_cast(matrix->RangeMap().NumMyElements())); - AssertDimension (src.local_size(), static_cast(matrix->DomainMap().NumMyElements())); - - Epetra_Vector tril_dst (View, matrix->RangeMap(), dst.begin()); - Epetra_Vector tril_src (View, matrix->DomainMap(), - const_cast(src.begin())); + namespace SparseMatrix + { + template + inline + void check_vector_map_equality(const Epetra_CrsMatrix &, + const VectorType &, + const VectorType &) + { + } - const int ierr = matrix->Multiply (false, tril_src, tril_dst); - Assert (ierr == 0, ExcTrilinosError(ierr)); - (void)ierr; // removes -Wunused-variable in optimized mode + inline + void check_vector_map_equality(const Epetra_CrsMatrix &m, + const TrilinosWrappers::MPI::Vector &in, + const TrilinosWrappers::MPI::Vector &out) + { + Assert (in.vector_partitioner().SameAs(m.DomainMap()) == true, + ExcMessage ("Column map of matrix does not fit with vector map!")); + Assert (out.vector_partitioner().SameAs(m.RangeMap()) == true, + ExcMessage ("Row map of matrix does not fit with vector map!")); + } + } } - + template inline void - SparseMatrix::vmult (dealii::Vector &dst, - const dealii::Vector &src) const + SparseMatrix::vmult (VectorType &dst, + const VectorType &src) const { Assert (&src != &dst, ExcSourceEqualsDestination()); Assert (matrix->Filled(), ExcMatrixNotCompressed()); - AssertDimension (static_cast(matrix->DomainMap().NumMyElements()), - static_cast(matrix->DomainMap().NumGlobalElements())); - AssertDimension (dst.size(), static_cast(matrix->RangeMap().NumMyElements())); - AssertDimension (src.size(), static_cast(matrix->DomainMap().NumMyElements())); + internal::SparseMatrix::check_vector_map_equality(*matrix, src, dst); + const int dst_local_size = dst.end() - dst.begin(); + AssertDimension (dst_local_size, matrix->RangeMap().NumMyElements()); + const int src_local_size = src.end() - src.begin(); + AssertDimension (src_local_size, matrix->DomainMap().NumMyElements()); - Epetra_Vector tril_dst (View, matrix->RangeMap(), dst.begin()); - Epetra_Vector tril_src (View, matrix->DomainMap(), - const_cast(src.begin())); + Epetra_MultiVector tril_dst (View, matrix->RangeMap(), dst.begin(), + matrix->DomainMap().NumMyPoints(), 1); + Epetra_MultiVector tril_src (View, matrix->DomainMap(), + const_cast(src.begin()), + matrix->DomainMap().NumMyPoints(), 1); const int ierr = matrix->Multiply (false, tril_src, tril_dst); Assert (ierr == 0, ExcTrilinosError(ierr)); @@ -3743,42 +3660,27 @@ namespace TrilinosWrappers } - - inline - void - SparseMatrix::Tvmult (VectorBase &dst, - const VectorBase &src) const - { - Assert (&src != &dst, ExcSourceEqualsDestination()); - Assert (matrix->Filled(), ExcMatrixNotCompressed()); - - Assert (src.vector_partitioner().SameAs(matrix->RangeMap()) == true, - ExcMessage ("Column map of matrix does not fit with vector map!")); - Assert (dst.vector_partitioner().SameAs(matrix->DomainMap()) == true, - ExcMessage ("Row map of matrix does not fit with vector map!")); - - const int ierr = matrix->Multiply (true, src.trilinos_vector(), - dst.trilinos_vector()); - Assert (ierr == 0, ExcTrilinosError(ierr)); - (void)ierr; // removes -Wunused-variable in optimized mode - } - - - + + template inline void - SparseMatrix::Tvmult (parallel::distributed::Vector &dst, - const parallel::distributed::Vector &src) const + SparseMatrix::Tvmult (VectorType &dst, + const VectorType &src) const { Assert (&src != &dst, ExcSourceEqualsDestination()); Assert (matrix->Filled(), ExcMatrixNotCompressed()); - AssertDimension (dst.local_size(), static_cast(matrix->DomainMap().NumMyElements())); - AssertDimension (src.local_size(), static_cast(matrix->RangeMap().NumMyElements())); + internal::SparseMatrix::check_vector_map_equality(*matrix, dst, src); + const int dst_local_size = dst.end() - dst.begin(); + AssertDimension (dst_local_size, matrix->DomainMap().NumMyElements()); + const int src_local_size = src.end() - src.begin(); + AssertDimension (src_local_size, matrix->RangeMap().NumMyElements()); - Epetra_Vector tril_dst (View, matrix->DomainMap(), dst.begin()); - Epetra_Vector tril_src (View, matrix->RangeMap(), - const_cast(src.begin())); + Epetra_MultiVector tril_dst (View, matrix->DomainMap(), dst.begin(), + matrix->DomainMap().NumMyPoints(), 1); + Epetra_MultiVector tril_src (View, matrix->RangeMap(), + const_cast(src.begin()), + matrix->DomainMap().NumMyPoints(), 1); const int ierr = matrix->Multiply (true, tril_src, tril_dst); Assert (ierr == 0, ExcTrilinosError(ierr)); @@ -3787,61 +3689,50 @@ namespace TrilinosWrappers + template inline void - SparseMatrix::Tvmult (dealii::Vector &dst, - const dealii::Vector &src) const + SparseMatrix::vmult_add (VectorType &dst, + const VectorType &src) const { Assert (&src != &dst, ExcSourceEqualsDestination()); - Assert (matrix->Filled(), ExcMatrixNotCompressed()); - - AssertDimension (static_cast(matrix->DomainMap().NumMyElements()), - static_cast(matrix->DomainMap().NumGlobalElements())); - AssertDimension (dst.size(), static_cast(matrix->DomainMap().NumMyElements())); - AssertDimension (src.size(), static_cast(matrix->RangeMap().NumMyElements())); - - Epetra_Vector tril_dst (View, matrix->DomainMap(), dst.begin()); - Epetra_Vector tril_src (View, matrix->RangeMap(), - const_cast(src.begin())); - const int ierr = matrix->Multiply (true, tril_src, tril_dst); - Assert (ierr == 0, ExcTrilinosError(ierr)); - (void)ierr; // removes -Wunused-variable in optimized mode + // Reinit a temporary vector with fast argument set, which does not + // overwrite the content (to save time). However, the + // TrilinosWrappers::Vector classes do not support this, so create a + // deal.II local vector that has this fast setting. It will be accepted in + // vmult because it only checks the local size. + dealii::Vector temp_vector; + temp_vector.reinit(dst.end()-dst.begin(), true); + dealii::VectorView src_view(src.end()-src.begin(), src.begin()); + dealii::VectorView dst_view(dst.end()-dst.begin(), dst.begin()); + vmult (temp_vector, static_cast&>(src_view)); + if (dst_view.size() > 0) + dst_view += temp_vector; } + template inline void - SparseMatrix::vmult_add (VectorBase &dst, - const VectorBase &src) const + SparseMatrix::Tvmult_add (VectorType &dst, + const VectorType &src) const { Assert (&src != &dst, ExcSourceEqualsDestination()); - // Choose to reinit the vector with fast - // argument set, which does not overwrite - // the content -- this is what we need - // since we're going to overwrite that - // anyway in the vmult operation. - temp_vector.reinit(dst, true); - - vmult (temp_vector, src); - dst += temp_vector; - } - - - - inline - void - SparseMatrix::Tvmult_add (VectorBase &dst, - const VectorBase &src) const - { - Assert (&src != &dst, ExcSourceEqualsDestination()); - - temp_vector.reinit(dst, true); - - Tvmult (temp_vector, src); - dst += temp_vector; + // Reinit a temporary vector with fast argument set, which does not + // overwrite the content (to save time). However, the + // TrilinosWrappers::Vector classes do not support this, so create a + // deal.II local vector that has this fast setting. It will be accepted in + // vmult because it only checks the local size. + dealii::Vector temp_vector; + temp_vector.reinit(dst.end()-dst.begin(), true); + dealii::VectorView src_view(src.end()-src.begin(), src.begin()); + dealii::VectorView dst_view(dst.end()-dst.begin(), dst.begin()); + Tvmult (temp_vector, static_cast&>(src_view)); + if (dst_view.size() > 0) + dst_view += temp_vector; } @@ -3853,7 +3744,8 @@ namespace TrilinosWrappers Assert (row_partitioner().SameAs(domain_partitioner()), ExcNotQuadratic()); - temp_vector.reinit(v); + VectorBase temp_vector; + temp_vector.reinit(v, true); vmult (temp_vector, v); return temp_vector*v; @@ -3869,7 +3761,8 @@ namespace TrilinosWrappers Assert (row_partitioner().SameAs(domain_partitioner()), ExcNotQuadratic()); - temp_vector.reinit(v); + VectorBase temp_vector; + temp_vector.reinit(v, true); vmult (temp_vector, v); return u*temp_vector; diff --git a/deal.II/include/deal.II/lac/trilinos_vector.h b/deal.II/include/deal.II/lac/trilinos_vector.h index 374bc9a29b..79bafae4fa 100644 --- a/deal.II/include/deal.II/lac/trilinos_vector.h +++ b/deal.II/include/deal.II/lac/trilinos_vector.h @@ -166,10 +166,23 @@ namespace TrilinosWrappers class Vector : public VectorBase { public: + /** + * A variable that indicates whether this vector + * supports distributed data storage. If true, then + * this vector also needs an appropriate compress() + * function that allows communicating recent set or + * add operations to individual elements to be communicated + * to other processors. + * + * For the current class, the variable equals + * true, since it does support parallel data storage. + */ + static const bool supports_distributed_data = true; + /** * @name Basic constructors and initalization. */ -//@{ + //@{ /** * Default constructor that * generates an empty (zero size) @@ -571,6 +584,21 @@ namespace TrilinosWrappers class Vector : public VectorBase { public: + /** + * A variable that indicates whether this vector + * supports distributed data storage. If true, then + * this vector also needs an appropriate compress() + * function that allows communicating recent set or + * add operations to individual elements to be communicated + * to other processors. + * + * For the current class, the variable equals + * false, since it does not support parallel data storage. + * If you do need parallel data storage, use + * TrilinosWrappers::MPI::Vector. + */ + static const bool supports_distributed_data = false; + /** * Default constructor that * generates an empty (zero size) diff --git a/deal.II/include/deal.II/lac/trilinos_vector_base.h b/deal.II/include/deal.II/lac/trilinos_vector_base.h index 134bc47064..05d5f24117 100644 --- a/deal.II/include/deal.II/lac/trilinos_vector_base.h +++ b/deal.II/include/deal.II/lac/trilinos_vector_base.h @@ -1,7 +1,7 @@ //--------------------------------------------------------------------------- // $Id$ // -// Copyright (C) 2008, 2009, 2010, 2011, 2012 by the deal.II authors +// Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -267,10 +267,12 @@ namespace TrilinosWrappers * C standard libraries * vector<...> class. */ - typedef TrilinosScalar value_type; - typedef TrilinosScalar real_type; - typedef std::size_t size_type; - typedef internal::VectorReference reference; + typedef TrilinosScalar value_type; + typedef TrilinosScalar real_type; + typedef std::size_t size_type; + typedef value_type *iterator; + typedef const value_type *const_iterator; + typedef internal::VectorReference reference; typedef const internal::VectorReference const_reference; /** @@ -519,6 +521,22 @@ namespace TrilinosWrappers */ bool in_local_range (const unsigned int index) const; + /** + * Return an index set that describes which elements of this vector + * are owned by the current processor. Note that this index set does + * not include elements this vector may store locally as ghost + * elements but that are in fact owned by another processor. + * As a consequence, the index sets returned on different + * processors if this is a distributed vector will form disjoint + * sets that add up to the complete index set. + * Obviously, if a vector is created on only one processor, then + * the result would satisfy + * @code + * vec.locally_owned_elements() == complete_index_set (vec.size()) + * @endcode + */ + IndexSet locally_owned_elements () const; + /** * Return if the vector contains ghost * elements. This answer is true if there @@ -659,6 +677,33 @@ namespace TrilinosWrappers */ TrilinosScalar el (const unsigned int index) const; + /** + * Make the Vector class a bit like the vector<> class of + * the C++ standard library by returning iterators to the start and end + * of the locally owned elements of this vector. The ordering of local elements corresponds to the one given + * + * It holds that end() - begin() == local_size(). + */ + iterator begin (); + + /** + * Return constant iterator to the start of the locally owned elements + * of the vector. + */ + const_iterator begin () const; + + /** + * Return an iterator pointing to the element past the end of the array + * of locally owned entries. + */ + iterator end (); + + /** + * Return a constant iterator pointing to the element past the end of + * the array of the locally owned entries. + */ + const_iterator end () const; + /** * A collective set operation: * instead of setting individual @@ -1197,6 +1242,23 @@ namespace TrilinosWrappers + inline + IndexSet + VectorBase::locally_owned_elements() const + { + IndexSet is (size()); + + // TODO: Trilinos does allow non-contiguous ranges for locally + // owned elements. if that is the case for the current + // vector, local_range() will throw an exception. Fix this. + const std::pair x = local_range(); + is.add_range (x.first, x.second); + + return is; + } + + + inline bool VectorBase::has_ghost_elements() const @@ -1232,6 +1294,42 @@ namespace TrilinosWrappers + inline + VectorBase::iterator + VectorBase::begin() + { + return (*vector)[0]; + } + + + + inline + VectorBase::iterator + VectorBase::end() + { + return (*vector)[0]+local_size(); + } + + + + inline + VectorBase::const_iterator + VectorBase::begin() const + { + return (*vector)[0]; + } + + + + inline + VectorBase::const_iterator + VectorBase::end() const + { + return (*vector)[0]+local_size(); + } + + + inline void VectorBase::reinit (const VectorBase &v, diff --git a/deal.II/include/deal.II/lac/vector.h b/deal.II/include/deal.II/lac/vector.h index 05ebaa680d..6c7fe3e9ce 100644 --- a/deal.II/include/deal.II/lac/vector.h +++ b/deal.II/include/deal.II/lac/vector.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -140,6 +141,19 @@ public: */ typedef typename numbers::NumberTraits::real_type real_type; + /** + * A variable that indicates whether this vector + * supports distributed data storage. If true, then + * this vector also needs an appropriate compress() + * function that allows communicating recent set or + * add operations to individual elements to be communicated + * to other processors. + * + * For the current class, the variable equals + * false, since it does not support parallel data storage. + */ + static const bool supports_distributed_data = false; + public: /** @@ -587,6 +601,26 @@ public: */ bool in_local_range (const types::global_dof_index global_index) const; + /** + * Return an index set that describes which elements of this vector + * are owned by the current processor. Note that this index set does + * not include elements this vector may store locally as ghost + * elements but that are in fact owned by another processor. + * As a consequence, the index sets returned on different + * processors if this is a distributed vector will form disjoint + * sets that add up to the complete index set. + * Obviously, if a vector is created on only one processor, then + * the result would satisfy + * @code + * vec.locally_owned_elements() == complete_index_set (vec.size()) + * @endcode + * + * Since the current data type does not support parallel data storage + * across different processors, the returned index set is the + * complete index set. + */ + IndexSet locally_owned_elements () const; + /** * Return dimension of the vector. */ diff --git a/deal.II/include/deal.II/lac/vector.templates.h b/deal.II/include/deal.II/lac/vector.templates.h index 09afdc4a68..5cb3ba459a 100644 --- a/deal.II/include/deal.II/lac/vector.templates.h +++ b/deal.II/include/deal.II/lac/vector.templates.h @@ -335,20 +335,20 @@ namespace internal } template - void copy_subrange (const unsigned int begin, - const unsigned int end, + void copy_subrange (const unsigned int begin, + const unsigned int end, const dealii::Vector &src, - dealii::Vector &dst) + dealii::Vector &dst) { memcpy(&*(dst.begin()+begin), &*(src.begin()+begin), (end-begin)*sizeof(T)); } template - void copy_subrange (const unsigned int begin, - const unsigned int end, + void copy_subrange (const unsigned int begin, + const unsigned int end, const dealii::Vector &src, - dealii::Vector &dst) + dealii::Vector &dst) { const T *q = src.begin()+begin; const T *const end_q = src.begin()+end; @@ -358,18 +358,21 @@ namespace internal } template - void copy_subrange_wrap (const unsigned int begin, - const unsigned int end, + void copy_subrange_wrap (const unsigned int begin, + const unsigned int end, const dealii::Vector &src, - dealii::Vector &dst) + dealii::Vector &dst) { copy_subrange (begin, end, src, dst); } template void copy_vector (const dealii::Vector &src, - dealii::Vector &dst) + dealii::Vector &dst) { + if (PointerComparison::equal(&src, &dst)) + return; + const unsigned int vec_size = src.size(); const unsigned int dst_size = dst.size(); if (dst_size != vec_size) @@ -1431,6 +1434,15 @@ void Vector::block_read (std::istream &in) +template +IndexSet +Vector::locally_owned_elements() const +{ + return complete_index_set(size()); +} + + + template std::size_t Vector::memory_consumption () const diff --git a/deal.II/include/deal.II/matrix_free/dof_info.h b/deal.II/include/deal.II/matrix_free/dof_info.h index 90c7fccdab..b7a791dd2b 100644 --- a/deal.II/include/deal.II/matrix_free/dof_info.h +++ b/deal.II/include/deal.II/matrix_free/dof_info.h @@ -354,6 +354,13 @@ namespace internal */ std::vector plain_dof_indices; + /** + * Stores the dimension of the underlying DoFHandler. Since the indices + * are not templated, this is the variable that makes the dimension + * accessible in the (rare) cases it is needed inside this class. + */ + unsigned int dimension; + /** * Stores the number of components in the DoFHandler where the indices * have been read from. diff --git a/deal.II/include/deal.II/matrix_free/dof_info.templates.h b/deal.II/include/deal.II/matrix_free/dof_info.templates.h index 2430dcecdb..64623405d5 100644 --- a/deal.II/include/deal.II/matrix_free/dof_info.templates.h +++ b/deal.II/include/deal.II/matrix_free/dof_info.templates.h @@ -131,6 +131,7 @@ namespace internal constrained_dofs (dof_info_in.constrained_dofs), row_starts_plain_indices (dof_info_in.row_starts_plain_indices), plain_dof_indices (dof_info_in.plain_dof_indices), + dimension (dof_info_in.dimension), n_components (dof_info_in.n_components), dofs_per_cell (dof_info_in.dofs_per_cell), dofs_per_face (dof_info_in.dofs_per_face), @@ -153,6 +154,7 @@ namespace internal ghost_dofs.clear(); dofs_per_cell.clear(); dofs_per_face.clear(); + dimension = 2; n_components = 0; row_starts_plain_indices.clear(); plain_dof_indices.clear(); diff --git a/deal.II/include/deal.II/matrix_free/fe_evaluation.h b/deal.II/include/deal.II/matrix_free/fe_evaluation.h index 9acb29a336..187716af64 100644 --- a/deal.II/include/deal.II/matrix_free/fe_evaluation.h +++ b/deal.II/include/deal.II/matrix_free/fe_evaluation.h @@ -687,22 +687,6 @@ protected: */ unsigned int cell_data_number; - /** - * If the present cell chunk for vectorization is not completely filled up - * with data, this field stores how many physical cells are underlying. Is - * between 1 and VectorizedArray::n_array_elements-1 (inclusive). - */ - unsigned int n_irreg_components_filled; - - /** - * Stores whether the present cell chunk used in vectorization is not - * completely filled up with physical cells. E.g. if vectorization dictates - * that four cells should be worked with but only three physical cells are - * left, this flag will be set to true, otherwise to false. Mainly used for - * internal checking when reading from vectors or writing to vectors. - */ - bool at_irregular_cell; - /** * Debug information to track whether dof values have been initialized * before accessed. Used to control exceptions when uninitialized data is @@ -1465,17 +1449,14 @@ FEEvaluationBase jacobian_grad_upper(0), cell (numbers::invalid_unsigned_int), cell_type (internal::MatrixFreeFunctions::undefined), - cell_data_number (0), - n_irreg_components_filled (0), - at_irregular_cell (false) + cell_data_number (0) { - Assert (matrix_info.indices_initialized() == true, - ExcNotInitialized()); Assert (matrix_info.mapping_initialized() == true, ExcNotInitialized()); AssertDimension (matrix_info.get_size_info().vectorization_length, VectorizedArray::n_array_elements); Assert (n_fe_components == 1 || + n_components == 1 || n_components == n_fe_components, ExcMessage ("The underlying FE is vector-valued. In this case, the " "template argument n_components must be a the same " @@ -1553,9 +1534,6 @@ FEEvaluationBase } } - n_irreg_components_filled = - std_cxx1x::get<2>(dof_info.row_starts[cell_in]); - at_irregular_cell = n_irreg_components_filled > 0; #ifdef DEBUG dof_values_initialized = false; values_quad_initialized = false; @@ -1832,6 +1810,8 @@ FEEvaluationBase // into the local data field or write local data into the vector. Certain // operations are no-ops for the given use case. + Assert (matrix_info.indices_initialized() == true, + ExcNotInitialized()); Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized()); // loop over all local dofs. ind_local holds local number on cell, index @@ -1844,6 +1824,10 @@ FEEvaluationBase dof_info.end_indicators(cell); unsigned int ind_local = 0; + const unsigned int n_irreg_components_filled = + std_cxx1x::get<2>(dof_info.row_starts[cell]); + const bool at_irregular_cell = n_irreg_components_filled > 0; + // scalar case (or case when all components have the same degrees of freedom // and sit on a different vector each) if (n_fe_components == 1) @@ -2447,6 +2431,8 @@ FEEvaluationBase { // this is different from the other three operations because we do not use // constraints here, so this is a separate function. + Assert (matrix_info.indices_initialized() == true, + ExcNotInitialized()); Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized()); Assert (dof_info.store_plain_indices == true, ExcNotInitialized()); @@ -2455,6 +2441,10 @@ FEEvaluationBase // points to the global indices stored in index_local_to_global const unsigned int *dof_indices = dof_info.begin_indices_plain(cell); + const unsigned int n_irreg_components_filled = + std_cxx1x::get<2>(dof_info.row_starts[cell]); + const bool at_irregular_cell = n_irreg_components_filled > 0; + // scalar case (or case when all components have the same degrees of freedom // and sit on a different vector each) if (n_fe_components == 1) diff --git a/deal.II/include/deal.II/matrix_free/matrix_free.h b/deal.II/include/deal.II/matrix_free/matrix_free.h index fa52dc196f..1cee5ba92b 100644 --- a/deal.II/include/deal.II/matrix_free/matrix_free.h +++ b/deal.II/include/deal.II/matrix_free/matrix_free.h @@ -1851,7 +1851,7 @@ namespace internal inline void compress_finish (parallel::distributed::Vector &vec) { - vec.compress_finish(); + vec.compress_finish(::dealii::VectorOperation::add); } diff --git a/deal.II/include/deal.II/matrix_free/matrix_free.templates.h b/deal.II/include/deal.II/matrix_free/matrix_free.templates.h index ccb397894f..301920ad23 100644 --- a/deal.II/include/deal.II/matrix_free/matrix_free.templates.h +++ b/deal.II/include/deal.II/matrix_free/matrix_free.templates.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -159,6 +160,32 @@ internal_reinit(const Mapping &mapping, initialize_indices (constraint, locally_owned_set); } + // initialize bare structures + else if (dof_info.size() != dof_handler.size()) + { + initialize_dof_handlers(dof_handler, additional_data.level_mg_handler); + std::vector dummy; + size_info.make_layout (cell_level_index.size(), + VectorizedArray::n_array_elements, + dummy, dummy); + for (unsigned int i=0; iget_fe().element_multiplicity(0); + dof_info[i].dofs_per_cell.push_back(dof_handler[i]->get_fe().dofs_per_cell); + dof_info[i].row_starts.resize(size_info.n_macro_cells+1); + std_cxx1x::get<2>(dof_info[i].row_starts.back()) = + cell_level_index.size() % VectorizedArray::n_array_elements; + + // if indices are not initialized, the cell_level_index might not be + // divisible by the vectorization length. But it must be for + // mapping_info... + while (cell_level_index.size() % VectorizedArray::n_array_elements + != 0) + cell_level_index.push_back(cell_level_index.back()); + } + } + // Reads out the FE information and stores the shape function values, // gradients and Hessians for quadrature points. const unsigned int n_fe = dof_handler.size(); @@ -254,6 +281,33 @@ internal_reinit(const Mapping &mapping, initialize_indices (constraint, locally_owned_set); } + // initialize bare structures + else if (dof_info.size() != dof_handler.size()) + { + initialize_dof_handlers(dof_handler, additional_data.level_mg_handler); + std::vector dummy; + size_info.make_layout (cell_level_index.size(), + VectorizedArray::n_array_elements, + dummy, dummy); + for (unsigned int i=0; iget_fe().size() == 1, ExcNotImplemented()); + dof_info[i].dimension = dim; + dof_info[i].n_components = dof_handler[i]->get_fe()[0].element_multiplicity(0); + dof_info[i].dofs_per_cell.push_back(dof_handler[i]->get_fe()[0].dofs_per_cell); + dof_info[i].row_starts.resize(size_info.n_macro_cells+1); + std_cxx1x::get<2>(dof_info[i].row_starts.back()) = + cell_level_index.size() % VectorizedArray::n_array_elements; + + // if indices are not initialized, the cell_level_index might not be + // divisible by the vectorization length. But it must be for + // mapping_info... + while (cell_level_index.size() % VectorizedArray::n_array_elements + != 0) + cell_level_index.push_back(cell_level_index.back()); + } + } + // Reads out the FE information and stores the shape function values, // gradients and Hessians for quadrature points. const unsigned int n_components = dof_handler.size(); @@ -460,8 +514,8 @@ void MatrixFree::initialize_indices // cache number of finite elements and dofs_per_cell dof_info[no].dofs_per_cell.push_back (fe.dofs_per_cell); dof_info[no].dofs_per_face.push_back (fe.dofs_per_face); - dof_info[no].n_components = n_fe_components; - + dof_info[no].dimension = dim; + dof_info[no].n_components = n_fe_components; // get permutation that gives lexicographic renumbering of the cell // dofs renumber (this is necessary for FE_Q, for example, since @@ -471,15 +525,25 @@ void MatrixFree::initialize_indices const FE_Poly,dim,dim> *fe_poly = dynamic_cast,dim,dim>*> (&fe.base_element(0)); + const FE_Poly >,dim,dim> *fe_poly_piece = + dynamic_cast >,dim,dim>*> + (&fe.base_element(0)); // This class currently only works for elements derived from - // FE_Poly,dim,dim>. For any other - // element, the dynamic cast above will fail and give fe_poly == 0. - Assert (fe_poly != 0, ExcNotImplemented()); + // FE_Poly,dim,dim> or piecewise + // polynomials. For any other element, the dynamic casts above will + // fail and give fe_poly == 0. + Assert (fe_poly != 0 || fe_poly_piece != 0, ExcNotImplemented()); if (n_fe_components == 1) { - lexicographic_inv[no][fe_index] = - fe_poly->get_poly_space_numbering_inverse(); + if (fe_poly != 0) + lexicographic_inv[no][fe_index] = + fe_poly->get_poly_space_numbering_inverse(); + else + lexicographic_inv[no][fe_index] = + fe_poly_piece->get_poly_space_numbering_inverse(); AssertDimension (lexicographic_inv[no][fe_index].size(), dof_info[no].dofs_per_cell[fe_index]); } @@ -488,7 +552,8 @@ void MatrixFree::initialize_indices // ok, we have more than one component Assert (n_fe_components > 1, ExcInternalError()); std::vector scalar_lex = - fe_poly->get_poly_space_numbering(); + fe_poly != 0 ? fe_poly->get_poly_space_numbering() : + fe_poly_piece->get_poly_space_numbering(); AssertDimension (scalar_lex.size() * n_fe_components, dof_info[no].dofs_per_cell[fe_index]); std::vector lexicographic (dof_info[no].dofs_per_cell[fe_index]); @@ -728,6 +793,8 @@ void MatrixFree::clear() cell_level_index.clear(); size_info.clear(); task_info.clear(); + dof_handlers.dof_handler.clear(); + dof_handlers.hp_dof_handler.clear(); indices_are_initialized = false; mapping_is_initialized = false; } diff --git a/deal.II/include/deal.II/matrix_free/shape_info.templates.h b/deal.II/include/deal.II/matrix_free/shape_info.templates.h index c1f251e56d..77386ed65d 100644 --- a/deal.II/include/deal.II/matrix_free/shape_info.templates.h +++ b/deal.II/include/deal.II/matrix_free/shape_info.templates.h @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 2011 by the deal.II authors +// Copyright (C) 2011, 2013 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -15,8 +15,8 @@ #include #include #include +#include #include -#include #include @@ -60,8 +60,14 @@ namespace internal { const FE_Poly,dim,dim> *fe_poly = dynamic_cast,dim,dim>*>(&fe); - Assert (fe_poly != 0, ExcNotImplemented()); - lexicographic = fe_poly->get_poly_space_numbering_inverse(); + const FE_Poly >,dim,dim> *fe_poly_piece = + dynamic_cast >,dim,dim>*> (&fe); + Assert (fe_poly != 0 || fe_poly_piece, ExcNotImplemented()); + lexicographic = fe_poly != 0 ? + fe_poly->get_poly_space_numbering_inverse() : + fe_poly_piece->get_poly_space_numbering_inverse(); // to evaluate 1D polynomials, evaluate along the line where y=z=0, // assuming that shape_value(0,Point()) == 1. otherwise, need diff --git a/deal.II/include/deal.II/meshworker/vector_info.h b/deal.II/include/deal.II/meshworker/vector_info.h deleted file mode 100644 index f0383ed55f..0000000000 --- a/deal.II/include/deal.II/meshworker/vector_info.h +++ /dev/null @@ -1,165 +0,0 @@ -//--------------------------------------------------------------------------- -// $Id$ -// -// Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 by the deal.II authors -// -// This file is subject to QPL and may not be distributed -// without copyright and license information. Please refer -// to the file deal.II/doc/license.html for the text and -// further information on this license. -// -//--------------------------------------------------------------------------- - -#ifndef __deal2__mesh_worker_vector_info_h -#define __deal2__mesh_worker_vector_info_h - -#include -#include -#include - -DEAL_II_NAMESPACE_OPEN - -namespace MeshWorker -{ - /** - * This class is a simple object that can be used in - * MeshWorker::loop(). For all vectors provided in the argument to - * initialize_data(), this class generates the local data vector on a - * cell specified by reinit(). - * - * This objects of class as part of VectorInfoBox conform to the - * interface for INFOBOX laid out in the documentation of MeshWorker. - * - * See IntegrationInfo for an alternative. - * - * @author Guido Kanschat - * @date 2012 - */ - class VectorInfo - { - public: - /** - * Initialize the data - * vector and cache the - * selector. - */ - void initialize_data(const NamedData*> &data); - - /** - * Reinitialize the local data - * vectors to represent the - * information on the given cell. - */ - template - void reinit(const MeshWorker::DoFInfo &cell); - - /** - * Return local data vector - * i, which selects the - * local data from vector - * i of the - * data object - * used in initialize_data(). - */ - BlockVector &operator() (unsigned int i); - - private: - std::vector > local_data; - /** - * The global data vector - * used to compute function - * values in quadrature - * points. - */ - SmartPointer*> > global_data; - }; - - - /** - * A class conforming to the INFOBOX interface in the documentation of - * MeshWorker. This class provides local operators in - * MeshWorker::loop() with the local values of the degrees of freedom - * of global vectors, for instance for calculations based on cell - * matrices. - * - * For an alternative providing the values in quadrature points see - * IntegrationInfoBox. - * - * @author Guido Kanschat - * @date 2012 - */ - class VectorInfoBox - { - public: - typedef VectorInfo CellInfo; - - void initialize_data(const NamedData*> &data); - - template - void post_cell(const MeshWorker::DoFInfoBox &) - {} - - template - void post_faces(const MeshWorker::DoFInfoBox &) - {} - - VectorInfo cell; - VectorInfo boundary; - VectorInfo face; - VectorInfo subface; - VectorInfo neighbor; - }; - - - inline - void - VectorInfo::initialize_data(const NamedData*> &data) - { - global_data = &data; - local_data.resize(global_data->size()); - } - - - template - inline - void - VectorInfo::reinit(const MeshWorker::DoFInfo &i) - { - const NamedData*> &gd = *global_data; - - for (unsigned int k=0; k &v = *gd(k); - - local_data[k].reinit(i.block_info->local()); - for (unsigned int j=0; j & - VectorInfo::operator() (unsigned int i) - { - AssertIndexRange(i, local_data.size()); - return local_data[i]; - } - - - inline - void - VectorInfoBox::initialize_data(const NamedData*> &data) - { - cell.initialize_data(data); - boundary.initialize_data(data); - face.initialize_data(data); - subface.initialize_data(data); - neighbor.initialize_data(data); - } - -} - -DEAL_II_NAMESPACE_CLOSE - -#endif diff --git a/deal.II/include/deal.II/multigrid/mg_block_smoother.h b/deal.II/include/deal.II/multigrid/mg_block_smoother.h index 6c5b41768b..688bfdf78b 100644 --- a/deal.II/include/deal.II/multigrid/mg_block_smoother.h +++ b/deal.II/include/deal.II/multigrid/mg_block_smoother.h @@ -287,7 +287,7 @@ MGSmootherBlock::smooth( { deallog.push("Smooth"); - unsigned int maxlevel = matrices.get_maxlevel(); + unsigned int maxlevel = matrices.max_level(); unsigned int steps2 = steps; if (variable) diff --git a/deal.II/include/deal.II/multigrid/mg_transfer.h b/deal.II/include/deal.II/multigrid/mg_transfer.h index 8ea9888b27..d938dab05a 100644 --- a/deal.II/include/deal.II/multigrid/mg_transfer.h +++ b/deal.II/include/deal.II/multigrid/mg_transfer.h @@ -1,7 +1,7 @@ //--------------------------------------------------------------------------- // $Id$ // -// Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012 by the deal.II authors +// Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012, 2013 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -43,6 +43,13 @@ namespace internal { typedef ::dealii::SparsityPattern Sparsity; typedef ::dealii::SparseMatrix Matrix; + + template + static void reinit(Matrix& matrix, Sparsity& sparsity, int level, const CSP& csp, const DH&) + { + sparsity.copy_from (csp); + matrix.reinit (sparsity); + } }; #ifdef DEAL_II_WITH_TRILINOS @@ -51,6 +58,15 @@ namespace internal { typedef ::dealii::TrilinosWrappers::SparsityPattern Sparsity; typedef ::dealii::TrilinosWrappers::SparseMatrix Matrix; + + template + static void reinit(Matrix& matrix, Sparsity& sparsity, int level, const CSP& csp, DH& dh) + { + matrix.reinit(dh.locally_owned_mg_dofs(level+1), + dh.locally_owned_mg_dofs(level), + csp, MPI_COMM_WORLD, true); + } + }; template <> @@ -58,6 +74,11 @@ namespace internal { typedef ::dealii::TrilinosWrappers::SparsityPattern Sparsity; typedef ::dealii::TrilinosWrappers::SparseMatrix Matrix; + + template + static void reinit(Matrix& matrix, Sparsity& sparsity, int level, const CSP& csp, DH& dh) + { + } }; #endif } @@ -196,14 +217,12 @@ public: set_component_to_block_map (const std::vector &map); /** - * Finite element does not - * provide prolongation matrices. + * Finite element does not provide prolongation matrices. */ DeclException0(ExcNoProlongation); /** - * Call @p build_matrices - * function first. + * You have to call build_matrices() before using this object. */ DeclException0(ExcMatricesNotBuilt); @@ -211,7 +230,11 @@ public: * Memory used by this object. */ std::size_t memory_consumption () const; - + + /** + * Print all the matrices for debugging purposes. + */ + void print_matrices(std::ostream& os) const; private: diff --git a/deal.II/include/deal.II/numerics/matrix_tools.h b/deal.II/include/deal.II/numerics/matrix_tools.h index 38be9336e2..fb3dca6ce8 100644 --- a/deal.II/include/deal.II/numerics/matrix_tools.h +++ b/deal.II/include/deal.II/numerics/matrix_tools.h @@ -26,6 +26,8 @@ DEAL_II_NAMESPACE_OPEN // forward declarations template class Quadrature; +class ConstraintMatrix; + template class Vector; template class FullMatrix; template class SparseMatrix; @@ -105,17 +107,27 @@ namespace TrilinosWrappers * the matrix wasn't empty before. Therefore you may want to clear the matrix * before assemblage. * - * All created matrices are `raw': they are not condensed, + * By default, all created matrices are `raw': they are not condensed, * i.e. hanging nodes are not eliminated. The reason is that you may * want to add several matrices and could then condense afterwards * only once, instead of for every matrix. To actually do computations * with these matrices, you have to condense the matrix using the * ConstraintMatrix::condense function; you also have to * condense the right hand side accordingly and distribute the - * solution afterwards. + * solution afterwards. Alternatively, you can give an optional argument + * ConstraintMatrix that writes cell matrix (and vector) entries with + * distribute_local_to_global into the global matrix and vector. This way, + * adding several matrices from different sources is more complicated and + * you should make sure that you do not mix different ways of applying + * constraints. Particular caution is necessary when the given + * constraint matrix contains inhomogeneous constraints: In that case, the + * matrix assembled this way must be the only matrix (or you need to + * assemble the same right hand side for every matrix + * you generate and add together). * * If you want to use boundary conditions with the matrices generated - * by the functions of this class, you have to use a function like + * by the functions of this class in addition to the ones in a possible + * constraint matrix, you have to use a function like * ProblemBase<>::apply_dirichlet_bc to matrix and right hand * side. * @@ -210,154 +222,141 @@ namespace TrilinosWrappers namespace MatrixCreator { /** - * Assemble the mass matrix. If no - * coefficient is given, it is assumed - * to be unity. + * Assemble the mass matrix. If no coefficient is given, it is assumed to be + * unity. + * + * If the library is configured to use multithreading, this function works + * in parallel. * - * If the library is configured - * to use multithreading, this - * function works in parallel. + * The optional argument @p constraints allows to apply constraints on the + * resulting matrix directly. Be careful when combining several matrices and + * using inhomogeneous constraints. * - * See the general doc of this class - * for more information. + * See the general doc of this class for more information. */ template void create_mass_matrix (const Mapping &mapping, const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Calls the create_mass_matrix() - * function, see above, with + * Calls the create_mass_matrix() function, see above, with * mapping=MappingQ1@(). */ template void create_mass_matrix (const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Assemble the mass matrix and a - * right hand side vector. If no - * coefficient is given, it is - * assumed to be unity. + * Assemble the mass matrix and a right hand side vector. If no coefficient + * is given, it is assumed to be unity. + * + * If the library is configured to use multithreading, this function works + * in parallel. * - * If the library is configured - * to use multithreading, this - * function works in parallel. + * The optional argument @p constraints allows to apply constraints on the + * resulting matrix directly. Be careful when combining several matrices and + * using inhomogeneous constraints. * - * See the general doc of this - * class for more information. + * See the general doc of this class for more information. */ template - void create_mass_matrix (const Mapping &mapping, - const DoFHandler &dof, + void create_mass_matrix (const Mapping &mapping, + const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function &rhs, + const Function &rhs, Vector &rhs_vector, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Calls the create_mass_matrix() - * function, see above, with + * Calls the create_mass_matrix() function, see above, with * mapping=MappingQ1@(). */ template - void create_mass_matrix (const DoFHandler &dof, + void create_mass_matrix (const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function &rhs, + const Function &rhs, Vector &rhs_vector, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); + /** - * Same function as above, but for hp - * objects. + * Same function as above, but for hp objects. */ template void create_mass_matrix (const hp::MappingCollection &mapping, const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Same function as above, but for hp - * objects. + * Same function as above, but for hp objects. */ template void create_mass_matrix (const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Same function as above, but for hp - * objects. + * Same function as above, but for hp objects. */ template - void create_mass_matrix (const hp::MappingCollection &mapping, - const hp::DoFHandler &dof, - const hp::QCollection &q, + void create_mass_matrix (const hp::MappingCollection &mapping, + const hp::DoFHandler &dof, + const hp::QCollection &q, SparseMatrix &matrix, - const Function &rhs, + const Function &rhs, Vector &rhs_vector, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Same function as above, but for hp - * objects. + * Same function as above, but for hp objects. */ template - void create_mass_matrix (const hp::DoFHandler &dof, - const hp::QCollection &q, + void create_mass_matrix (const hp::DoFHandler &dof, + const hp::QCollection &q, SparseMatrix &matrix, - const Function &rhs, + const Function &rhs, Vector &rhs_vector, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Assemble the mass matrix and a - * right hand side vector along - * the boundary. + * Assemble the mass matrix and a right hand side vector along the boundary. * - * The matrix is assumed to - * already be initialized with a - * suiting sparsity pattern (the - * DoFHandler provides an - * appropriate function). + * The matrix is assumed to already be initialized with a suiting sparsity + * pattern (the DoFHandler provides an appropriate function). * - * If the library is configured - * to use multithreading, this - * function works in parallel. + * If the library is configured to use multithreading, this function works + * in parallel. * - * @arg @p weight: an optional - * weight for the computation of - * the mass matrix. If no weight - * is given, it is set to one. + * @arg @p weight: an optional weight for the computation of the mass + * matrix. If no weight is given, it is set to one. * - * @arg @p component_mapping: if - * the components in @p - * boundary_functions and @p dof - * do not coincide, this vector - * allows them to be remapped. If - * the vector is not empty, it - * has to have one entry for each - * component in @p dof. This - * entry is the component number - * in @p boundary_functions that - * should be used for this - * component in @p dof. By - * default, no remapping is + * @arg @p component_mapping: if the components in @p boundary_functions and + * @p dof do not coincide, this vector allows them to be remapped. If the + * vector is not empty, it has to have one entry for each component in @p + * dof. This entry is the component number in @p boundary_functions that + * should be used for this component in @p dof. By default, no remapping is * applied. * - * @todo This function does not - * work for finite elements with - * cell-dependent shape functions. + * @todo This function does not work for finite elements with cell-dependent + * shape functions. */ template @@ -396,9 +395,7 @@ namespace MatrixCreator /** - * Calls the - * create_boundary_mass_matrix() - * function, see above, with + * Calls the create_boundary_mass_matrix() function, see above, with * mapping=MappingQ1@(). */ template @@ -413,8 +410,7 @@ namespace MatrixCreator std::vector component_mapping = std::vector()); /** - * Same function as above, but for hp - * objects. + * Same function as above, but for hp objects. */ template @@ -429,8 +425,7 @@ namespace MatrixCreator std::vector component_mapping = std::vector()); /** - * Same function as above, but for hp - * objects. + * Same function as above, but for hp objects. */ // // void create_boundary_mass_matrix (const hp::MappingCollection<1,1> &mapping, @@ -443,8 +438,7 @@ namespace MatrixCreator // const Function<1> * const a = 0); /** - * Same function as above, but for hp - * objects. + * Same function as above, but for hp objects. */ template @@ -458,123 +452,122 @@ namespace MatrixCreator std::vector component_mapping = std::vector()); /** - * Assemble the Laplace - * matrix. If no coefficient is - * given, it is assumed to be - * constant one. + * Assemble the Laplace matrix. If no coefficient is given, it is assumed to + * be constant one. + * + * If the library is configured to use multithreading, this function works + * in parallel. * - * If the library is configured - * to use multithreading, this - * function works in parallel. + * The optional argument @p constraints allows to apply constraints on the + * resulting matrix directly. Be careful when combining several matrices and + * using inhomogeneous constraints. * - * See the general doc of this - * class for more information. + * See the general doc of this class for more information. */ template - void create_laplace_matrix (const Mapping &mapping, - const DoFHandler &dof, + void create_laplace_matrix (const Mapping &mapping, + const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Calls the - * create_laplace_matrix() - * function, see above, with + * Calls the create_laplace_matrix() function, see above, with * mapping=MappingQ1@(). */ template - void create_laplace_matrix (const DoFHandler &dof, + void create_laplace_matrix (const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Assemble the Laplace matrix - * and a right hand side - * vector. If no coefficient is - * given, it is assumed to be - * constant one. + * Assemble the Laplace matrix and a right hand side vector. If no + * coefficient is given, it is assumed to be constant one. + * + * If the library is configured to use multithreading, this function works + * in parallel. * - * If the library is configured - * to use multithreading, this - * function works in parallel. + * The optional argument @p constraints allows to apply constraints on the + * resulting matrix directly. Be careful when combining several matrices and + * using inhomogeneous constraints. * - * See the general doc of this - * class for more information. + * See the general doc of this class for more information. */ template - void create_laplace_matrix (const Mapping &mapping, - const DoFHandler &dof, + void create_laplace_matrix (const Mapping &mapping, + const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function &rhs, + const Function &rhs, Vector &rhs_vector, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Calls the - * create_laplace_matrix() - * function, see above, with + * Calls the create_laplace_matrix() function, see above, with * mapping=MappingQ1@(). */ template - void create_laplace_matrix (const DoFHandler &dof, + void create_laplace_matrix (const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function &rhs, + const Function &rhs, Vector &rhs_vector, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Like the functions above, but for hp - * dof handlers, mappings, and quadrature - * collections. + * Like the functions above, but for hp dof handlers, mappings, and + * quadrature collections. */ template - void create_laplace_matrix (const hp::MappingCollection &mapping, - const hp::DoFHandler &dof, + void create_laplace_matrix (const hp::MappingCollection &mapping, + const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Like the functions above, but for hp - * dof handlers, mappings, and quadrature - * collections. + * Like the functions above, but for hp dof handlers, mappings, and + * quadrature collections. */ template - void create_laplace_matrix (const hp::DoFHandler &dof, + void create_laplace_matrix (const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Like the functions above, but for hp - * dof handlers, mappings, and quadrature - * collections. + * Like the functions above, but for hp dof handlers, mappings, and + * quadrature collections. */ template - void create_laplace_matrix (const hp::MappingCollection &mapping, - const hp::DoFHandler &dof, + void create_laplace_matrix (const hp::MappingCollection &mapping, + const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** - * Like the functions above, but for hp - * dof handlers, mappings, and quadrature - * collections. + * Like the functions above, but for hp dof handlers, mappings, and + * quadrature collections. */ template - void create_laplace_matrix (const hp::DoFHandler &dof, + void create_laplace_matrix (const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function *const a = 0); + const Function *const a = 0, + const ConstraintMatrix &constraints = ConstraintMatrix()); /** * Exception @@ -609,7 +602,7 @@ namespace MatrixCreator * respect for fixed degrees of freedom, and in a second step eliminate them * again from the linear system. The inclusion into the assembly process is as * follows: when the matrix and vectors are set up, a list of nodes subject to - * dirichlet bc is made and matrix and vectors are modified accordingly. This + * Dirichlet bc is made and matrix and vectors are modified accordingly. This * is done by deleting all entries in the matrix in the line of this degree of * freedom, setting the main diagonal entry to a suitable positive value and * the right hand side element to a value so that the solution of the linear @@ -798,7 +791,7 @@ namespace MatrixTools using namespace MatrixCreator; /** - * Apply dirichlet boundary conditions + * Apply Dirichlet boundary conditions * to the system matrix and vectors * as described in the general * documentation. @@ -812,7 +805,7 @@ namespace MatrixTools const bool eliminate_columns = true); /** - * Apply dirichlet boundary + * Apply Dirichlet boundary * conditions to the system * matrix and vectors as * described in the general @@ -830,7 +823,7 @@ namespace MatrixTools #ifdef DEAL_II_WITH_PETSC /** - * Apply dirichlet boundary conditions to + * Apply Dirichlet boundary conditions to * the system matrix and vectors as * described in the general * documentation. This function works on @@ -930,7 +923,7 @@ namespace MatrixTools #ifdef DEAL_II_WITH_TRILINOS /** - * Apply dirichlet boundary + * Apply Dirichlet boundary * conditions to the system matrix * and vectors as described in the * general documentation. This @@ -987,7 +980,7 @@ namespace MatrixTools const bool eliminate_columns = true); /** - * Apply dirichlet boundary + * Apply Dirichlet boundary * conditions to the system matrix * and vectors as described in the * general documentation. This diff --git a/deal.II/include/deal.II/numerics/vector_tools.h b/deal.II/include/deal.II/numerics/vector_tools.h index 4b72071289..12fa9add4e 100644 --- a/deal.II/include/deal.II/numerics/vector_tools.h +++ b/deal.II/include/deal.II/numerics/vector_tools.h @@ -669,15 +669,15 @@ namespace VectorTools const bool project_to_boundary_first = false); /** - * Prepare Dirichlet boundary - * conditions. Make up the list + * Compute Dirichlet boundary + * conditions. This function makes up a map * of degrees of freedom subject * to Dirichlet boundary - * conditions and the values to + * conditions and the corresponding values to * be assigned to them, by * interpolation around the * boundary. If the - * @p boundary_values contained + * @p boundary_values object contained * values before, the new ones * are added, or the old ones * overwritten if a node of the @@ -717,13 +717,24 @@ namespace VectorTools * boundary function will be used * for which the respective flag * was set in the component mask. - * See also @ref GlossComponentMask. - * - * It is assumed that the number - * of components of the function - * in @p boundary_function matches that + * See also @ref GlossComponentMask. As an example, assume that you are + * solving the Stokes equations in 2d, with variables $(u,v,p)$ and that + * you only want to interpolate boundary values for the pressure, then + * the component mask should correspond to (true,true,false). + * + * @note Whether a component mask has been specified or not, the number + * of components of the functions + * in @p function_map must match that * of the finite element used by - * @p dof. + * @p dof. In other words, for the example above, you need to provide a + * Function object that has 3 components (the two velocities and the + * pressure), even though you are only + * interested in the first two of them. interpolate_boundary_values() + * will then call this function to obtain a vector of 3 values at each + * interpolation point but only take the first two and discard the third. + * In other words, you are free to return whatever you like in the third + * component of the vector returned by Function::vector_value, but the + * Function object must state that it has 3 components. * * If the finite element used has * shape functions that are @@ -739,7 +750,7 @@ namespace VectorTools * non-primitive shape functions * must be @p false. * - * See the general doc for more + * See the general documentation of this class for more * information. */ template @@ -755,9 +766,9 @@ namespace VectorTools * taking only one pair of * boundary indicator and * corresponding boundary - * function. Calls the other - * function with remapped - * arguments. + * function. The same comments apply as for the previous function, in particular + * about the use of the component mask and the requires size of the function + * object. * * @see @ref GlossBoundaryIndicator "Glossary entry on boundary indicators" */ @@ -775,6 +786,9 @@ namespace VectorTools * interpolate_boundary_values() * function, see above, with * mapping=MappingQ1@(). + * The same comments apply as for the previous function, in particular + * about the use of the component mask and the requires size of the function + * object. * * @see @ref GlossBoundaryIndicator "Glossary entry on boundary indicators" */ @@ -792,6 +806,9 @@ namespace VectorTools * interpolate_boundary_values() * function, see above, with * mapping=MappingQ1@(). + * The same comments apply as for the previous function, in particular + * about the use of the component mask and the requires size of the function + * object. */ template void @@ -841,32 +858,44 @@ namespace VectorTools * The parameter @p boundary_component * corresponds to the number @p * boundary_indicator of the face. - * numbers::internal_face_boundary_id - * is an illegal value, since it is - * reserved for interior faces. * - * The flags in the last parameter, @p - * component_mask (see @ref GlossComponentMask) - * denote which - * components of the finite element - * space shall be interpolated. If it - * is left as specified by the default - * value (i.e. an empty array), all - * components are interpolated. If it - * is different from the default value, - * it is assumed that the number of - * entries equals the number of - * components in the boundary functions - * and the finite element, and those - * components in the given boundary - * function will be used for which the - * respective flag was set in the - * component mask. - * - * It is assumed that the number of - * components of the function in @p - * boundary_function matches that of - * the finite element used by @p dof. + * The flags in the last + * parameter, @p component_mask + * denote which components of the + * finite element space shall be + * interpolated. If it is left as + * specified by the default value + * (i.e. an empty array), all + * components are + * interpolated. If it is + * different from the default + * value, it is assumed that the + * number of entries equals the + * number of components in the + * boundary functions and the + * finite element, and those + * components in the given + * boundary function will be used + * for which the respective flag + * was set in the component mask. + * See also @ref GlossComponentMask. As an example, assume that you are + * solving the Stokes equations in 2d, with variables $(u,v,p)$ and that + * you only want to interpolate boundary values for the pressure, then + * the component mask should correspond to (true,true,false). + * + * @note Whether a component mask has been specified or not, the number + * of components of the functions + * in @p function_map must match that + * of the finite element used by + * @p dof. In other words, for the example above, you need to provide a + * Function object that has 3 components (the two velocities and the + * pressure), even though you are only + * interested in the first two of them. interpolate_boundary_values() + * will then call this function to obtain a vector of 3 values at each + * interpolation point but only take the first two and discard the third. + * In other words, you are free to return whatever you like in the third + * component of the vector returned by Function::vector_value, but the + * Function object must state that it has 3 components. * * If the finite element used has shape * functions that are non-zero in more @@ -879,7 +908,7 @@ namespace VectorTools * components of these non-primitive * shape functions must be @p false. * - * See the general doc for more + * See the general documentation of this class for more * information. * * @ingroup constraints @@ -896,8 +925,10 @@ namespace VectorTools * Same function as above, but taking * only one pair of boundary indicator * and corresponding boundary - * function. Calls the other function - * with remapped arguments. + * function. + * The same comments apply as for the previous function, in particular + * about the use of the component mask and the requires size of the function + * object. * * @ingroup constraints * @@ -917,6 +948,9 @@ namespace VectorTools * interpolate_boundary_values() * function, see above, with * mapping=MappingQ1@(). + * The same comments apply as for the previous function, in particular + * about the use of the component mask and the requires size of the function + * object. * * @ingroup constraints * @@ -936,6 +970,9 @@ namespace VectorTools * interpolate_boundary_values() * function, see above, with * mapping=MappingQ1@(). + * The same comments apply as for the previous function, in particular + * about the use of the component mask and the requires size of the function + * object. * * @ingroup constraints */ @@ -1856,77 +1893,107 @@ namespace VectorTools //@{ /** - * Compute the error of the - * finite element solution. - * Integrate the difference - * between a reference function - * which is given as a continuous - * function object, and a finite - * element function. - * - * The value of @p exponent is - * used for computing $L^p$-norms - * and $W^{1,p}$-norms. - * - * The additional argument @p - * weight allows to evaluate - * weighted norms. The weight - * function may be scalar, - * establishing a weight in the - * domain for all components - * equally. This may be used, for - * instance, to only integrates - * over parts of the domain. - * - * The weight function may also - * be vector-valued, with as many - * components as the finite - * element function: Then, - * different components get - * different weights. A typical - * application is when the error - * with respect to only one or a - * subset of the solution - * variables is to be computed, - * in which the other components - * would have weight values equal - * to zero. The - * ComponentSelectFunction class - * is particularly useful for - * this purpose. - * - * The weight function is - * expected to be positive, but - * negative values are not - * filtered. By default, no - * weighting function is given, - * i.e. weight=1 in the whole - * domain for all vector - * components uniformly. Note that - * one often wants to compute the - * error in only one component of - * a solution vector (e.g. for the - * pressure in the Stokes system, - * when the solution vector also - * contains the velocity components). - * In these cases, the weight should - * be a mask, i.e., be a - * vector function for which individual - * components are either zero or one. - * This can easily be achieved using - * the ComponentSelectFunction - * class. - * - * It is assumed that the number - * of components of the function - * @p exact_solution matches that - * of the finite element used by - * @p dof. + * Compute the error of the finite element solution. Integrate the + * difference between a reference function which is given as a + * continuous function object, and a finite element function. The + * result of this function is the vector @p difference that contains + * one value per active cell $K$ of the triangulation. Each of the values + * of this vector $d$ equals + * @f{align*} + * d_K = \| u-u_h \|_X + * @f} + * where $X$ denotes the norm chosen and $u$ represents the exact solution. + * + * It is assumed that the number of components of the function + * @p exact_solution matches that of the finite element used by @p dof. + * + * @param[in] mapping The mapping that is used when integrating the + * difference $u-u_h$. + * @param[in] dof The DoFHandler object that describes the finite + * element space in which the solution vector lives. + * @param[in] fe_function A vector with nodal values representing the + * numerical approximation $u_h$. This vector needs to correspond + * to the finite element space represented by @p dof + * @param[out] difference The vector of values $d_K$ computed as above. + * @param[in] q The quadrature formula used to approximate the integral + * shown above. Note that some quadrature formulas are more useful + * than other in integrating $u-u_h$. For example, it is known that + * the $Q_1$ approximation $u_h$ to the exact solution $u$ of a Laplace + * equation is particularly accurate (in fact, superconvergent, i.e. + * accurate to higher order) at the 4 Gauss points of a cell in 2d + * (or 8 points in 3d) that correspond to a QGauss(2) object. Consequently, + * because a QGauss(2) formula only evaluates the two solutions at these + * particular points, choosing this quadrature formula may indicate an error + * far smaller than it actually is. + * @param[in] norm The norm $X$ shown above that should be computed. + * @param[in] weight The additional argument @p weight allows to evaluate weighted + * norms. The weight function may be scalar, establishing a weight + * in the domain for all components equally. This may be used, for + * instance, to only integrate over parts of the domain. The weight function + * may also be vector-valued, with as many components as the finite element: + * Then, different components get different weights. A typical application is when + * the error with respect to only one or a subset of the solution + * variables is to be computed, in which the other components would + * have weight values equal to zero. The ComponentSelectFunction + * class is particularly useful for this purpose as it provides + * such as "mask" weight.. + * The weight function is expected to be positive, but negative + * values are not filtered. By default, no weighting function is + * given, i.e. weight=1 in the whole domain for all vector + * components uniformly. + * @param[in] exponent This value denotes the $p$ used in computing + * $L^p$-norms and $W^{1,p}$-norms. The value is ignores if a @p norm + * other than NormType::Lp_norm or NormType::W1p_norm is chosen. + * * * See the general documentation of this * class for more information. * - * @note Instantiations for this template + * @note If the integration here happens over the cells of a + * parallel::distribute::Triangulation object, then this function + * computes the vector elements $d_K$ for an output vector with as + * many cells as there are active cells of the triangulation object + * of the current processor. However, not all active cells are in + * fact locally owned: some may be ghost or artificial cells (see + * @ref GlossGhostCell "here" and @ref GlossArtificialCell + * "here"). The vector computed will, in the case of a distributed + * triangulation, contain zeros for cells that are not locally + * owned. As a consequence, in order to compute the global + * $L_2$ error (for example), the errors from different processors + * need to be combined, but this is simple because every processor + * only computes contributions for those cells of the global + * triangulation it locally owns (and these sets are, by definition, + * mutually disjoint). Consequently, the following piece of code + * computes the global $L_2$ error across multiple processors + * sharing a parallel::distribute::Triangulation: + * @code + * Vector local_errors (tria.n_active_cells()); + * VectorTools::integrate_difference (mapping, dof, + * solution, exact_solution, + * local_errors, + * QGauss(fe.degree+2), + * NormType::L2_norm); + * const double total_local_error = local_errors.l2_norm(); + * const double total_global_error + * = std::sqrt (Utilities::MPI::sum (total_local_error * total_local_error, MPI_COMM_WORLD)); + * @endcode + * The squaring and taking the square root is necessary in order to + * compute the sum of squares of norms over all all cells in the definition + * of the $L_2$ norm: + * @f{align*} + * \textrm{error} = \sqrt{\sum_K \|u-u_h\|_{L_2(K)}^2} + * @f} + * Obviously, if you are interested in computing the $L_1$ norm of the + * error, the correct form of the last two lines would have been + * @code + * const double total_local_error = local_errors.l1_norm(); + * const double total_global_error + * = Utilities::MPI::sum (total_local_error, MPI_COMM_WORLD); + * @endcode + * instead, and similar considerations hold when computing the $L_\infty$ + * norm of the error. + * + * Instantiations for this template * are provided for some vector types * (see the general documentation of the * class), but only for InVectors as in @@ -1961,8 +2028,8 @@ namespace VectorTools const double exponent = 2.); /** - * Same as above for hp. - */ + * Same as above for hp. + */ template void integrate_difference (const hp::MappingCollection &mapping, const hp::DoFHandler &dof, diff --git a/deal.II/include/deal.II/numerics/vector_tools.templates.h b/deal.II/include/deal.II/numerics/vector_tools.templates.h index 129cc5f779..e598adf066 100644 --- a/deal.II/include/deal.II/numerics/vector_tools.templates.h +++ b/deal.II/include/deal.II/numerics/vector_tools.templates.h @@ -67,30 +67,6 @@ DEAL_II_NAMESPACE_OPEN namespace VectorTools { - namespace - { - template - void perform_compress_insert (VEC &vec) - { - vec.compress(::dealii::VectorOperation::insert); - } - - template - void perform_compress_insert (::dealii::parallel::distributed::Vector &vec) - { - // should not do compress when inserting - // elements as the ghosts are fixed and some - // will not be set at all. Instead, zero the - // ghost data afterwards to get consistent - // data. Otherwise, at least with the layout - // in r27671 the tests/mpi/step-48/ncpu_10 - // will signal incorrect results because of - // incorrect interpolation. - vec.zero_out_ghosts(); - } - - } - template void interpolate (const Mapping &mapping, @@ -284,7 +260,7 @@ namespace VectorTools = function_values_scalar[fe_index][dof_to_rep_index_table[fe_index][i]]; } } - perform_compress_insert(vec); + vec.compress(VectorOperation::insert); } @@ -557,11 +533,11 @@ namespace VectorTools template - void project (const Mapping &mapping, - const DoFHandler &dof, + void project (const Mapping &mapping, + const DoFHandler &dof, const ConstraintMatrix &constraints, const Quadrature &quadrature, - const Function &function, + const Function &function, VECTOR &vec_result, const bool enforce_zero_boundary, const Quadrature &q_boundary, @@ -607,11 +583,27 @@ namespace VectorTools Vector vec (dof.n_dofs()); SparsityPattern sparsity; + // check if constraints are compatible (see below) + bool constraints_are_compatible = true; + { + for (std::map::iterator it=boundary_values.begin(); + it != boundary_values.end(); ++it) + if (constraints.is_constrained(it->first)) + if (!(constraints.get_constraint_entries(it->first)->size() > 0 + || + (constraints.get_inhomogeneity(it->first) == it->second))) + { + constraints_are_compatible = false; + break; + } + } + // use csp to consume less memory and to // still be fast { CompressedSimpleSparsityPattern csp (dof.n_dofs(), dof.n_dofs()); - DoFTools::make_sparsity_pattern (dof, csp, constraints); + DoFTools::make_sparsity_pattern (dof, csp, constraints, + !constraints_are_compatible); sparsity.copy_from (csp); } @@ -619,52 +611,60 @@ namespace VectorTools SparseMatrix mass_matrix (sparsity); Vector tmp (mass_matrix.n()); - // create mass matrix and rhs at once, - // which is faster. - MatrixCreator::create_mass_matrix (mapping, dof, quadrature, mass_matrix, - function, tmp); - - constraints.condense (mass_matrix); - constraints.condense (tmp); - if (boundary_values.size() != 0) - MatrixTools::apply_boundary_values (boundary_values, - mass_matrix, vec, tmp, - true); - // Allow for a maximum of 5*n - // steps to reduce the residual by - // 10^-12. n steps may not be - // sufficient, since roundoff - // errors may accumulate for badly - // conditioned matrices - ReductionControl control(5*tmp.size(), 0., 1e-12, false, false); + // If the constraint matrix does not conflict with the given boundary + // values (i.e., it either does not contain boundary values or it contains + // the same as boundary_values), we can let it call + // distribute_local_to_global straight away, otherwise we need to first + // interpolate the boundary values and then condense the matrix and vector + if (constraints_are_compatible) + { + const Function* dummy = 0; + MatrixCreator::create_mass_matrix (mapping, dof, quadrature, + mass_matrix, function, tmp, + dummy, constraints); + if (boundary_values.size() > 0) + MatrixTools::apply_boundary_values (boundary_values, + mass_matrix, vec, tmp, + true); + } + else + { + // create mass matrix and rhs at once, which is faster. + MatrixCreator::create_mass_matrix (mapping, dof, quadrature, + mass_matrix, function, tmp); + MatrixTools::apply_boundary_values (boundary_values, + mass_matrix, vec, tmp, + true); + constraints.condense(mass_matrix, tmp); + } + + // Allow for a maximum of 5*n steps to reduce the residual by 10^-12. n + // steps may not be sufficient, since roundoff errors may accumulate for + // badly conditioned matrices + ReductionControl control(5*tmp.size(), 0., 1e-12, false, false); GrowingVectorMemory<> memory; - SolverCG<> cg(control,memory); + SolverCG<> cg(control,memory); PreconditionSSOR<> prec; prec.initialize(mass_matrix, 1.2); - // solve + cg.solve (mass_matrix, vec, tmp, prec); - // distribute solution constraints.distribute (vec); - // copy vec into vec_result. we - // can't use ve_result itself - // above, since it may be of - // another type than Vector - // and that wouldn't necessarily go - // together with the matrix and - // other functions + // copy vec into vec_result. we can't use vec_result itself above, since + // it may be of another type than Vector and that wouldn't + // necessarily go together with the matrix and other functions for (unsigned int i=0; i - void project (const DoFHandler &dof, + void project (const DoFHandler &dof, const ConstraintMatrix &constraints, const Quadrature &quadrature, - const Function &function, + const Function &function, VECTOR &vec, const bool enforce_zero_boundary, const Quadrature &q_boundary, @@ -3063,25 +3063,12 @@ namespace VectorTools { 0, 1 } }; - // The projection is - // divided into two steps. - // In the first step we - // project the boundary - // function on the - // horizontal shape - // functions. Then the - // bounary function is - // projected on the - // vertical shape - // functions. We begin - // with the horizontal - // shape functions and - // set up a linear system - // of equations to get - // the values for degrees - // of freedom associated - // with the interior of - // the face. + // The projection is divided into two steps. In the first step we + // project the boundary function on the horizontal shape functions. + // Then the boundary function is projected on the vertical shape + // functions. We begin with the horizontal shape functions and set + // up a linear system of equations to get the values for degrees of + // freedom associated with the interior of the face. for (unsigned int q_point = 0; q_point < fe_values.n_quadrature_points; ++q_point) { @@ -4269,19 +4256,16 @@ namespace VectorTools std::vector face_dofs; - // create FE and mapping - // collections for all elements in - // use by this DoFHandler + // create FE and mapping collections for all elements in use by this + // DoFHandler hp::FECollection fe_collection (dof_handler.get_fe()); hp::MappingCollection mapping_collection; for (unsigned int i=0; i face_quadrature_collection; for (unsigned int i=0; i (unit_support_points)); } - // now create the object with which - // we will generate the normal - // vectors + // now create the object with which we will generate the normal vectors hp::FEFaceValues x_fe_face_values (mapping_collection, fe_collection, face_quadrature_collection, update_q_points); - // have a map that stores normal - // vectors for each vector-dof - // tuple we want to - // constrain. since we can get at - // the same vector dof tuple more - // than once (for example if it is - // located at a vertex that we - // visit from all adjacent cells), - // we will want to average later on - // the normal vectors computed on - // different cells as described in - // the documentation of this - // function. however, we can only - // average if the contributions - // came from different cells, - // whereas we want to constrain - // twice or more in case the - // contributions came from - // different faces of the same cell - // (i.e. constrain not just the - // *average normal direction* but - // *all normal directions* we - // find). consequently, we also - // have to store which cell a + // have a map that stores normal vectors for each vector-dof tuple we want + // to constrain. since we can get at the same vector dof tuple more than + // once (for example if it is located at a vertex that we visit from all + // adjacent cells), we will want to average later on the normal vectors + // computed on different cells as described in the documentation of this + // function. however, we can only average if the contributions came from + // different cells, whereas we want to constrain twice or more in case the + // contributions came from different faces of the same cell + // (i.e. constrain not just the *average normal direction* but *all normal + // directions* we find). consequently, we also have to store which cell a // normal vector was computed on typedef std::multimap, @@ -4349,24 +4317,20 @@ namespace VectorTools const FiniteElement &fe = cell->get_fe (); typename DH::face_iterator face = cell->face(face_no); - // get the indices of the - // dofs on this cell... + // get the indices of the dofs on this cell... face_dofs.resize (fe.dofs_per_face); face->get_dof_indices (face_dofs, cell->active_fe_index()); x_fe_face_values.reinit (cell, face_no); const FEFaceValues &fe_values = x_fe_face_values.get_present_fe_values(); - // then identify which of - // them correspond to the - // selected set of vector - // components + // then identify which of them correspond to the selected set of + // vector components for (unsigned int i=0; i vector_dofs; vector_dofs.dof_indices[0] = face_dofs[i]; @@ -4389,59 +4353,33 @@ namespace VectorTools Assert (vector_dofs.dof_indices[d] < dof_handler.n_dofs(), ExcInternalError()); - // we need the normal - // vector on this face. we - // know that it is a vector - // of length 1 but at least - // with higher order - // mappings it isn't always - // possible to guarantee - // that each component is - // exact up to zero - // tolerance. in - // particular, as shown in - // the deal.II/no_flux_06 - // test, if we just take - // the normal vector as - // given by the fe_values - // object, we can get - // entries in the normal - // vectors of the unit cube - // that have entries up to + // we need the normal vector on this face. we know that it + // is a vector of length 1 but at least with higher order + // mappings it isn't always possible to guarantee that + // each component is exact up to zero tolerance. in + // particular, as shown in the deal.II/no_flux_06 test, if + // we just take the normal vector as given by the + // fe_values object, we can get entries in the normal + // vectors of the unit cube that have entries up to // several times 1e-14. // - // the problem with this is - // that this later yields - // constraints that are - // circular (e.g., in the - // testcase, we get - // constraints of the form + // the problem with this is that this later yields + // constraints that are circular (e.g., in the testcase, + // we get constraints of the form // // x22 = 2.93099e-14*x21 + 2.93099e-14*x23 // x21 = -2.93099e-14*x22 + 2.93099e-14*x21 // - // in both of these - // constraints, the small - // numbers should be zero - // and the constraints - // should simply be - // x22 = x21 = 0 + // in both of these constraints, the small numbers should + // be zero and the constraints should simply be + // x22 = x21 = 0 // - // to achieve this, we - // utilize that we know - // that the normal vector - // has (or should have) - // length 1 and that we can - // simply set small - // elements to zero - // (without having to check - // that they are small - // *relative to something - // else*). we do this and - // then normalize the - // length of the vector - // back to one, just to be - // on the safe side + // to achieve this, we utilize that we know that the + // normal vector has (or should have) length 1 and that we + // can simply set small elements to zero (without having + // to check that they are small *relative to something + // else*). we do this and then normalize the length of the + // vector back to one, just to be on the safe side Point normal_vector = (cell->face(face_no)->get_boundary() .normal_vector (cell->face(face_no), @@ -4453,9 +4391,8 @@ namespace VectorTools normal_vector[d] = 0; normal_vector /= normal_vector.norm(); - // now enter the - // (dofs,(normal_vector,cell)) - // entry into the map + // now enter the (dofs,(normal_vector,cell)) entry into + // the map dof_to_normals_map .insert (std::make_pair (vector_dofs, std::make_pair (normal_vector, @@ -4463,27 +4400,19 @@ namespace VectorTools } } - // Now do something with the - // collected information. To this - // end, loop through all sets of - // pairs (dofs,normal_vector) and - // identify which entries belong to - // the same set of dofs and then do - // as described in the - // documentation, i.e. either - // average the normal vector or - // don't for this particular set of - // dofs + // Now do something with the collected information. To this end, loop + // through all sets of pairs (dofs,normal_vector) and identify which + // entries belong to the same set of dofs and then do as described in the + // documentation, i.e. either average the normal vector or don't for this + // particular set of dofs typename DoFToNormalsMap::const_iterator p = dof_to_normals_map.begin(); while (p != dof_to_normals_map.end()) { - // first find the range of entries in - // the multimap that corresponds to the - // same vector-dof tuple. as usual, we - // define the range half-open. the - // first entry of course is 'p' + // first find the range of entries in the multimap that corresponds to + // the same vector-dof tuple. as usual, we define the range + // half-open. the first entry of course is 'p' typename DoFToNormalsMap::const_iterator same_dof_range[2] = { p }; for (++p; p != dof_to_normals_map.end(); ++p) @@ -4495,13 +4424,10 @@ namespace VectorTools if (p == dof_to_normals_map.end()) same_dof_range[1] = dof_to_normals_map.end(); - // now compute the reverse mapping: for - // each of the cells that contributed - // to the current set of vector dofs, - // add up the normal vectors. the - // values of the map are pairs of - // normal vectors and number of cells - // that have contributed + // now compute the reverse mapping: for each of the cells that + // contributed to the current set of vector dofs, add up the normal + // vectors. the values of the map are pairs of normal vectors and + // number of cells that have contributed typedef std::map ::active_cell_iterator, @@ -4525,9 +4451,7 @@ namespace VectorTools Assert (old_count > 0, ExcInternalError()); - // in the same entry, - // store again the now - // averaged normal vector + // in the same entry, store again the now averaged normal vector // and the new count cell_to_normals_map[q->second.second] = std::make_pair ((old_normal * old_count + q->second.first) / (old_count + 1), @@ -4536,8 +4460,7 @@ namespace VectorTools Assert (cell_to_normals_map.size() >= 1, ExcInternalError()); - // count the maximum number of - // contributions from each cell + // count the maximum number of contributions from each cell unsigned int max_n_contributions_per_cell = 1; for (typename CellToNormalsMap::const_iterator x = cell_to_normals_map.begin(); @@ -4546,40 +4469,26 @@ namespace VectorTools = std::max (max_n_contributions_per_cell, x->second.second); - // verify that each cell can have only - // contributed at most dim times, since - // that is the maximum number of faces - // that come together at a single place + // verify that each cell can have only contributed at most dim times, + // since that is the maximum number of faces that come together at a + // single place Assert (max_n_contributions_per_cell <= dim, ExcInternalError()); switch (max_n_contributions_per_cell) { - // first deal with the case that a - // number of cells all have - // registered that they have a - // normal vector defined at the - // location of a given vector dof, - // and that each of them have - // encountered this vector dof - // exactly once while looping over - // all their faces. as stated in - // the documentation, this is the - // case where we want to simply - // average over all normal vectors + // first deal with the case that a number of cells all have + // registered that they have a normal vector defined at the + // location of a given vector dof, and that each of them have + // encountered this vector dof exactly once while looping over all + // their faces. as stated in the documentation, this is the case + // where we want to simply average over all normal vectors case 1: { - // compute the average - // normal vector from all - // the ones that have the - // same set of dofs. we - // could add them up and - // divide them by the - // number of additions, - // or simply normalize - // them right away since - // we want them to have - // unit length anyway + // compute the average normal vector from all the ones that have + // the same set of dofs. we could add them up and divide them by + // the number of additions, or simply normalize them right away + // since we want them to have unit length anyway Tensor<1,dim> normal; for (typename CellToNormalsMap::const_iterator x = cell_to_normals_map.begin(); @@ -4593,8 +4502,7 @@ namespace VectorTools normal[d] = 0; normal /= normal.norm(); - // then construct constraints - // from this: + // then construct constraints from this: const internal::VectorDoFTuple & dof_indices = same_dof_range[0]->first; internal::add_constraint (dof_indices, normal, @@ -4604,56 +4512,27 @@ namespace VectorTools } - // this is the slightly - // more complicated case - // that a single cell has - // contributed with exactly - // DIM normal vectors to - // the same set of vector - // dofs. this is what - // happens in a corner in - // 2d and 3d (but not on an - // edge in 3d, where we - // have only 2, i.e. t; @@ -4666,14 +4545,10 @@ namespace VectorTools ExcMessage("Found a set of normal vectors that are nearly collinear.")); } - // so all components of - // this vector dof are - // constrained. enter - // this into the - // constraint matrix + // so all components of this vector dof are constrained. enter + // this into the constraint matrix // - // ignore dofs already - // constrained + // ignore dofs already constrained for (unsigned int i=0; ifirst.dof_indices[i]) @@ -4689,31 +4564,18 @@ namespace VectorTools } - // this is the case of an - // edge contribution in 3d, - // i.e. the vector is - // constrained in two - // directions but not the - // third. + // this is the case of an edge contribution in 3d, i.e. the vector + // is constrained in two directions but not the third. default: { Assert (dim >= 3, ExcNotImplemented()); Assert (max_n_contributions_per_cell == 2, ExcInternalError()); - // as described in the - // documentation, let us - // first collect what - // each of the cells - // contributed at the - // current point. we use - // a std::list instead of - // a std::set (which - // would be more natural) - // because std::set - // requires that the - // stored elements are - // comparable with - // operator< + // as described in the documentation, let us first collect what + // each of the cells contributed at the current point. we use a + // std::list instead of a std::set (which would be more natural) + // because std::set requires that the stored elements are + // comparable with operator< typedef std::map::active_cell_iterator, std::list > > CellContributions; @@ -4725,24 +4587,13 @@ namespace VectorTools cell_contributions[q->second.second].push_back (q->second.first); Assert (cell_contributions.size() >= 1, ExcInternalError()); - // now for each cell that - // has contributed - // determine the number - // of normal vectors it - // has contributed. we - // currently only - // implement if this is - // dim-1 for all cells - // (if a single cell has - // contributed dim, or if - // all adjacent cells - // have contributed 1 - // normal vector, this is - // already handled above) + // now for each cell that has contributed determine the number of + // normal vectors it has contributed. we currently only implement + // if this is dim-1 for all cells (if a single cell has + // contributed dim, or if all adjacent cells have contributed 1 + // normal vector, this is already handled above) // - // for each contributing - // cell compute the - // tangential vector that + // for each contributing cell compute the tangential vector that // remains unconstrained std::list > tangential_vectors; for (typename CellContributions::const_iterator @@ -4763,81 +4614,24 @@ namespace VectorTools Assert (index == dim-1, ExcInternalError()); } - // calculate the - // tangent as the - // outer product of - // the normal - // vectors. since - // these vectors do - // not need to be - // orthogonal (think, - // for example, the - // case of the - // deal.II/no_flux_07 - // test: a sheared - // cube in 3d, with - // Q2 elements, where - // we have - // constraints from - // the two normal - // vectors of two - // faces of the - // sheared cube that - // are not - // perpendicular to - // each other), we - // have to normalize - // the outer product + // calculate the tangent as the outer product of the normal + // vectors. since these vectors do not need to be orthogonal + // (think, for example, the case of the deal.II/no_flux_07 + // test: a sheared cube in 3d, with Q2 elements, where we have + // constraints from the two normal vectors of two faces of the + // sheared cube that are not perpendicular to each other), we + // have to normalize the outer product Tensor<1,dim> tangent; switch (dim) { case 3: - // take - // cross - // product - // between - // normals[0] - // and - // normals[1]. write - // it in - // the - // current - // form - // (with - // [dim-2]) - // to make - // sure - // that - // compilers - // don't - // warn - // about - // out-of-bounds - // accesses - // -- the - // warnings - // are - // bogus - // since we - // get here - // only for - // dim==3, - // but at - // least - // one - // isn't - // quite - // smart - // enough - // to - // notice - // this and - // warns - // when - // compiling - // the - // function - // in 2d + // take cross product between normals[0] and + // normals[1]. write it in the current form (with [dim-2]) + // to make sure that compilers don't warn about + // out-of-bounds accesses -- the warnings are bogus since + // we get here only for dim==3, but at least one isn't + // quite smart enough to notice this and warns when + // compiling the function in 2d cross_product (tangent, normals[0], normals[dim-2]); break; default: @@ -4852,15 +4646,9 @@ namespace VectorTools tangential_vectors.push_back (tangent); } - // go through the list of - // tangents and make sure - // that they all roughly - // point in the same - // direction as the first - // one (i.e. have an - // angle less than 90 - // degrees); if they - // don't then flip their + // go through the list of tangents and make sure that they all + // roughly point in the same direction as the first one (i.e. have + // an angle less than 90 degrees); if they don't then flip their // sign { const Tensor<1,dim> first_tangent = tangential_vectors.front(); @@ -4872,9 +4660,7 @@ namespace VectorTools *t *= -1; } - // now compute the - // average tangent and - // normalize it + // now compute the average tangent and normalize it Tensor<1,dim> average_tangent; for (typename std::list >::const_iterator t = tangential_vectors.begin(); @@ -4883,11 +4669,8 @@ namespace VectorTools average_tangent += *t; average_tangent /= average_tangent.norm(); - // now all that is left - // is that we add the - // constraints that the - // vector is parallel - // to the tangent + // now all that is left is that we add the constraints that the + // vector is parallel to the tangent const internal::VectorDoFTuple & dof_indices = same_dof_range[0]->first; internal::add_tangentiality_constraints (dof_indices, diff --git a/deal.II/source/base/CMakeLists.txt b/deal.II/source/base/CMakeLists.txt index 3eb89bfc66..5269d6c64e 100644 --- a/deal.II/source/base/CMakeLists.txt +++ b/deal.II/source/base/CMakeLists.txt @@ -51,6 +51,7 @@ SET(_src polynomials_adini.cc polynomials_bdm.cc polynomials_nedelec.cc + polynomials_piecewise.cc polynomial_space.cc polynomials_p.cc polynomials_raviart_thomas.cc diff --git a/deal.II/source/base/convergence_table.cc b/deal.II/source/base/convergence_table.cc index eb668c53f0..df3555ff05 100644 --- a/deal.II/source/base/convergence_table.cc +++ b/deal.II/source/base/convergence_table.cc @@ -23,7 +23,8 @@ ConvergenceTable::ConvergenceTable() void ConvergenceTable::evaluate_convergence_rates(const std::string &data_column_key, const std::string &reference_column_key, - const RateMode rate_mode) + const RateMode rate_mode, + const unsigned int dim) { Assert(columns.count(data_column_key), ExcColumnNotExistent(data_column_key)); @@ -39,7 +40,7 @@ void ConvergenceTable::evaluate_convergence_rates(const std::string &data_column std::vector &entries=columns[data_column_key].entries; std::vector &ref_entries=columns[reference_column_key].entries; - std::string rate_key = data_column_key; + std::string rate_key = data_column_key+"..."; const unsigned int n = entries.size(); const unsigned int n_ref = ref_entries.size(); @@ -79,7 +80,7 @@ void ConvergenceTable::evaluate_convergence_rates(const std::string &data_column } break; case reduction_rate_log2: - rate_key += "red.rate"; + rate_key += "red.rate.log2"; no_rate_entries = columns[rate_key].entries.size(); // Calculate all missing rate values: for (unsigned int i = no_rate_entries; i DataOutBase::svg_project_point(Point<3> point, Point<3> camera_position, Point<3> camera_direction, Point<3> camera_horizontal, float camera_focus) +{ + Point<3> camera_vertical; + camera_vertical[0] = camera_horizontal[1] * camera_direction[2] - camera_horizontal[2] * camera_direction[1]; + camera_vertical[1] = camera_horizontal[2] * camera_direction[0] - camera_horizontal[0] * camera_direction[2]; + camera_vertical[2] = camera_horizontal[0] * camera_direction[1] - camera_horizontal[1] * camera_direction[0]; + + float phi; + phi = camera_focus; + phi /= (point[0] - camera_position[0]) * camera_direction[0] + (point[1] - camera_position[1]) * camera_direction[1] + (point[2] - camera_position[2]) * camera_direction[2]; + + Point<3> projection; + projection[0] = camera_position[0] + phi * (point[0] - camera_position[0]); + projection[1] = camera_position[1] + phi * (point[1] - camera_position[1]); + projection[2] = camera_position[2] + phi * (point[2] - camera_position[2]); + + Point<2> projection_decomposition; + projection_decomposition[0] = (projection[0] - camera_position[0] - camera_focus * camera_direction[0]) * camera_horizontal[0]; + projection_decomposition[0] += (projection[1] - camera_position[1] - camera_focus * camera_direction[1]) * camera_horizontal[1]; + projection_decomposition[0] += (projection[2] - camera_position[2] - camera_focus * camera_direction[2]) * camera_horizontal[2]; + + projection_decomposition[1] = (projection[0] - camera_position[0] - camera_focus * camera_direction[0]) * camera_vertical[0]; + projection_decomposition[1] += (projection[1] - camera_position[1] - camera_focus * camera_direction[1]) * camera_vertical[1]; + projection_decomposition[1] += (projection[2] - camera_position[2] - camera_focus * camera_direction[2]) * camera_vertical[2]; + + return projection_decomposition; +} + +Point<6> DataOutBase::svg_get_gradient_parameters(Point<3> points[]) +{ + Point<3> v_min, v_max, v_inter; + + // Use the Bubblesort algorithm to sort the points with respect to the third coordinate + int i, j; + + for (i = 0; i < 2; ++i) + { + for (j = 0; j < 2-i; ++j) + { + if (points[j][2] > points[j + 1][2]) + { + Point<3> temp = points[j]; + points[j] = points[j+1]; + points[j+1] = temp; + } + } + } + + // save the related three-dimensional vectors v_min, v_inter, and v_max + v_min = points[0]; + v_inter = points[1]; + v_max = points[2]; + + Point<2> A[2]; + Point<2> b, gradient; + + // determine the plane offset c + A[0][0] = v_max[0] - v_min[0]; + A[0][1] = v_inter[0] - v_min[0]; + A[1][0] = v_max[1] - v_min[1]; + A[1][1] = v_inter[1] - v_min[1]; + + b[0] = - v_min[0]; + b[1] = - v_min[1]; + + double x, sum; + bool col_change = false; + + if (A[0][0] == 0) + { + col_change = true; + + A[0][0] = A[0][1]; + A[0][1] = 0; + + double temp = A[1][0]; + A[1][0] = A[1][1]; + A[1][1] = temp; + } + + for (unsigned int k = 0; k < 1; k++) + { + for (unsigned int i = k+1; i < 2; i++) + { + x = A[i][k] / A[k][k]; + + for (unsigned int j = k+1; j < 2; j++) A[i][j] = A[i][j] - A[k][j] * x; + + b[i] = b[i] - b[k]*x; + + } + } + + b[1] = b[1] / A[1][1]; + + for (int i = 0; i >= 0; i--) + { + sum = b[i]; + + for (unsigned int j = i+1; j < 2; j++) sum = sum - A[i][j] * b[j]; + + b[i] = sum / A[i][i]; + } + + if (col_change) + { + double temp = b[0]; + b[0] = b[1]; + b[1] = temp; + } + + double c = b[0] * (v_max[2] - v_min[2]) + b[1] * (v_inter[2] - v_min[2]) + v_min[2]; + + // Determine the first entry of the gradient (phi, cf. documentation) + A[0][0] = v_max[0] - v_min[0]; + A[0][1] = v_inter[0] - v_min[0]; + A[1][0] = v_max[1] - v_min[1]; + A[1][1] = v_inter[1] - v_min[1]; + + b[0] = 1.0 - v_min[0]; + b[1] = - v_min[1]; + + col_change = false; + + if (A[0][0] == 0) + { + col_change = true; + + A[0][0] = A[0][1]; + A[0][1] = 0; + + double temp = A[1][0]; + A[1][0] = A[1][1]; + A[1][1] = temp; + } + + for (unsigned int k = 0; k < 1; k++) + { + for (unsigned int i = k+1; i < 2; i++) + { + x = A[i][k] / A[k][k]; + + for (unsigned int j = k+1; j < 2; j++) A[i][j] = A[i][j] - A[k][j] * x; + + b[i] = b[i] - b[k] * x; + + } + } + + b[1]=b[1] / A[1][1]; + + for (int i = 0; i >= 0; i--) + { + sum = b[i]; + + for (unsigned int j = i+1; j < 2; j++) sum = sum - A[i][j]*b[j]; + + b[i] = sum / A[i][i]; + } + + if (col_change) + { + double temp = b[0]; + b[0] = b[1]; + b[1] = temp; + } + + gradient[0] = b[0] * (v_max[2] - v_min[2]) + b[1] * (v_inter[2] - v_min[2]) - c + v_min[2]; + + // determine the second entry of the gradient + A[0][0] = v_max[0] - v_min[0]; + A[0][1] = v_inter[0] - v_min[0]; + A[1][0] = v_max[1] - v_min[1]; + A[1][1] = v_inter[1] - v_min[1]; + + b[0] = - v_min[0]; + b[1] = 1.0 - v_min[1]; + + col_change = false; + + if (A[0][0] == 0) + { + col_change = true; + + A[0][0] = A[0][1]; + A[0][1] = 0; + + double temp = A[1][0]; + A[1][0] = A[1][1]; + A[1][1] = temp; + } + + for (unsigned int k = 0; k < 1; k++) + { + for (unsigned int i = k+1; i < 2; i++) + { + x = A[i][k] / A[k][k]; + + for (unsigned int j = k+1; j < 2; j++) A[i][j] = A[i][j] - A[k][j] * x; + + b[i] = b[i] - b[k] * x; + } + } + + b[1] = b[1] / A[1][1]; + + for (int i = 0; i >= 0; i--) + { + sum = b[i]; + + for (unsigned int j = i+1; j < 2; j++) sum = sum - A[i][j] * b[j]; + + b[i] = sum / A[i][i]; + } + + if (col_change) + { + double temp = b[0]; + b[0] = b[1]; + b[1] = temp; + } + + gradient[1] = b[0] * (v_max[2] - v_min[2]) + b[1] * (v_inter[2] - v_min[2]) - c + v_min[2]; + + // normalize the gradient + double gradient_norm = sqrt(pow(gradient[0], 2.0) + pow(gradient[1], 2.0)); + gradient[0] /= gradient_norm; + gradient[1] /= gradient_norm; + + double lambda = - gradient[0] * (v_min[0] - v_max[0]) - gradient[1] * (v_min[1] - v_max[1]); + + Point<6> gradient_parameters(true); + + gradient_parameters[0] = v_min[0]; + gradient_parameters[1] = v_min[1]; + + gradient_parameters[2] = v_min[0] + lambda * gradient[0]; + gradient_parameters[3] = v_min[1] + lambda * gradient[1]; + + gradient_parameters[4] = v_min[2]; + gradient_parameters[5] = v_max[2]; + + return gradient_parameters; +} + + +bool DataOutBase::SvgCell::operator < (const SvgCell &e) const +{ + // note the "wrong" order in + // which we sort the elements + return depth > e.depth; +} + + + template void DataOutBase::write_ucd (const std::vector > &patches, const std::vector &data_names, @@ -3657,13 +3941,13 @@ void DataOutBase::write_eps (const std::vector > &patches, patch->data.n_rows())); const double color_values[4] = { patch->data.n_rows() != 0 ? - patch->data(flags.color_vector,i1*d1 + i2*d2) : 1, + patch->data(flags.color_vector,i1 *d1 + i2 *d2) : 1, patch->data.n_rows() != 0 ? - patch->data(flags.color_vector,(i1+1)*d1 + i2*d2) : 1, + patch->data(flags.color_vector,(i1+1)*d1 + i2 *d2) : 1, patch->data.n_rows() != 0 ? - patch->data(flags.color_vector,i1*d1 + (i2+1)*d2) : 1, + patch->data(flags.color_vector,i1 *d1 + (i2+1)*d2) : 1, patch->data.n_rows() != 0 ? patch->data(flags.color_vector,(i1+1)*d1 + (i2+1)*d2) : 1 @@ -4648,26 +4932,48 @@ DataOutBase::write_vtk (const std::vector > &patches, /////////////////////// // preamble - if (true) - { - std::time_t time1= std::time (0); - std::tm *time = std::localtime(&time1); - out << "# vtk DataFile Version 3.0" - << '\n' - << "#This file was generated by the deal.II library on " - << time->tm_year+1900 << "/" - << time->tm_mon+1 << "/" - << time->tm_mday << " at " - << time->tm_hour << ":" - << std::setw(2) << time->tm_min << ":" - << std::setw(2) << time->tm_sec - << '\n' - << "ASCII" - << '\n' - << "DATASET UNSTRUCTURED_GRID\n" - << '\n'; - } + { + std::time_t time1= std::time (0); + std::tm *time = std::localtime(&time1); + out << "# vtk DataFile Version 3.0" + << '\n' + << "#This file was generated by the deal.II library on " + << time->tm_year+1900 << "/" + << time->tm_mon+1 << "/" + << time->tm_mday << " at " + << time->tm_hour << ":" + << std::setw(2) << time->tm_min << ":" + << std::setw(2) << time->tm_sec + << '\n' + << "ASCII" + << '\n'; + // now output the data header + out << "DATASET UNSTRUCTURED_GRID\n" + << '\n'; + } + // if desired, output time and cycle of the simulation, following + // the instructions at + // http://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files + { + const unsigned int + n_metadata = ((flags.cycle != std::numeric_limits::min() ? 1 : 0) + + + (flags.time != std::numeric_limits::min() ? 1 : 0)); + if (n_metadata > 0) + out << "FIELD FieldData " << n_metadata << "\n"; + + if (flags.cycle != std::numeric_limits::min()) + { + out << "CYCLE 1 1 int\n" + << flags.cycle << "\n"; + } + if (flags.time != std::numeric_limits::min()) + { + out << "TIME 1 1 double\n" + << flags.time << "\n"; + } + } // first count the number of cells // and cells for later use @@ -4991,6 +5297,37 @@ void DataOutBase::write_vtu_main (const std::vector > &patch } #endif + // first up: metadata + // + // if desired, output time and cycle of the simulation, following + // the instructions at + // http://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files + { + const unsigned int + n_metadata = ((flags.cycle != std::numeric_limits::min() ? 1 : 0) + + + (flags.time != std::numeric_limits::min() ? 1 : 0)); + if (n_metadata > 0) + out << "\n"; + + if (flags.cycle != std::numeric_limits::min()) + { + out << "" + << flags.cycle + << "\n"; + } + if (flags.time != std::numeric_limits::min()) + { + out << "" + << flags.time + << "\n"; + } + + if (n_metadata > 0) + out << "\n"; +} + + VtuStream vtu_out(out, flags); const unsigned int n_data_sets = data_names.size(); @@ -5246,6 +5583,579 @@ void DataOutBase::write_vtu_main (const std::vector > &patch } +template +void DataOutBase::write_svg (const std::vector > &patches, + const std::vector &data_names, + const std::vector > &vector_data_ranges, + const SvgFlags &flags, + std::ostream &out) +{ + // do not allow volume rendering + AssertThrow (dim==2, ExcNotImplemented()); + + const unsigned int height = 4000; + unsigned int width; + + // margin around the plotted area + unsigned int margin_in_percent = 0; + if(flags.margin) margin_in_percent = 5; + + +// determine the bounding box in the model space + double x_min, y_min, z_min; + double x_max, y_max, z_max; + double x_dimension, y_dimension, z_dimension; + + typename std::vector >::const_iterator patch = patches.begin(); + + unsigned int n_subdivisions = patch->n_subdivisions; + unsigned int n = n_subdivisions + 1; + const unsigned int d1 = 1; + const unsigned int d2 = n; + + Point projected_point; + Point projected_points[4]; + + Point<2> projection_decomposition; + Point<2> projection_decompositions[4]; + + compute_node(projected_point, &*patch, 0, 0, 0, n_subdivisions); + + Assert ((flags.height_vector < patch->data.n_rows()) || + patch->data.n_rows() == 0, + ExcIndexRange (flags.height_vector, 0, patch->data.n_rows())); + + x_min = projected_point[0]; + x_max = x_min; + y_min = projected_point[1]; + y_max = y_min; + z_min = patch->data.n_rows() != 0 ? patch->data(flags.height_vector,0) : 0; + z_max = z_min; + + // iterate over the patches + for (; patch != patches.end(); ++patch) + { + n_subdivisions = patch->n_subdivisions; + n = n_subdivisions + 1; + + for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2) + { + for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1) + { + compute_node(projected_points[0], &*patch, i1, i2, 0, n_subdivisions); + compute_node(projected_points[1], &*patch, i1+1, i2, 0, n_subdivisions); + compute_node(projected_points[2], &*patch, i1, i2+1, 0, n_subdivisions); + compute_node(projected_points[3], &*patch, i1+1, i2+1, 0, n_subdivisions); + + x_min = std::min(x_min, (double)projected_points[0][0]); + x_min = std::min(x_min, (double)projected_points[1][0]); + x_min = std::min(x_min, (double)projected_points[2][0]); + x_min = std::min(x_min, (double)projected_points[3][0]); + + x_max = std::max(x_max, (double)projected_points[0][0]); + x_max = std::max(x_max, (double)projected_points[1][0]); + x_max = std::max(x_max, (double)projected_points[2][0]); + x_max = std::max(x_max, (double)projected_points[3][0]); + + y_min = std::min(y_min, (double)projected_points[0][1]); + y_min = std::min(y_min, (double)projected_points[1][1]); + y_min = std::min(y_min, (double)projected_points[2][1]); + y_min = std::min(y_min, (double)projected_points[3][1]); + + y_max = std::max(y_max, (double)projected_points[0][1]); + y_max = std::max(y_max, (double)projected_points[1][1]); + y_max = std::max(y_max, (double)projected_points[2][1]); + y_max = std::max(y_max, (double)projected_points[3][1]); + + Assert ((flags.height_vector < patch->data.n_rows()) || + patch->data.n_rows() == 0, + ExcIndexRange (flags.height_vector, 0, patch->data.n_rows())); + + z_min = std::min(z_min, (double)patch->data(flags.height_vector, i1*d1 + i2*d2)); + z_min = std::min(z_min, (double)patch->data(flags.height_vector, (i1+1)*d1 + i2*d2)); + z_min = std::min(z_min, (double)patch->data(flags.height_vector, i1*d1 + (i2+1)*d2)); + z_min = std::min(z_min, (double)patch->data(flags.height_vector, (i1+1)*d1 + (i2+1)*d2)); + + z_max = std::max(z_max, (double)patch->data(flags.height_vector, i1*d1 + i2*d2)); + z_max = std::max(z_max, (double)patch->data(flags.height_vector, (i1+1)*d1 + i2*d2)); + z_max = std::max(z_max, (double)patch->data(flags.height_vector, i1*d1 + (i2+1)*d2)); + z_max = std::max(z_max, (double)patch->data(flags.height_vector, (i1+1)*d1 + (i2+1)*d2)); + } + } + } + + x_dimension = x_max - x_min; + y_dimension = y_max - y_min; + z_dimension = z_max - z_min; + + +// set initial camera position + Point<3> camera_position(true); + Point<3> camera_direction(true); + Point<3> camera_horizontal(true); + float camera_focus = 0; + + // translate camera from the origin to the initial position + camera_position[0] = 0.; + camera_position[1] = 0.; + camera_position[2] = z_min + 2. * z_dimension; + + camera_direction[0] = 0.; + camera_direction[1] = 0.; + camera_direction[2] = - 1.; + + camera_horizontal[0] = 1.; + camera_horizontal[1] = 0.; + camera_horizontal[2] = 0.; + + camera_focus = .5 * z_dimension; + + Point<3> camera_position_temp(true); + Point<3> camera_direction_temp(true); + Point<3> camera_horizontal_temp(true); + + const float angle_factor = 3.14159265 / 180.; + + // (I) rotate the camera to the chosen polar angle + camera_position_temp[1] = cos(angle_factor * flags.polar_angle) * camera_position[1] - sin(angle_factor * flags.polar_angle) * camera_position[2]; + camera_position_temp[2] = sin(angle_factor * flags.polar_angle) * camera_position[1] + cos(angle_factor * flags.polar_angle) * camera_position[2]; + + camera_direction_temp[1] = cos(angle_factor * flags.polar_angle) * camera_direction[1] - sin(angle_factor * flags.polar_angle) * camera_direction[2]; + camera_direction_temp[2] = sin(angle_factor * flags.polar_angle) * camera_direction[1] + cos(angle_factor * flags.polar_angle) * camera_direction[2]; + + camera_horizontal_temp[1] = cos(angle_factor * flags.polar_angle) * camera_horizontal[1] - sin(angle_factor * flags.polar_angle) * camera_horizontal[2]; + camera_horizontal_temp[2] = sin(angle_factor * flags.polar_angle) * camera_horizontal[1] + cos(angle_factor * flags.polar_angle) * camera_horizontal[2]; + + camera_position[1] = camera_position_temp[1]; + camera_position[2] = camera_position_temp[2]; + + camera_direction[1] = camera_direction_temp[1]; + camera_direction[2] = camera_direction_temp[2]; + + camera_horizontal[1] = camera_horizontal_temp[1]; + camera_horizontal[2] = camera_horizontal_temp[2]; + + // (II) rotate the camera to the chosen azimuth angle + camera_position_temp[0] = cos(angle_factor * flags.azimuth_angle) * camera_position[0] - sin(angle_factor * flags.azimuth_angle) * camera_position[1]; + camera_position_temp[1] = sin(angle_factor * flags.azimuth_angle) * camera_position[0] + cos(angle_factor * flags.azimuth_angle) * camera_position[1]; + + camera_direction_temp[0] = cos(angle_factor * flags.azimuth_angle) * camera_direction[0] - sin(angle_factor * flags.azimuth_angle) * camera_direction[1]; + camera_direction_temp[1] = sin(angle_factor * flags.azimuth_angle) * camera_direction[0] + cos(angle_factor * flags.azimuth_angle) * camera_direction[1]; + + camera_horizontal_temp[0] = cos(angle_factor * flags.azimuth_angle) * camera_horizontal[0] - sin(angle_factor * flags.azimuth_angle) * camera_horizontal[1]; + camera_horizontal_temp[1] = sin(angle_factor * flags.azimuth_angle) * camera_horizontal[0] + cos(angle_factor * flags.azimuth_angle) * camera_horizontal[1]; + + camera_position[0] = camera_position_temp[0]; + camera_position[1] = camera_position_temp[1]; + + camera_direction[0] = camera_direction_temp[0]; + camera_direction[1] = camera_direction_temp[1]; + + camera_horizontal[0] = camera_horizontal_temp[0]; + camera_horizontal[1] = camera_horizontal_temp[1]; + + // (III) translate the camera + camera_position[0] = x_min + .5 * x_dimension; + camera_position[1] = y_min + .5 * y_dimension; + + camera_position[0] += (z_min + 2. * z_dimension) * sin(angle_factor * flags.polar_angle) * sin(angle_factor * flags.azimuth_angle); + camera_position[1] -= (z_min + 2. * z_dimension) * sin(angle_factor * flags.polar_angle) * cos(angle_factor * flags.azimuth_angle); + + +// determine the bounding box on the projection plane + double x_min_perspective, y_min_perspective; + double x_max_perspective, y_max_perspective; + double x_dimension_perspective, y_dimension_perspective; + + patch = patches.begin(); + + n_subdivisions = patch->n_subdivisions; + n = n_subdivisions + 1; + + Point<3> point(true); + + compute_node(projected_point, &*patch, 0, 0, 0, n_subdivisions); + + Assert ((flags.height_vector < patch->data.n_rows()) || + patch->data.n_rows() == 0, + ExcIndexRange (flags.height_vector, 0, patch->data.n_rows())); + + point[0] = projected_point[0]; + point[1] = projected_point[1]; + point[2] = patch->data.n_rows() != 0 ? patch->data(flags.height_vector, 0) : 0; + + projection_decomposition = svg_project_point(point, camera_position, camera_direction, camera_horizontal, camera_focus); + + x_min_perspective = projection_decomposition[0]; + x_max_perspective = projection_decomposition[0]; + y_min_perspective = projection_decomposition[1]; + y_max_perspective = projection_decomposition[1]; + + // iterate over the patches + for (; patch != patches.end(); ++patch) + { + n_subdivisions = patch->n_subdivisions; + n = n_subdivisions + 1; + + for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2) + { + for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1) + { + Point projected_vertices[4]; + Point<3> vertices[4]; + + compute_node(projected_vertices[0], &*patch, i1, i2, 0, n_subdivisions); + compute_node(projected_vertices[1], &*patch, i1+1, i2, 0, n_subdivisions); + compute_node(projected_vertices[2], &*patch, i1, i2+1, 0, n_subdivisions); + compute_node(projected_vertices[3], &*patch, i1+1, i2+1, 0, n_subdivisions); + + Assert ((flags.height_vector < patch->data.n_rows()) || + patch->data.n_rows() == 0, + ExcIndexRange (flags.height_vector, 0, patch->data.n_rows())); + + vertices[0][0] = projected_vertices[0][0]; + vertices[0][1] = projected_vertices[0][1]; + vertices[0][2] = patch->data.n_rows() != 0 ? patch->data(0,i1*d1 + i2*d2) : 0; + + vertices[1][0] = projected_vertices[1][0]; + vertices[1][1] = projected_vertices[1][1]; + vertices[1][2] = patch->data.n_rows() != 0 ? patch->data(0,(i1+1)*d1 + i2*d2) : 0; + + vertices[2][0] = projected_vertices[2][0]; + vertices[2][1] = projected_vertices[2][1]; + vertices[2][2] = patch->data.n_rows() != 0 ? patch->data(0,i1*d1 + (i2+1)*d2) : 0; + + vertices[3][0] = projected_vertices[3][0]; + vertices[3][1] = projected_vertices[3][1]; + vertices[3][2] = patch->data.n_rows() != 0 ? patch->data(0,(i1+1)*d1 + (i2+1)*d2) : 0; + + projection_decompositions[0] = svg_project_point(vertices[0], camera_position, camera_direction, camera_horizontal, camera_focus); + projection_decompositions[1] = svg_project_point(vertices[1], camera_position, camera_direction, camera_horizontal, camera_focus); + projection_decompositions[2] = svg_project_point(vertices[2], camera_position, camera_direction, camera_horizontal, camera_focus); + projection_decompositions[3] = svg_project_point(vertices[3], camera_position, camera_direction, camera_horizontal, camera_focus); + + x_min_perspective = std::min(x_min_perspective, (double)projection_decompositions[0][0]); + x_min_perspective = std::min(x_min_perspective, (double)projection_decompositions[1][0]); + x_min_perspective = std::min(x_min_perspective, (double)projection_decompositions[2][0]); + x_min_perspective = std::min(x_min_perspective, (double)projection_decompositions[3][0]); + + x_max_perspective = std::max(x_max_perspective, (double)projection_decompositions[0][0]); + x_max_perspective = std::max(x_max_perspective, (double)projection_decompositions[1][0]); + x_max_perspective = std::max(x_max_perspective, (double)projection_decompositions[2][0]); + x_max_perspective = std::max(x_max_perspective, (double)projection_decompositions[3][0]); + + y_min_perspective = std::min(y_min_perspective, (double)projection_decompositions[0][1]); + y_min_perspective = std::min(y_min_perspective, (double)projection_decompositions[1][1]); + y_min_perspective = std::min(y_min_perspective, (double)projection_decompositions[2][1]); + y_min_perspective = std::min(y_min_perspective, (double)projection_decompositions[3][1]); + + y_max_perspective = std::max(y_max_perspective, (double)projection_decompositions[0][1]); + y_max_perspective = std::max(y_max_perspective, (double)projection_decompositions[1][1]); + y_max_perspective = std::max(y_max_perspective, (double)projection_decompositions[2][1]); + y_max_perspective = std::max(y_max_perspective, (double)projection_decompositions[3][1]); + } + } + } + + x_dimension_perspective = x_max_perspective - x_min_perspective; + y_dimension_perspective = y_max_perspective - y_min_perspective; + + std::multiset cells; + + // iterate over the patches + for (patch = patches.begin(); patch != patches.end(); ++patch) + { + n_subdivisions = patch->n_subdivisions; + n = n_subdivisions + 1; + + for (unsigned int i2 = 0; i2 < n_subdivisions; ++i2) + { + for (unsigned int i1 = 0; i1 < n_subdivisions; ++i1) + { + Point projected_vertices[4]; + SvgCell cell; + + compute_node(projected_vertices[0], &*patch, i1, i2, 0, n_subdivisions); + compute_node(projected_vertices[1], &*patch, i1+1, i2, 0, n_subdivisions); + compute_node(projected_vertices[2], &*patch, i1, i2+1, 0, n_subdivisions); + compute_node(projected_vertices[3], &*patch, i1+1, i2+1, 0, n_subdivisions); + + Assert ((flags.height_vector < patch->data.n_rows()) || + patch->data.n_rows() == 0, + ExcIndexRange (flags.height_vector, 0, patch->data.n_rows())); + + cell.vertices[0][0] = projected_vertices[0][0]; + cell.vertices[0][1] = projected_vertices[0][1]; + cell.vertices[0][2] = patch->data.n_rows() != 0 ? patch->data(0,i1*d1 + i2*d2) : 0; + + cell.vertices[1][0] = projected_vertices[1][0]; + cell.vertices[1][1] = projected_vertices[1][1]; + cell.vertices[1][2] = patch->data.n_rows() != 0 ? patch->data(0,(i1+1)*d1 + i2*d2) : 0; + + cell.vertices[2][0] = projected_vertices[2][0]; + cell.vertices[2][1] = projected_vertices[2][1]; + cell.vertices[2][2] = patch->data.n_rows() != 0 ? patch->data(0,i1*d1 + (i2+1)*d2) : 0; + + cell.vertices[3][0] = projected_vertices[3][0]; + cell.vertices[3][1] = projected_vertices[3][1]; + cell.vertices[3][2] = patch->data.n_rows() != 0 ? patch->data(0,(i1+1)*d1 + (i2+1)*d2) : 0; + + cell.projected_vertices[0] = svg_project_point(cell.vertices[0], camera_position, camera_direction, camera_horizontal, camera_focus); + cell.projected_vertices[1] = svg_project_point(cell.vertices[1], camera_position, camera_direction, camera_horizontal, camera_focus); + cell.projected_vertices[2] = svg_project_point(cell.vertices[2], camera_position, camera_direction, camera_horizontal, camera_focus); + cell.projected_vertices[3] = svg_project_point(cell.vertices[3], camera_position, camera_direction, camera_horizontal, camera_focus); + + cell.center = .25 * (cell.vertices[0] + cell.vertices[1] + cell.vertices[2] + cell.vertices[3]); + cell.projected_center = svg_project_point(cell.center, camera_position, camera_direction, camera_horizontal, camera_focus); + + cell.depth = cell.center.distance(camera_position); + + cells.insert(cell); + } + } + } + + +// write the svg file + width = static_cast(.5 + height * (x_dimension_perspective / y_dimension_perspective)); + unsigned int additional_width = 0; + + if(flags.draw_colorbar) additional_width = static_cast(.5 + height * .3); // additional width for colorbar + + // basic svg header and background rectangle + out << "" << '\n' + << " " << '\n' << '\n'; + + unsigned int triangle_counter = 0; + + // write the cells in the correct order + for (typename std::multiset::const_iterator cell = cells.begin(); cell != cells.end(); ++cell) + { + Point<3> points3d_triangle[3]; + + for (unsigned int triangle_index = 0; triangle_index < 4; triangle_index++) + { + switch (triangle_index) + { + case 0: points3d_triangle[0] = cell->vertices[0], points3d_triangle[1] = cell->vertices[1], points3d_triangle[2] = cell->center; break; + case 1: points3d_triangle[0] = cell->vertices[1], points3d_triangle[1] = cell->vertices[3], points3d_triangle[2] = cell->center; break; + case 2: points3d_triangle[0] = cell->vertices[3], points3d_triangle[1] = cell->vertices[2], points3d_triangle[2] = cell->center; break; + case 3: points3d_triangle[0] = cell->vertices[2], points3d_triangle[1] = cell->vertices[0], points3d_triangle[2] = cell->center; break; + default: break; + } + + Point<6> gradient_param = svg_get_gradient_parameters(points3d_triangle); + + double start_h = .667 - ((gradient_param[4] - z_min) / z_dimension) * .667; + double stop_h = .667 - ((gradient_param[5] - z_min) / z_dimension) * .667; + + unsigned int start_r = 0; + unsigned int start_g = 0; + unsigned int start_b = 0; + + unsigned int stop_r = 0; + unsigned int stop_g = 0; + unsigned int stop_b = 0; + + unsigned int start_i = static_cast(start_h * 6.); + unsigned int stop_i = static_cast(stop_h * 6.); + + double start_f = start_h * 6. - start_i; + double start_q = 1. - start_f; + + double stop_f = stop_h * 6. - stop_i; + double stop_q = 1. - stop_f; + + switch (start_i % 6) + { + case 0: start_r = 255, start_g = static_cast(.5 + 255. * start_f); break; + case 1: start_r = static_cast(.5 + 255. * start_q), start_g = 255; break; + case 2: start_g = 255, start_b = static_cast(.5 + 255. * start_f); break; + case 3: start_g = static_cast(.5 + 255. * start_q), start_b = 255; break; + case 4: start_r = static_cast(.5 + 255. * start_f), start_b = 255; break; + case 5: start_r = 255, start_b = static_cast(.5 + 255. * start_q); break; + default: break; + } + + switch (stop_i % 6) + { + case 0: stop_r = 255, stop_g = static_cast(.5 + 255. * stop_f); break; + case 1: stop_r = static_cast(.5 + 255. * stop_q), stop_g = 255; break; + case 2: stop_g = 255, stop_b = static_cast(.5 + 255. * stop_f); break; + case 3: stop_g = static_cast(.5 + 255. * stop_q), stop_b = 255; break; + case 4: stop_r = static_cast(.5 + 255. * stop_f), stop_b = 255; break; + case 5: stop_r = 255, stop_b = static_cast(.5 + 255. * stop_q); break; + default: break; + } + + Point<3> gradient_start_point_3d, gradient_stop_point_3d; + + gradient_start_point_3d[0] = gradient_param[0]; + gradient_start_point_3d[1] = gradient_param[1]; + gradient_start_point_3d[2] = gradient_param[4]; + + gradient_stop_point_3d[0] = gradient_param[2]; + gradient_stop_point_3d[1] = gradient_param[3]; + gradient_stop_point_3d[2] = gradient_param[5]; + + Point<2> gradient_start_point = svg_project_point(gradient_start_point_3d, camera_position, camera_direction, camera_horizontal, camera_focus); + Point<2> gradient_stop_point = svg_project_point(gradient_stop_point_3d, camera_position, camera_direction, camera_horizontal, camera_focus); + + // define linear gradient + out << " (.5 + ((gradient_start_point[0] - x_min_perspective) / x_dimension_perspective) * (width - (width/100.) * 2. * margin_in_percent) + ((width/100.) * margin_in_percent)) + << "\" " + << "y1=\"" + << static_cast(.5 + height - (height/100.) * margin_in_percent - ((gradient_start_point[1] - y_min_perspective) / y_dimension_perspective) * (height - (height/100.) * 2. * margin_in_percent)) + << "\" " + << "x2=\"" + << static_cast(.5 + ((gradient_stop_point[0] - x_min_perspective) / x_dimension_perspective) * (width - (width/100.) * 2. * margin_in_percent) + ((width/100.) * margin_in_percent)) + << "\" " + << "y2=\"" + << static_cast(.5 + height - (height/100.) * margin_in_percent - ((gradient_stop_point[1] - y_min_perspective) / y_dimension_perspective) * (height - (height/100.) * 2. * margin_in_percent)) + << "\"" + << ">" << '\n' + << " " << '\n' + << " " << '\n' + << " " << '\n'; + + // draw current triangle + double x1 = 0, y1 = 0, x2 = 0, y2 = 0; + double x3 = cell->projected_center[0]; + double y3 = cell->projected_center[1]; + + switch (triangle_index) + { + case 0: x1 = cell->projected_vertices[0][0], y1 = cell->projected_vertices[0][1], x2 = cell->projected_vertices[1][0], y2 = cell->projected_vertices[1][1]; break; + case 1: x1 = cell->projected_vertices[1][0], y1 = cell->projected_vertices[1][1], x2 = cell->projected_vertices[3][0], y2 = cell->projected_vertices[3][1]; break; + case 2: x1 = cell->projected_vertices[3][0], y1 = cell->projected_vertices[3][1], x2 = cell->projected_vertices[2][0], y2 = cell->projected_vertices[2][1]; break; + case 3: x1 = cell->projected_vertices[2][0], y1 = cell->projected_vertices[2][1], x2 = cell->projected_vertices[0][0], y2 = cell->projected_vertices[0][1]; break; + default: break; + } + + out << " (.5 + ((x1 - x_min_perspective) / x_dimension_perspective) * (width - (width/100.) * 2. * margin_in_percent) + ((width/100.) * margin_in_percent)) + << ' ' + << static_cast(.5 + height - (height/100.) * margin_in_percent - ((y1 - y_min_perspective) / y_dimension_perspective) * (height - (height/100.) * 2. * margin_in_percent)) + << " L " + << static_cast(.5 + ((x2 - x_min_perspective) / x_dimension_perspective) * (width - (width/100.) * 2. * margin_in_percent) + ((width/100.) * margin_in_percent)) + << ' ' + << static_cast(.5 + height - (height/100.) * margin_in_percent - ((y2 - y_min_perspective) / y_dimension_perspective) * (height - (height/100.) * 2. * margin_in_percent)) + << " L " + << static_cast(.5 + ((x3 - x_min_perspective) / x_dimension_perspective) * (width - (width/100.) * 2. * margin_in_percent) + ((width/100.) * margin_in_percent)) + << ' ' + << static_cast(.5 + height - (height/100.) * margin_in_percent - ((y3 - y_min_perspective) / y_dimension_perspective) * (height - (height/100.) * 2. * margin_in_percent)) + << " L " + << static_cast(.5 + ((x1 - x_min_perspective) / x_dimension_perspective) * (width - (width/100.) * 2. * margin_in_percent) + ((width/100.) * margin_in_percent)) + << ' ' + << static_cast(.5 + height - (height/100.) * margin_in_percent - ((y1 - y_min_perspective) / y_dimension_perspective) * (height - (height/100.) * 2. * margin_in_percent)) + << "\" style=\"stroke:black; fill:url(#" << triangle_counter << "); stroke-width:" << flags.line_thickness << "\"/>" << '\n'; + + triangle_counter++; + } + } + + +// draw the colorbar + if (flags.draw_colorbar) + { + out << '\n' << " " << '\n'; + + unsigned int element_height = static_cast(((height/100.) * (71. - 2.*margin_in_percent)) / 4); + unsigned int element_width = static_cast(.5 + (height/100.) * 2.5); + + additional_width = 0; + if (!flags.margin) additional_width = static_cast(.5 + (height/100.) * 2.5); + + for (unsigned int index = 0; index < 4; index++) + { + double start_h = .667 - ((index+1) / 4.) * .667; + double stop_h = .667 - (index / 4.) * .667; + + unsigned int start_r = 0; + unsigned int start_g = 0; + unsigned int start_b = 0; + + unsigned int stop_r = 0; + unsigned int stop_g = 0; + unsigned int stop_b = 0; + + unsigned int start_i = static_cast(start_h * 6.); + unsigned int stop_i = static_cast(stop_h * 6.); + + double start_f = start_h * 6. - start_i; + double start_q = 1. - start_f; + + double stop_f = stop_h * 6. - stop_i; + double stop_q = 1. - stop_f; + + switch (start_i % 6) + { + case 0: start_r = 255, start_g = static_cast(.5 + 255. * start_f); break; + case 1: start_r = static_cast(.5 + 255. * start_q), start_g = 255; break; + case 2: start_g = 255, start_b = static_cast(.5 + 255. * start_f); break; + case 3: start_g = static_cast(.5 + 255. * start_q), start_b = 255; break; + case 4: start_r = static_cast(.5 + 255. * start_f), start_b = 255; break; + case 5: start_r = 255, start_b = static_cast(.5 + 255. * start_q); break; + default: break; + } + + switch (stop_i % 6) + { + case 0: stop_r = 255, stop_g = static_cast(.5 + 255. * stop_f); break; + case 1: stop_r = static_cast(.5 + 255. * stop_q), stop_g = 255; break; + case 2: stop_g = 255, stop_b = static_cast(.5 + 255. * stop_f); break; + case 3: stop_g = static_cast(.5 + 255. * stop_q), stop_b = 255; break; + case 4: stop_r = static_cast(.5 + 255. * stop_f), stop_b = 255; break; + case 5: stop_r = 255, stop_b = static_cast(.5 + 255. * stop_q); break; + default: break; + } + + // define gradient + out << " (.5 + (height/100.) * (margin_in_percent + 29)) + (3-index) * element_height << "\" " + << "x2=\"" << width + additional_width << "\" " + << "y2=\"" << static_cast(.5 + (height/100.) * (margin_in_percent + 29)) + (4-index) * element_height << "\"" + << ">" << '\n' + << " " << '\n' + << " " << '\n' + << " " << '\n'; + + // draw box corresponding to the gradient above + out << " (.5 + (height/100.) * (margin_in_percent + 29)) + (3-index) * element_height + << "\" width=\"" << element_width + << "\" height=\"" << element_height + << "\" style=\"stroke:black; stroke-width:2; fill:url(#colorbar_" << index << ")\"/>" << '\n'; + } + + for (unsigned int index = 0; index < 5; index++) + { + out << " (1.5 * element_width) + << "\" y=\"" << static_cast(.5 + (height/100.) * (margin_in_percent + 29) + (4.-index) * element_height + 30.) << "\"" + << " style=\"text-anchor:start; font-size:80; font-family:Helvetica"; + + if (index == 0 || index == 4) out << "; font-weight:bold"; + + out << "\">" << (float)(((int)((z_min + index * (z_dimension / 4.))*10000))/10000.); + + if (index == 4) out << " max"; + if (index == 0) out << " min"; + + out << "" << '\n'; + } + } + + // finalize the svg file + out << '\n' << ""; + out.flush(); + +} + + template void @@ -5470,6 +6380,14 @@ void DataOutInterface::write_vtu (std::ostream &out) const vtk_flags, out); } +template +void DataOutInterface::write_svg (std::ostream &out) const +{ + DataOutBase::write_svg (get_patches(), get_dataset_names(), + get_vector_data_ranges(), + svg_flags, out); +} + template void DataOutInterface::write_vtu_in_parallel (const char *filename, MPI_Comm comm) const { @@ -5938,7 +6856,7 @@ void DataOutBase::write_hdf5_parallel (const std::vector > & #ifndef DEAL_II_COMPILER_SUPPORTS_MPI // verify that there are indeed // patches to be written out. most - // of the times, people just forget + // of the times, people just forget // to call build_patches when there // are no patches, so a warning is // in order. that said, the @@ -5975,15 +6893,18 @@ void DataOutBase::write_hdf5_parallel (const std::vector > & #endif #endif - if (empty_proc) { - local_node_cell_count[0] = local_node_cell_count[1] = 0; - } else { - compute_sizes(patches, local_node_cell_count[0], local_node_cell_count[1]); + if (empty_proc) + { + local_node_cell_count[0] = local_node_cell_count[1] = 0; + } + else + { + compute_sizes(patches, local_node_cell_count[0], local_node_cell_count[1]); - data_vectors = Table<2,double> (n_data_sets, local_node_cell_count[0]); - void (*fun_ptr) (const std::vector > &, Table<2,double> &) = &DataOutBase::template write_gmv_reorder_data_vectors; - reorder_task = Threads::new_task (fun_ptr, patches, data_vectors); - } + data_vectors = Table<2,double> (n_data_sets, local_node_cell_count[0]); + void (*fun_ptr) (const std::vector > &, Table<2,double> &) = &DataOutBase::template write_gmv_reorder_data_vectors; + reorder_task = Threads::new_task (fun_ptr, patches, data_vectors); + } // Create file access properties plist_id = H5Pcreate(H5P_FILE_ACCESS); @@ -6273,7 +7194,7 @@ DataOutInterface::write (std::ostream &out, break; case eps: - write_eps(out); + write_eps (out); break; case gmv: @@ -6296,6 +7217,10 @@ DataOutInterface::write (std::ostream &out, write_vtu (out); break; + case svg: + write_svg (out); + break; + case deal_II_intermediate: write_deal_II_intermediate (out); break; @@ -6389,6 +7314,15 @@ DataOutInterface::set_flags (const VtkFlags &flags) +template +void +DataOutInterface::set_flags (const SvgFlags &flags) +{ + svg_flags = flags; +} + + + template void DataOutInterface::set_flags (const Deal_II_IntermediateFlags &flags) @@ -6521,6 +7455,7 @@ DataOutInterface::memory_consumption () const MemoryConsumption::memory_consumption (gmv_flags) + MemoryConsumption::memory_consumption (tecplot_flags) + MemoryConsumption::memory_consumption (vtk_flags) + + MemoryConsumption::memory_consumption (svg_flags) + MemoryConsumption::memory_consumption (deal_II_intermediate_flags)); } diff --git a/deal.II/source/base/function.cc b/deal.II/source/base/function.cc index 02b1e29fca..17187a03e4 100644 --- a/deal.II/source/base/function.cc +++ b/deal.II/source/base/function.cc @@ -602,8 +602,6 @@ VectorFunctionFromTensorFunction::VectorFunctionFromTensorFunction (const T // Verify that the Tensor<1,dim> will fit in the given length selected_components // and not hang over the end of the vector. - Assert (0 <= selected_component, - ExcIndexRange (selected_component,0,0)); Assert (selected_component + dim - 1 < this->n_components, ExcIndexRange (selected_component, 0, this->n_components)); } diff --git a/deal.II/source/base/logstream.cc b/deal.II/source/base/logstream.cc index 588f1af187..e0fc350cf5 100644 --- a/deal.II/source/base/logstream.cc +++ b/deal.II/source/base/logstream.cc @@ -168,12 +168,14 @@ LogStream::operator<< (std::ostream& (*p) (std::ostream &)) void -LogStream::attach(std::ostream &o) +LogStream::attach(std::ostream &o, + const bool print_job_id) { Threads::Mutex::ScopedLock lock(log_lock); file = &o; o.setf(std::ios::showpoint | std::ios::left); - o << dealjobid(); + if (print_job_id) + o << dealjobid(); } diff --git a/deal.II/source/base/parameter_handler.cc b/deal.II/source/base/parameter_handler.cc index 2b019ada2c..6268cca617 100644 --- a/deal.II/source/base/parameter_handler.cc +++ b/deal.II/source/base/parameter_handler.cc @@ -1732,12 +1732,10 @@ void ParameterHandler::set (const std::string &entry_string, const bool &new_value) { - std::ostringstream s; - s << new_value; - // hand this off to the function that // actually sets the value as a string - set (entry_string, s.str()); + set (entry_string, + (new_value ? "true" : "false")); } diff --git a/deal.II/source/base/polynomial.cc b/deal.II/source/base/polynomial.cc index 0664ce5e2a..7d399b31c0 100644 --- a/deal.II/source/base/polynomial.cc +++ b/deal.II/source/base/polynomial.cc @@ -102,15 +102,12 @@ namespace Polynomials Assert (values.size() > 0, ExcZero()); const unsigned int values_size=values.size(); - // evaluate Lagrange polynomial and - // derivatives + // evaluate Lagrange polynomial and derivatives if (in_lagrange_product_form == true) { - // to compute the value and all derivatives of - // a polynomial of the form - // (x-x_1)*(x-x_2)*...*(x-x_n), expand the - // derivatives like automatic differentiation - // does. + // to compute the value and all derivatives of a polynomial of the + // form (x-x_1)*(x-x_2)*...*(x-x_n), expand the derivatives like + // automatic differentiation does. const unsigned int n_supp = lagrange_support_points.size(); switch (values_size) { @@ -122,31 +119,24 @@ namespace Polynomials { const number v = x-lagrange_support_points[i]; - // multiply by (x-x_i) and compute action on - // all derivatives, too (inspired from - // automatic differentiation: implement the - // product rule for the old value and the new - // variable 'v', i.e., expand value v and - // derivative one). since we reuse a value - // from the next lower derivative from the - // steps before, need to start from the - // highest derivative + // multiply by (x-x_i) and compute action on all derivatives, + // too (inspired from automatic differentiation: implement the + // product rule for the old value and the new variable 'v', + // i.e., expand value v and derivative one). since we reuse a + // value from the next lower derivative from the steps before, + // need to start from the highest derivative for (unsigned int k=values_size-1; k>0; --k) values[k] = (values[k] * v + values[k-1]); values[0] *= v; } - // finally, multiply by the weight in the - // Lagrange denominator. Could be done instead - // of setting values[0] = 1 above, but that - // gives different accumulation of round-off - // errors (multiplication is not associative) - // compared to when we computed the weight, - // and hence a basis function might not be - // exactly one at the center point, which is - // nice to have. We also multiply derivatives - // by k! to transform the product p_n = - // p^(n)(x)/k! into the actual form of the - // derivative + // finally, multiply by the weight in the Lagrange + // denominator. Could be done instead of setting values[0] = 1 + // above, but that gives different accumulation of round-off + // errors (multiplication is not associative) compared to when we + // computed the weight, and hence a basis function might not be + // exactly one at the center point, which is nice to have. We also + // multiply derivatives by k! to transform the product p_n = + // p^(n)(x)/k! into the actual form of the derivative { number k_faculty = 1; for (unsigned int k=0; k 0, ExcEmptyObject()); - // if we only need the value, then - // call the other function since - // that is significantly faster - // (there is no need to allocate - // and free memory, which is really - // expensive compared to all the - // other operations!) + // if we only need the value, then call the other function since that is + // significantly faster (there is no need to allocate and free memory, + // which is really expensive compared to all the other operations!) if (values_size == 1) { values[0] = value(x); return; }; - // if there are derivatives needed, - // then do it properly by the - // full Horner scheme + // if there are derivatives needed, then do it properly by the full Horner + // scheme const unsigned int m=coefficients.size(); std::vector a(coefficients); unsigned int j_faculty=1; - // loop over all requested - // derivatives. note that - // derivatives @p{j>m} are - // necessarily zero, as they - // differentiate the polynomial - // more often than the highest - // power is + // loop over all requested derivatives. note that derivatives @p{j>m} are + // necessarily zero, as they differentiate the polynomial more often than + // the highest power is const unsigned int min_valuessize_m=std::min(values_size, m); for (unsigned int j=0; j::transform_into_standard_form () { - // should only be called when the product form - // is active + // should only be called when the product form is active Assert (in_lagrange_product_form == true, ExcInternalError()); Assert (coefficients.size() == 0, ExcInternalError()); - // compute coefficients by expanding the - // product (x-x_i) term by term + // compute coefficients by expanding the product (x-x_i) term by term coefficients.resize (lagrange_support_points.size()+1); if (lagrange_support_points.size() == 0) coefficients[0] = 1.; @@ -738,9 +715,8 @@ namespace Polynomials { Assert (coefficients.size() == 0, ExcInternalError()); - // For polynomial order up to 3, we have - // precomputed weights. Use these weights - // instead of the product form + // For polynomial order up to 3, we have precomputed weights. Use these + // weights instead of the product form if (n <= 3) { this->in_lagrange_product_form = false; diff --git a/deal.II/source/base/polynomials_piecewise.cc b/deal.II/source/base/polynomials_piecewise.cc new file mode 100644 index 0000000000..70a259ffa4 --- /dev/null +++ b/deal.II/source/base/polynomials_piecewise.cc @@ -0,0 +1,145 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2010, 2011, 2012 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//--------------------------------------------------------------------------- + + +#include + + +DEAL_II_NAMESPACE_OPEN + + + +namespace Polynomials +{ + + template + PiecewisePolynomial::PiecewisePolynomial (const Polynomial &coefficients_on_interval, + const unsigned int n_intervals, + const unsigned int interval, + const bool spans_next_interval) + : + polynomial (coefficients_on_interval), + n_intervals (n_intervals), + interval (interval), + spans_two_intervals (spans_next_interval) + { + Assert (n_intervals > 0, ExcMessage ("No intervals given")); + AssertIndexRange (interval, n_intervals); + } + + + + template + void + PiecewisePolynomial::value (const number x, + std::vector &values) const + { + Assert (values.size() > 0, ExcZero()); + const unsigned int values_size=values.size(); + + // shift polynomial if necessary + number y = x; + double derivative_change_sign = 1.; + if (n_intervals > 0) + { + const number step = 1./n_intervals; + // polynomial spans over two intervals + if (spans_two_intervals) + { + const double offset = step * interval; + if (xoffset+step+step) + { + for (unsigned int k=0; koffset+step) + { + for (unsigned int k=0; k 0 || + derivative_change_sign == -1.)) + || + (std::abs(y-step)<1e-14 && + (interval < n_intervals-1 || derivative_change_sign == -1.))) + { + values[0] = value(x); + for (unsigned int d=1; d > + generate_complete_Lagrange_basis_on_subdivisions (const unsigned int n_subdivisions, + const unsigned int base_degree) + { + std::vector > p_base = + LagrangeEquidistant::generate_complete_basis(base_degree); + for (unsigned int i=0; i > p; + p.reserve (n_subdivisions * base_degree + 1); + + p.push_back (PiecewisePolynomial (p_base[0], n_subdivisions, 0, + false)); + for (unsigned int s=0; s (p_base[i+1], n_subdivisions, + s, + i==(base_degree-1) && + s; + template class PiecewisePolynomial; + template class PiecewisePolynomial; +} + +DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/source/base/table_handler.cc b/deal.II/source/base/table_handler.cc index 2395c347f4..d9b500718c 100644 --- a/deal.II/source/base/table_handler.cc +++ b/deal.II/source/base/table_handler.cc @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2011, 2012 by the deal.II authors +// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2011, 2012, 2013 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -669,9 +669,9 @@ void TableHandler::get_selected_columns(std::vector &sel_columns) c { // i.e. key is a supercolumn key const unsigned int n_subcolumns=super_iter->second.size(); - for (unsigned int j=0; jsecond[j]; + const std::string subkey=super_iter->second[k]; Assert(columns.count(subkey), ExcInternalError()); sel_columns.push_back(subkey); } diff --git a/deal.II/source/base/tensor_product_polynomials.cc b/deal.II/source/base/tensor_product_polynomials.cc index f57e8d06a8..7a9329fa1a 100644 --- a/deal.II/source/base/tensor_product_polynomials.cc +++ b/deal.II/source/base/tensor_product_polynomials.cc @@ -13,6 +13,7 @@ #include +#include #include #include @@ -22,56 +23,72 @@ DEAL_II_NAMESPACE_OPEN /* ------------------- TensorProductPolynomials -------------- */ -template <> -inline -void -TensorProductPolynomials<1>:: -compute_index (const unsigned int i, - unsigned int (&indices)[1]) const -{ - Assert (i + inline + void compute_tensor_index(const unsigned int, + const unsigned int, + const unsigned int, + unsigned int (&)[dim]) + { + Assert(false, ExcNotImplemented()); + } + inline + void compute_tensor_index(const unsigned int n, + const unsigned int , + const unsigned int , + unsigned int (&indices)[1]) + { + indices[0] = n; + } -template <> -inline -void -TensorProductPolynomials<2>:: -compute_index (const unsigned int i, - unsigned int (&indices)[2]) const -{ - const unsigned int n_pols = polynomials.size(); - Assert (i +template inline void -TensorProductPolynomials<3>:: +TensorProductPolynomials:: compute_index (const unsigned int i, - unsigned int (&indices)[3]) const + unsigned int (&indices)[(dim > 0 ? dim : 1)]) const { - const unsigned int n_pols = polynomials.size(); - Assert (i(polynomials.size()),ExcInternalError()); + internal::compute_tensor_index(index_map[i], polynomials.size(), + polynomials.size(), indices); } -template + +template void -TensorProductPolynomials::output_indices(std::ostream &out) const +TensorProductPolynomials::output_indices(std::ostream &out) const { unsigned int ix[dim]; for (unsigned int i=0; i::output_indices(std::ostream &out) const -template +template void -TensorProductPolynomials::set_numbering( +TensorProductPolynomials::set_numbering( const std::vector &renumber) { Assert(renumber.size()==index_map.size(), @@ -101,11 +118,25 @@ TensorProductPolynomials::set_numbering( -template +template <> +double +TensorProductPolynomials<0,Polynomials::Polynomial > +::compute_value(const unsigned int, + const Point<0> &) const +{ + Assert (false, ExcNotImplemented()); + return 0; +} + + + +template double -TensorProductPolynomials::compute_value (const unsigned int i, - const Point &p) const +TensorProductPolynomials::compute_value (const unsigned int i, + const Point &p) const { + Assert(dim>0, ExcNotImplemented()); + unsigned int indices[dim]; compute_index (i, indices); @@ -118,21 +149,10 @@ TensorProductPolynomials::compute_value (const unsigned int i, -template <> -double -TensorProductPolynomials<0>::compute_value (const unsigned int , - const Point<0> &) const -{ - Assert (false, ExcNotImplemented()); - return 0.; -} - - - -template +template Tensor<1,dim> -TensorProductPolynomials::compute_grad (const unsigned int i, - const Point &p) const +TensorProductPolynomials::compute_grad (const unsigned int i, + const Point &p) const { unsigned int indices[dim]; compute_index (i, indices); @@ -164,10 +184,11 @@ TensorProductPolynomials::compute_grad (const unsigned int i, } -template + +template Tensor<2,dim> -TensorProductPolynomials::compute_grad_grad (const unsigned int i, - const Point &p) const +TensorProductPolynomials::compute_grad_grad (const unsigned int i, + const Point &p) const { unsigned int indices[dim]; compute_index (i, indices); @@ -209,9 +230,9 @@ TensorProductPolynomials::compute_grad_grad (const unsigned int i, -template +template void -TensorProductPolynomials:: +TensorProductPolynomials:: compute (const Point &p, std::vector &values, std::vector > &grads, @@ -308,24 +329,6 @@ compute (const Point &p, -template -unsigned int -TensorProductPolynomials::n() const -{ - return n_tensor_pols; -} - - - -template <> -unsigned int -TensorProductPolynomials<0>::n() const -{ - return numbers::invalid_unsigned_int; -} - - - /* ------------------- AnisotropicPolynomials -------------- */ @@ -347,45 +350,19 @@ AnisotropicPolynomials(const std::vector -void -AnisotropicPolynomials<1>:: -compute_index (const unsigned int i, - unsigned int (&indices)[1]) const -{ - Assert (i -void -AnisotropicPolynomials<2>:: -compute_index (const unsigned int i, - unsigned int (&indices)[2]) const -{ - Assert (i < polynomials[0].size()*polynomials[1].size(), - ExcInternalError()); - - indices[0] = i % polynomials[0].size(); - indices[1] = i / polynomials[0].size(); -} - - - -template <> +template void -AnisotropicPolynomials<3>:: +AnisotropicPolynomials:: compute_index (const unsigned int i, - unsigned int (&indices)[3]) const + unsigned int (&indices)[dim]) const { - Assert (i < polynomials[0].size()*polynomials[1].size()*polynomials[2].size(), - ExcInternalError()); + unsigned int n_poly = 1; + for (unsigned int d=0; d /* ------------------- explicit instantiations -------------- */ -template class TensorProductPolynomials<1>; -template class TensorProductPolynomials<2>; -template class TensorProductPolynomials<3>; +template class TensorProductPolynomials<1,Polynomials::Polynomial >; +template class TensorProductPolynomials<2,Polynomials::Polynomial >; +template class TensorProductPolynomials<3,Polynomials::Polynomial >; + +template class TensorProductPolynomials<1,Polynomials::PiecewisePolynomial >; +template class TensorProductPolynomials<2,Polynomials::PiecewisePolynomial >; +template class TensorProductPolynomials<3,Polynomials::PiecewisePolynomial >; template class AnisotropicPolynomials<1>; template class AnisotropicPolynomials<2>; diff --git a/deal.II/source/base/tensor_product_polynomials_const.cc b/deal.II/source/base/tensor_product_polynomials_const.cc index 7265e869a6..6d6e6876b2 100644 --- a/deal.II/source/base/tensor_product_polynomials_const.cc +++ b/deal.II/source/base/tensor_product_polynomials_const.cc @@ -21,106 +21,25 @@ DEAL_II_NAMESPACE_OPEN /* ------------------- TensorProductPolynomialsConst -------------- */ -template <> -inline -void -TensorProductPolynomialsConst<1>:: -compute_index (const unsigned int i, - unsigned int (&indices)[1]) const -{ - Assert (i -inline -void -TensorProductPolynomialsConst<2>:: -compute_index (const unsigned int i, - unsigned int (&indices)[2]) const -{ - const unsigned int n_pols = polynomials.size(); - Assert (i -inline -void -TensorProductPolynomialsConst<3>:: -compute_index (const unsigned int i, - unsigned int (&indices)[3]) const -{ - const unsigned int n_pols = polynomials.size(); - Assert (i -void -TensorProductPolynomialsConst::output_indices(std::ostream &out) const -{ - unsigned int ix[dim]; - //ouput without constant locally constant function - for (unsigned int i=0; i -void -TensorProductPolynomialsConst::set_numbering( - const std::vector &renumber) -{ - Assert(renumber.size()==index_map.size(), - ExcDimensionMismatch(renumber.size(), index_map.size())); - - index_map=renumber; - for (unsigned int i=0; i double TensorProductPolynomialsConst::compute_value (const unsigned int i, const Point &p) const { - const unsigned int max_indices = Utilities::fixed_power(polynomials.size()); - + const unsigned int max_indices = this->n_tensor_pols; Assert (i<=max_indices, ExcInternalError()); // treat the regular basis functions if (iTensorProductPolynomials::compute_value(i,p); else // this is for the constant function return 1.; } + + template <> double TensorProductPolynomialsConst<0>::compute_value (const unsigned int , @@ -136,42 +55,12 @@ Tensor<1,dim> TensorProductPolynomialsConst::compute_grad (const unsigned int i, const Point &p) const { - const unsigned int max_indices = Utilities::fixed_power(polynomials.size()); - + const unsigned int max_indices = this->n_tensor_pols; Assert (i<=max_indices, ExcInternalError()); // treat the regular basis functions if (i grad; - - unsigned int indices[dim]; - compute_index (i, indices); - - // compute values and - // uni-directional derivatives at - // the given point in each - // co-ordinate direction - double v [dim][2]; - { - std::vector tmp (2); - for (unsigned int d=0; dTensorProductPolynomials::compute_grad(i,p); else // this is for the constant function return Tensor<1,dim>(); @@ -184,48 +73,12 @@ Tensor<2,dim> TensorProductPolynomialsConst::compute_grad_grad (const unsigned int i, const Point &p) const { - const unsigned int max_indices = Utilities::fixed_power(polynomials.size()); - + const unsigned int max_indices = this->n_tensor_pols; Assert (i<=max_indices, ExcInternalError()); // treat the regular basis functions if (i grad_grad; - - unsigned int indices[dim]; - compute_index (i, indices); - - double v [dim][3]; - { - std::vector tmp (3); - for (unsigned int d=0; dTensorProductPolynomials::compute_grad_grad(i,p); else // this is for the constant function return Tensor<2,dim>(); @@ -239,116 +92,44 @@ compute (const Point &p, std::vector > &grads, std::vector > &grad_grads) const { - Assert (values.size()==n_tensor_pols || values.size()==0, - ExcDimensionMismatch2(values.size(), n_tensor_pols, 0)); - Assert (grads.size()==n_tensor_pols || grads.size()==0, - ExcDimensionMismatch2(grads.size(), n_tensor_pols, 0)); - Assert (grad_grads.size()==n_tensor_pols|| grad_grads.size()==0, - ExcDimensionMismatch2(grad_grads.size(), n_tensor_pols, 0)); - - const bool update_values = (values.size() == n_tensor_pols), - update_grads = (grads.size()==n_tensor_pols), - update_grad_grads = (grad_grads.size()==n_tensor_pols); - - // check how many - // values/derivatives we have to - // compute - unsigned int n_values_and_derivatives = 0; - if (update_values) - n_values_and_derivatives = 1; - if (update_grads) - n_values_and_derivatives = 2; - if (update_grad_grads) - n_values_and_derivatives = 3; - - - // compute the values (and derivatives, if - // necessary) of all polynomials at this - // evaluation point. to avoid many - // reallocation, use one std::vector for - // polynomial evaluation and store the - // result as Tensor<1,3> (that has enough - // fields for any evaluation of values and - // derivatives) - Table<2,Tensor<1,3> > v(dim, polynomials.size()+1); - { - std::vector tmp (n_values_and_derivatives); - for (unsigned int d=0; dn_tensor_pols+1 || values.size()==0, + ExcDimensionMismatch2(values.size(), this->n_tensor_pols+1, 0)); + Assert (grads.size()==this->n_tensor_pols+1 || grads.size()==0, + ExcDimensionMismatch2(grads.size(), this->n_tensor_pols+1, 0)); + Assert (grad_grads.size()==this->n_tensor_pols+1 || grad_grads.size()==0, + ExcDimensionMismatch2(grad_grads.size(), this->n_tensor_pols+1, 0)); + + // remove slot for const value, go into the base class compute method and + // finally append the const value again + bool do_values = false, do_grads = false, do_grad_grads = false; + if (values.empty() == false) { - // first get the - // one-dimensional indices of - // this particular tensor - // product polynomial - unsigned int indices[dim]; - compute_index (i, indices); - - if (update_values) - { - values[i] = 1; - for (unsigned int x=0; xTensorProductPolynomials::compute(p, values, grads, grad_grads); -template -unsigned int -TensorProductPolynomialsConst::n() const -{ - return n_tensor_pols; + //for dgq node: values =1, grads=0, grads_grads=0 + if (do_values) + values.push_back(1.); + if (do_grads) + grads.push_back(Tensor<1,dim>()); + if (do_grad_grads) + grad_grads.push_back(Tensor<2,dim>()); } - -template <> -unsigned int -TensorProductPolynomialsConst<0>::n() const -{ - return numbers::invalid_unsigned_int; -} - /* ------------------- explicit instantiations -------------- */ template class TensorProductPolynomialsConst<1>; template class TensorProductPolynomialsConst<2>; diff --git a/deal.II/source/distributed/tria.cc b/deal.II/source/distributed/tria.cc index ebc48f1483..b9c7d52c33 100644 --- a/deal.II/source/distributed/tria.cc +++ b/deal.II/source/distributed/tria.cc @@ -197,6 +197,8 @@ namespace internal static size_t (&connectivity_memory_used) (types<2>::connectivity *p4est); + + static const unsigned max_level; }; int (&functions<2>::quadrant_compare) (const void *v1, const void *v2) @@ -327,6 +329,7 @@ namespace internal size_t (&functions<2>::connectivity_memory_used) (types<2>::connectivity *p4est) = p4est_connectivity_memory_used; + const unsigned int functions<2>::max_level = P4EST_MAXLEVEL; template <> struct functions<3> { @@ -457,6 +460,8 @@ namespace internal static size_t (&connectivity_memory_used) (types<3>::connectivity *p4est); + + static const unsigned max_level; }; @@ -590,6 +595,7 @@ namespace internal size_t (&functions<3>::connectivity_memory_used) (types<3>::connectivity *p4est) = p8est_connectivity_memory_used; + const unsigned int functions<3>::max_level = P8EST_MAXLEVEL; template @@ -2849,6 +2855,18 @@ namespace parallel #endif + // safety check: p4est has an upper limit on the level of a cell + if (this->n_levels()==dealii::internal::p4est::functions::max_level) + { + for (typename Triangulation::active_cell_iterator + cell = this->begin_active(dealii::internal::p4est::functions::max_level-1); + cell != this->end(dealii::internal::p4est::functions::max_level-1); ++cell) + { + AssertThrow(!(cell->refine_flag_set()), + ExcMessage("Fatal Error: maximum refinement level of p4est reached.")); + } + } + // now do the work we're // supposed to do when we are // in charge diff --git a/deal.II/source/dofs/dof_handler_policy.cc b/deal.II/source/dofs/dof_handler_policy.cc index 87fe5c1f73..a45fd8c167 100644 --- a/deal.II/source/dofs/dof_handler_policy.cc +++ b/deal.II/source/dofs/dof_handler_policy.cc @@ -1742,7 +1742,6 @@ namespace internal MPI_Isend(&(*buffer)[0], buffer->size(), MPI_BYTE, it->first, 123, tr->get_communicator(), &requests[idx]); - std::cout << "send ghostdata: " << tr->locally_owned_subdomain() << "->" << it->first << std::endl; } @@ -1797,8 +1796,6 @@ namespace internal MPI_Get_count(&status, MPI_BYTE, &len); receive.resize(len); - std::cout << "receive ghostdata: " << status.MPI_SOURCE << "->" << tr->locally_owned_subdomain() << std::endl; - char *ptr = &receive[0]; MPI_Recv(ptr, len, MPI_BYTE, status.MPI_SOURCE, status.MPI_TAG, tr->get_communicator(), &status); @@ -1851,8 +1848,6 @@ namespace internal unsigned int sent=needs_to_get_cells.size(); unsigned int recv=senders.size(); - std::cout << tr->locally_owned_subdomain() << " " << sent << " " << recv << std::endl; - MPI_Reduce(&sent, &sum_send, 1, MPI_UNSIGNED, MPI_SUM, 0, tr->get_communicator()); MPI_Reduce(&recv, &sum_recv, 1, MPI_UNSIGNED, MPI_SUM, 0, tr->get_communicator()); Assert(sum_send==sum_recv, ExcInternalError()); @@ -2153,7 +2148,6 @@ namespace internal for (unsigned int level = 0; level < n_levels; ++level) { - std::cout << tr->locally_owned_subdomain() << ": on level " << level << std::endl; NumberCache &number_cache = number_caches[level]; //* 1. distribute on own @@ -2177,7 +2171,7 @@ namespace internal endc = dof_handler.end(level); for (; cell != endc; ++cell) - if (!cell->is_artificial() && + if (cell->level_subdomain_id()!=numbers::artificial_subdomain_id && (cell->level_subdomain_id() < tr->locally_owned_subdomain())) { // we found a diff --git a/deal.II/source/dofs/dof_tools.cc b/deal.II/source/dofs/dof_tools.cc index 45a2e0406f..4772293030 100644 --- a/deal.II/source/dofs/dof_tools.cc +++ b/deal.II/source/dofs/dof_tools.cc @@ -2786,7 +2786,147 @@ namespace DoFTools - template + namespace + { + // enter constraints for periodicity into the given ConstraintMatrix object. + // this function is called when at least one of the two face iterators corresponds + // to an active object without further children + // + // @param transformation A matrix that maps degrees of freedom from one face + // to another. If the DoFs on the two faces are supposed to match exactly, then + // the matrix so provided will be the identity matrix. if face 2 is once refined + // from face 1, then the matrix needs to be the interpolation matrix from a face + // to this particular child + // + // @precondition: face_1 is supposed to be active + template + void + set_periodicity_constraints (const FaceIterator &face_1, + const typename identity::type &face_2, + const FullMatrix &transformation, + dealii::ConstraintMatrix &constraint_matrix, + const ComponentMask &component_mask, + const bool face_orientation, + const bool face_flip, + const bool face_rotation) + { + static const int dim = FaceIterator::AccessorType::dimension; + static const int spacedim = FaceIterator::AccessorType::space_dimension; + + // we should be in the case where face_1 is active, i.e. has no children: + Assert (!face_1->has_children(), + ExcInternalError()); + + Assert (face_1->n_active_fe_indices() == 1, + ExcInternalError()); + + // if face_2 does have children, then we need to iterate over them + if (face_2->has_children()) + { + Assert (face_2->n_children() == GeometryInfo::max_children_per_face, + ExcNotImplemented()); + const unsigned int dofs_per_face + = face_1->get_fe(face_1->nth_active_fe_index(0)).dofs_per_face; + FullMatrix child_transformation (dofs_per_face, dofs_per_face); + FullMatrix subface_interpolation (dofs_per_face, dofs_per_face); + for (unsigned int c=0; cn_children(); ++c) + { + // get the interpolation matrix recursively from the one that + // interpolated from face_1 to face_2 by multiplying from the + // left with the one that interpolates from face_2 to + // its child + face_1->get_fe(face_1->nth_active_fe_index(0)) + .get_subface_interpolation_matrix (face_1->get_fe(face_1->nth_active_fe_index(0)), + c, + subface_interpolation); + subface_interpolation.mmult (child_transformation, transformation); + set_periodicity_constraints(face_1, face_2->child(c), + child_transformation, + constraint_matrix, component_mask, + face_orientation, face_flip, face_rotation); + } + } + else + // both faces are active. we need to match the corresponding DoFs of both faces + { + const unsigned int face_1_index = face_1->nth_active_fe_index(0); + const unsigned int face_2_index = face_2->nth_active_fe_index(0); + Assert(face_1->get_fe(face_1_index) == face_2->get_fe(face_1_index), + ExcMessage ("Matching periodic cells need to use the same finite element")); + + const FiniteElement &fe = face_1->get_fe(face_1_index); + + Assert(component_mask.represents_n_components(fe.n_components()), + ExcMessage ("The number of components in the mask has to be either " + "zero or equal to the number of components in the finite " "element.")); + + const unsigned int dofs_per_face = fe.dofs_per_face; + + std::vector dofs_1(dofs_per_face); + std::vector dofs_2(dofs_per_face); + + face_1->get_dof_indices(dofs_1, face_1_index); + face_2->get_dof_indices(dofs_2, face_2_index); + + // Well, this is a hack: + // + // There is no + // face_to_face_index(face_index, + // face_orientation, + // face_flip, + // face_rotation) + // function in FiniteElementData, so we have to use + // face_to_cell_index(face_index, face + // face_orientation, + // face_flip, + // face_rotation) + // But this will give us an index on a cell - something we cannot work + // with directly. But luckily we can match them back :-] + + std::map cell_to_rotated_face_index; + + // Build up a cell to face index for face_2: + for (unsigned int i = 0; i < dofs_per_face; ++i) + { + const unsigned int cell_index = fe.face_to_cell_index(i, 0, /* It doesn't really matter, just assume + * we're on the first face... + */ + true, false, false // default orientation + ); + cell_to_rotated_face_index[cell_index] = i; + } + + // loop over all dofs on face 2 and constrain them again the ones on face 1 + for (unsigned int i=0; i void make_periodicity_constraints (const FaceIterator &face_1, const typename identity::type &face_2, @@ -2882,92 +3022,23 @@ namespace DoFTools face_flip, face_rotation); } - return; } - - // .. otherwise we should be in the case were both faces are active - // and have no children .. - Assert (!face_1->has_children() && !face_2->has_children(), - ExcNotImplemented()); - - Assert (face_1->n_active_fe_indices() == 1 && face_2->n_active_fe_indices() == 1, - ExcInternalError()); - - // .. then we match the corresponding DoFs of both faces .. - const unsigned int face_1_index = face_1->nth_active_fe_index(0); - const unsigned int face_2_index = face_2->nth_active_fe_index(0); - Assert (face_1->get_fe(face_1_index) == face_2->get_fe(face_1_index), - ExcMessage ("Matching periodic cells need to use the same finite element")); - - const dealii::FiniteElement &fe = face_1->get_fe(face_1_index); - - Assert (component_mask.represents_n_components(fe.n_components()), - ExcMessage ("The number of components in the mask has to be either " - "zero or equal to the number of components in the finite " - "element.")); - - const unsigned int dofs_per_face = fe.dofs_per_face; - - std::vector dofs_1(dofs_per_face); - std::vector dofs_2(dofs_per_face); - - face_1->get_dof_indices(dofs_1, face_1_index); - face_2->get_dof_indices(dofs_2, face_2_index); - - - // Well, this is a hack: - // - // There is no - // face_to_face_index(face_index, - // face_orientation, - // face_flip, - // face_rotation) - // function in FiniteElementData, so we have to use - // face_to_cell_index(face_index, face - // face_orientation, - // face_flip, - // face_rotation) - // But this will give us an index on a cell - something we cannot work - // with directly. But luckily we can match them back :-] - - std::map cell_to_rotated_face_index; - - // Build up a cell to face index for face_2: - for (unsigned int i = 0; i < dofs_per_face; ++i) - { - const unsigned int cell_index = - fe.face_to_cell_index(i, - 0, /* It doesn't really matter, just assume - * we're on the first face... - */ - true, false, false // default orientation - ); - cell_to_rotated_face_index[cell_index] = i; - } - - for (unsigned int i = 0; i < dofs_per_face; ++i) + else + // otherwise at least one of the two faces is active and + // we need to enter the constraints { - // Query the correct face_index on face_2 respecting the given - // orientation: - const unsigned int j = - cell_to_rotated_face_index[fe.face_to_cell_index(i, - 0, /* It doesn't really matter, just assume - * we're on the first face... - */ - face_orientation, - face_flip, - face_rotation)]; - - // And finally constrain the two DoFs respecting component_mask: - if (component_mask.n_selected_components(fe.n_components()) == fe.n_components() - || component_mask[fe.face_system_to_component_index(i).first]) - { - if (!constraint_matrix.is_constrained(dofs_1[i])) - { - constraint_matrix.add_line(dofs_1[i]); - constraint_matrix.add_entry(dofs_1[i], dofs_2[j], 1.0); - } - } + if (face_2->has_children() == false) + set_periodicity_constraints(face_2, face_1, + FullMatrix(IdentityMatrix(face_1->get_fe(face_1->nth_active_fe_index(0)).dofs_per_face)), + constraint_matrix, + component_mask, + face_orientation, face_flip, face_rotation); + else + set_periodicity_constraints(face_1, face_2, + FullMatrix(IdentityMatrix(face_1->get_fe(face_1->nth_active_fe_index(0)).dofs_per_face)), + constraint_matrix, + component_mask, + face_orientation, face_flip, face_rotation); } } diff --git a/deal.II/source/fe/CMakeLists.txt b/deal.II/source/fe/CMakeLists.txt index 5ee58d523b..d5d3852aab 100644 --- a/deal.II/source/fe/CMakeLists.txt +++ b/deal.II/source/fe/CMakeLists.txt @@ -36,8 +36,10 @@ SET(_src fe_poly.cc fe_poly_tensor.cc fe_q.cc + fe_q_base.cc fe_q_dg0.cc fe_q_hierarchical.cc + fe_q_iso_q1.cc fe_raviart_thomas.cc fe_raviart_thomas_nodal.cc fe_system.cc @@ -67,9 +69,11 @@ SET(_inst fe_nothing.inst.in fe_poly.inst.in fe_poly_tensor.inst.in + fe_q_base.inst.in fe_q_dg0.inst.in fe_q_hierarchical.inst.in fe_q.inst.in + fe_q_iso_q1.inst.in fe_raviart_thomas.inst.in fe_raviart_thomas_nodal.inst.in fe_system.inst.in diff --git a/deal.II/source/fe/fe.cc b/deal.II/source/fe/fe.cc index c5504e0cb8..1a9e9f9fa4 100644 --- a/deal.II/source/fe/fe.cc +++ b/deal.II/source/fe/fe.cc @@ -180,11 +180,9 @@ FiniteElement::FiniteElement ( ExcInternalError()); }; - // initialize some tables in the - // default way, i.e. if there is - // only one (vector-)component; if - // the element is not primitive, - // leave these tables empty. + // initialize some tables in the default way, i.e. if there is only one + // (vector-)component; if the element is not primitive, leave these tables + // empty. if (this->is_primitive()) { system_to_component_table.resize(this->dofs_per_cell); @@ -200,15 +198,11 @@ FiniteElement::FiniteElement ( face_system_to_base_table[j] = std::make_pair(std::make_pair(0U,0U),j); } } - // Fill with default value; may be - // changed by constructor of - // derived class. + // Fill with default value; may be changed by constructor of derived class. base_to_block_indices.reinit(1,1); - // initialize the restriction and - // prolongation matrices. the default - // contructur of FullMatrix initializes - // them with size zero + // initialize the restriction and prolongation matrices. the default + // contructur of FullMatrix initializes them with size zero prolongation.resize(RefinementCase::isotropic_refinement); restriction.resize(RefinementCase::isotropic_refinement); for (unsigned int ref=RefinementCase::cut_x; @@ -337,11 +331,9 @@ FiniteElement::get_restriction_matrix (const unsigned int child, ExcMessage("Restriction matrices are only available for refined cells!")); Assert (child::n_children(RefinementCase(refinement_case)), ExcIndexRange(child,0,GeometryInfo::n_children(RefinementCase(refinement_case)))); - // we use refinement_case-1 here. the -1 takes care - // of the origin of the vector, as for - // RefinementCase::no_refinement (=0) there is no - // data available and so the vector indices - // are shifted + // we use refinement_case-1 here. the -1 takes care of the origin of the + // vector, as for RefinementCase::no_refinement (=0) there is no data + // available and so the vector indices are shifted Assert (restriction[refinement_case-1][child].n() == this->dofs_per_cell, ExcProjectionVoid()); return restriction[refinement_case-1][child]; } @@ -614,6 +606,8 @@ FiniteElement::prolongation_is_implemented () const for (unsigned int c=0; c::n_children(RefinementCase(ref_case)); ++c) { + // make sure also the lazily initialized matrices are created + get_prolongation_matrix(c, RefinementCase(ref_case)); Assert ((prolongation[ref_case-1][c].m() == this->dofs_per_cell) || (prolongation[ref_case-1][c].m() == 0), ExcInternalError()); @@ -638,6 +632,8 @@ FiniteElement::restriction_is_implemented () const for (unsigned int c=0; c::n_children(RefinementCase(ref_case)); ++c) { + // make sure also the lazily initialized matrices are created + get_restriction_matrix(c, RefinementCase(ref_case)); Assert ((restriction[ref_case-1][c].m() == this->dofs_per_cell) || (restriction[ref_case-1][c].m() == 0), ExcInternalError()); @@ -662,6 +658,8 @@ FiniteElement::isotropic_prolongation_is_implemented () const for (unsigned int c=0; c::n_children(RefinementCase(ref_case)); ++c) { + // make sure also the lazily initialized matrices are created + get_prolongation_matrix(c, RefinementCase(ref_case)); Assert ((prolongation[ref_case-1][c].m() == this->dofs_per_cell) || (prolongation[ref_case-1][c].m() == 0), ExcInternalError()); @@ -686,6 +684,8 @@ FiniteElement::isotropic_restriction_is_implemented () const for (unsigned int c=0; c::n_children(RefinementCase(ref_case)); ++c) { + // make sure also the lazily initialized matrices are created + get_restriction_matrix(c, RefinementCase(ref_case)); Assert ((restriction[ref_case-1][c].m() == this->dofs_per_cell) || (restriction[ref_case-1][c].m() == 0), ExcInternalError()); diff --git a/deal.II/source/fe/fe_poly.cc b/deal.II/source/fe/fe_poly.cc index c2be11407a..2c1ba722ea 100644 --- a/deal.II/source/fe/fe_poly.cc +++ b/deal.II/source/fe/fe_poly.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/deal.II/source/fe/fe_poly.inst.in b/deal.II/source/fe/fe_poly.inst.in index 061f74b81a..50ad8f5f85 100644 --- a/deal.II/source/fe/fe_poly.inst.in +++ b/deal.II/source/fe/fe_poly.inst.in @@ -14,9 +14,10 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS) { -#if deal_II_dimension <= deal_II_space_dimension +#if deal_II_dimension <= deal_II_space_dimension template class FE_Poly, deal_II_dimension, deal_II_space_dimension>; template class FE_Poly, deal_II_dimension, deal_II_space_dimension>; + template class FE_Poly >, deal_II_dimension, deal_II_space_dimension>; template class FE_Poly, deal_II_dimension, deal_II_space_dimension>; template class FE_Poly, deal_II_dimension, deal_II_space_dimension>; #endif diff --git a/deal.II/source/fe/fe_q.cc b/deal.II/source/fe/fe_q.cc index ede4249d59..5e281b948d 100644 --- a/deal.II/source/fe/fe_q.cc +++ b/deal.II/source/fe/fe_q.cc @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 by the deal.II authors +// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -11,13 +11,8 @@ // //--------------------------------------------------------------------------- -#include -#include -#include -#include -#include -#include #include +#include #include #include @@ -25,527 +20,25 @@ DEAL_II_NAMESPACE_OPEN -// namespace for some functions that are used in this file. they are -// specific to numbering conventions used for the FE_Q element, and -// are thus not very interesting to the outside world. we'd like to -// simply put them into an anonymous namespace, but that triggers an -// odd error with icc which can't compile this small snippet if the -// function is static: -// -------------------- -// template struct int2type {}; -// -// namespace { -// static void SYMBOL (const int2type<1> & ) {} -// } -// -// template void g() { -// SYMBOL(int2type()); -// } -// -// template void g<1>(); -// -------------------- -// the function needs to be static because of another icc bug, though. -// work around this by packing everything into a namespace of its own -// and have the anonymous namespace inside -// -// this is now intel icc issue 216082 -namespace FE_Q_Helper -{ - namespace - { - // given a permutation array, - // compute and return the inverse - // permutation - inline - std::vector - invert_numbering (const std::vector &in) - { - std::vector out (in.size()); - for (unsigned int i=0; i - inline - void - zero_indices (unsigned int indices[dim]) - { - for (unsigned int d=0; d - inline - void - increment_indices (unsigned int indices[dim], - const unsigned int dofs1d) - { - ++indices[0]; - for (int d=0; d - inline - std::vector > - generate_poly_space1d (const std::vector > &unit_support_points, - const std::vector &index_map_inverse, - const unsigned int dofs1d) - { - AssertDimension (Utilities::fixed_power (dofs1d), - unit_support_points.size()); - std::vector > points1d (dofs1d); - for (unsigned int i=0; i(unit_support_points[j](0)); - for (unsigned int d=1; d -struct FE_Q::Implementation -{ - /** - * Initialize the hanging node - * constraints matrices. Called from the - * constructor in case the finite element - * is based on quadrature points. - */ - template - static - void initialize_constraints (const Quadrature<1> &, - FE_Q<1,spacedim> &) - { - // no constraints in 1d - } - - - template - static - void initialize_constraints (const Quadrature<1> &points, - FE_Q<2,spacedim> &fe) - { - const unsigned int dim = 2; - - // restricted to each face, the - // traces of the shape functions is - // an element of P_{k} (in 2d), or - // Q_{k} (in 3d), where k is the - // degree of the element. from - // this, we interpolate between - // mother and cell face. - - // the interpolation process works - // as follows: on each subface, - // we want that finite element - // solutions from both sides - // coincide. i.e. if a and b are - // expansion coefficients for the - // shape functions from both sides, - // we seek a relation between a and - // b such that - // sum_j a_j phi^c_j(x) - // == sum_j b_j phi_j(x) - // for all points x on the - // interface. here, phi^c_j are the - // shape functions on the small - // cell on one side of the face, - // and phi_j those on the big cell - // on the other side. To get this - // relation, it suffices to look at - // a sufficient number of points - // for which this has to hold. if - // there are n functions, then we - // need n evaluation points, and we - // choose them equidistantly. - // - // we obtain the matrix system - // A a == B b - // where - // A_ij = phi^c_j(x_i) - // B_ij = phi_j(x_i) - // and the relation we are looking - // for is - // a = A^-1 B b - // - // for the special case of Lagrange - // interpolation polynomials, A_ij - // reduces to delta_ij, and - // a_i = B_ij b_j - // Hence, - // interface_constraints(i,j)=B_ij. - // - // for the general case, where we - // don't have Lagrange - // interpolation polynomials, this - // is a little more - // complicated. Then we would - // evaluate at a number of points - // and invert the interpolation - // matrix A. - // - // Note, that we build up these - // matrices for all subfaces at - // once, rather than considering - // them separately. the reason is - // that we finally will want to - // have them in this order anyway, - // as this is the format we need - // inside deal.II - - // In the following the points x_i - // are constructed in following - // order (n=degree-1) - // *----------*---------* - // 1..n 0 n+1..2n - // i.e. first the midpoint of the - // line, then the support points on - // subface 0 and on subface 1 - std::vector > constraint_points; - // Add midpoint - constraint_points.push_back (Point (0.5)); - - if (fe.degree>1) - { - const unsigned int n=fe.degree-1; - const double step=1./fe.degree; - // subface 0 - for (unsigned int i=1; i<=n; ++i) - constraint_points.push_back ( - GeometryInfo::child_to_cell_coordinates(Point(i*step),0)); - // subface 1 - for (unsigned int i=1; i<=n; ++i) - constraint_points.push_back ( - GeometryInfo::child_to_cell_coordinates(Point(i*step),1)); - } - - // Now construct relation between - // destination (child) and source (mother) - // dofs. - const std::vector > polynomials= - Polynomials::generate_complete_Lagrange_basis(points.get_points()); - - fe.interface_constraints - .TableBase<2,double>::reinit (fe.interface_constraints_size()); - - for (unsigned int i=0; i - static - void initialize_constraints (const Quadrature<1> &points, - FE_Q<3,spacedim> &fe) - { - const unsigned int dim = 3; - - // For a detailed documentation of - // the interpolation see the - // FE_Q<2>::initialize_constraints - // function. - - // In the following the points x_i - // are constructed in the order as - // described in the documentation - // of the FiniteElement class - // (fe_base.h), i.e. - // *--15--4--16--* - // | | | - // 10 19 6 20 12 - // | | | - // 1--7---0--8---2 - // | | | - // 9 17 5 18 11 - // | | | - // *--13--3--14--* - std::vector > constraint_points; - - // Add midpoint - constraint_points.push_back (Point (0.5, 0.5)); - - // Add midpoints of lines of - // "mother-face" - constraint_points.push_back (Point (0, 0.5)); - constraint_points.push_back (Point (1, 0.5)); - constraint_points.push_back (Point (0.5, 0)); - constraint_points.push_back (Point (0.5, 1)); - - if (fe.degree>1) - { - const unsigned int n=fe.degree-1; - const double step=1./fe.degree; - std::vector > line_support_points(n); - for (unsigned int i=0; i qline(line_support_points); - - // auxiliary points in 2d - std::vector > p_line(n); - - // Add nodes of lines interior - // in the "mother-face" - - // line 5: use line 9 - QProjector::project_to_subface(qline, 0, 0, p_line); - for (unsigned int i=0; i (0.5, 0)); - // line 6: use line 10 - QProjector::project_to_subface(qline, 0, 1, p_line); - for (unsigned int i=0; i (0.5, 0)); - // line 7: use line 13 - QProjector::project_to_subface(qline, 2, 0, p_line); - for (unsigned int i=0; i (0, 0.5)); - // line 8: use line 14 - QProjector::project_to_subface(qline, 2, 1, p_line); - for (unsigned int i=0; i (0, 0.5)); - - // DoFs on bordering lines - // lines 9-16 - for (unsigned int face=0; face::faces_per_cell; ++face) - for (unsigned int subface=0; - subface::max_children_per_face; ++subface) - { - QProjector::project_to_subface(qline, face, subface, p_line); - constraint_points.insert(constraint_points.end(), - p_line.begin(), p_line.end()); - } - - // Create constraints for - // interior nodes - std::vector > inner_points(n*n); - for (unsigned int i=0, iy=1; iy<=n; ++iy) - for (unsigned int ix=1; ix<=n; ++ix) - inner_points[i++] = Point (ix*step, iy*step); - - // at the moment do this for - // isotropic face refinement only - for (unsigned int child=0; - child::max_children_per_cell; ++child) - for (unsigned int i=0; i::child_to_cell_coordinates(inner_points[i], child)); - } - - // Now construct relation between - // destination (child) and source (mother) - // dofs. - const unsigned int pnts=(fe.degree+1)*(fe.degree+1); - const std::vector > polynomial_basis= - Polynomials::generate_complete_Lagrange_basis(points.get_points()); - - const TensorProductPolynomials face_polynomials(polynomial_basis); - - fe.interface_constraints - .TableBase<2,double>::reinit (fe.interface_constraints_size()); - - for (unsigned int i=0; i constraint_point; - - // Eliminate FP errors in constraint - // points. Due to their origin, they - // must all be fractions of the unit - // interval. If we have polynomial - // degree 4, the refined element has 8 - // intervals. Hence the coordinates - // must be 0, 0.125, 0.25, 0.375 etc. - // Now the coordinates of the - // constraint points will be multiplied - // by the inverse of the interval size - // (in the example by 8). After that - // the coordinates must be integral - // numbers. Hence a normal truncation - // is performed and the coordinates - // will be scaled back. The equal - // treatment of all coordinates should - // eliminate any FP errors. - for (unsigned int k=0; k (constraint_points[i](k) * interval + 0.25); - constraint_point(k) = 1.*coord_int / interval; - - // The following lines of code - // should eliminate the problems - // with the Constraint-Matrix, - // which appeared for P>=4. The - // Constraint-Matrix class - // complained about different - // constraints for the same entry - // of the Constraint-Matrix. - // Actually this difference could - // be attributed to FP errors, as - // it was in the range of - // 1.0e-16. These errors originate - // in the loss of symmetry in the - // FP approximation of the - // shape-functions. Considering a - // 3rd order shape function in 1D, - // we have N0(x)=N3(1-x) and - // N1(x)=N2(1-x). For higher order - // polynomials the FP - // approximations of the shape - // functions do not satisfy these - // equations any more! Thus in the - // following code everything is - // computed in the interval x \in - // [0..0.5], which is sufficient to - // express all values that could - // come out from a computation of - // any shape function in the full - // interval [0..1]. If x > 0.5 the - // computation is done for 1-x with - // the shape function N_{p-n} - // instead of N_n. Hence symmetry - // is preserved and everything - // works fine... - // - // For a different explanation of - // the problem, see the discussion - // in the FiniteElement class - // for constraint matrices in 3d. - mirror[k] = (constraint_point(k) > 0.5); - if (mirror[k]) - constraint_point(k) = 1.0 - constraint_point(k); - } - - for (unsigned int j=0; j FE_Q::FE_Q (const unsigned int degree) : - FE_Poly, dim, spacedim> ( + FE_Q_Base, dim, spacedim> ( TensorProductPolynomials(Polynomials::LagrangeEquidistant::generate_complete_basis(degree)), - FiniteElementData(get_dpo_vector(degree), + FiniteElementData(this->get_dpo_vector(degree), 1, degree, FiniteElementData::H1), - std::vector (1, false), - std::vector(1, std::vector(1,true))), - face_index_map(FE_Q_Helper::invert_numbering(face_lexicographic_to_hierarchic_numbering (degree))) + std::vector (1, false)) { Assert (degree > 0, ExcMessage ("This element can only be used for polynomial degrees " "greater than zero")); + std::vector > support_points_1d(degree+1); + for (unsigned int i=0; i<=degree; ++i) + support_points_1d[i][0] = static_cast(i)/degree; - std::vector renumber (this->dofs_per_cell); - FETools::hierarchic_to_lexicographic_numbering (*this, renumber); - this->poly_space.set_numbering(renumber); - - // finally fill in support points - // on cell and face - initialize_unit_support_points (); - initialize_unit_face_support_points (); - - // reinit constraints - initialize_constraints (); - - // Reinit the vectors of restriction and - // prolongation matrices to the right sizes - // and compute the matrices - this->reinit_restriction_and_prolongation_matrices(); - initialize_embedding(); - initialize_restriction(); - - initialize_quad_dof_index_permutation(); + this->initialize(support_points_1d); } @@ -553,45 +46,19 @@ FE_Q::FE_Q (const unsigned int degree) template FE_Q::FE_Q (const Quadrature<1> &points) : - FE_Poly, dim, spacedim> ( + FE_Q_Base, dim, spacedim> ( TensorProductPolynomials(Polynomials::generate_complete_Lagrange_basis(points.get_points())), - FiniteElementData(get_dpo_vector(points.size()-1), + FiniteElementData(this->get_dpo_vector(points.size()-1), 1, points.size()-1, FiniteElementData::H1), - std::vector (1, false), - std::vector(1, std::vector(1,true))), - face_index_map(FE_Q_Helper::invert_numbering(face_lexicographic_to_hierarchic_numbering (points.size()-1))) + std::vector (1, false)) { - const int degree = points.size()-1; - + const unsigned int degree = points.size()-1; Assert (degree > 0, ExcMessage ("This element can only be used for polynomial degrees " "at least zero")); - Assert (points.point(0)(0) == 0, - ExcMessage ("The first support point has to be zero.")); - Assert (points.point(degree)(0) == 1, - ExcMessage ("The last support point has to be one.")); - - std::vector renumber (this->dofs_per_cell); - FETools::hierarchic_to_lexicographic_numbering (*this, renumber); - this->poly_space.set_numbering(renumber); - - // finally fill in support points - // on cell and face - initialize_unit_support_points (points); - initialize_unit_face_support_points (points); - // reinit constraints - Implementation::initialize_constraints (points, *this); - - // Reinit the vectors of restriction and - // prolongation matrices to the right sizes - // and compute the matrices - this->reinit_restriction_and_prolongation_matrices(); - initialize_embedding(); - initialize_restriction(); - - initialize_quad_dof_index_permutation(); + this->initialize(points.get_points()); } @@ -600,12 +67,9 @@ template std::string FE_Q::get_name () const { - // note that the - // FETools::get_fe_from_name - // function depends on the - // particular format of the string - // this function returns, so they - // have to be kept in synch + // note that the FETools::get_fe_from_name function depends on the + // particular format of the string this function returns, so they have to be + // kept in synch std::ostringstream namebuf; bool type = true; @@ -615,8 +79,7 @@ FE_Q::get_name () const const std::vector > &unit_support_points = this->unit_support_points; unsigned int index = 0; - // Decode the support points - // in one coordinate direction. + // Decode the support points in one coordinate direction. for (unsigned int j=0; j1) ? (unit_support_points[j](1)==0 && @@ -635,8 +98,7 @@ FE_Q::get_name () const Assert (index == n_points, ExcMessage ("Could not decode support points in one coordinate direction.")); - // Check whether the support - // points are equidistant. + // Check whether the support points are equidistant. for (unsigned int j=0; jdegree) > 1e-15) { @@ -649,8 +111,7 @@ FE_Q::get_name () const else { - // Check whether the support - // points come from QGaussLobatto. + // Check whether the support points come from QGaussLobatto. const QGaussLobatto<1> points_gl(n_points); type = true; for (unsigned int j=0; j::clone() const } - -template -void -FE_Q:: -get_interpolation_matrix (const FiniteElement &x_source_fe, - FullMatrix &interpolation_matrix) const -{ - // this is only implemented, if the - // source FE is also a - // Q element - typedef FE_Q FEQ; - typedef FiniteElement FEL; - - AssertThrow ((x_source_fe.get_name().find ("FE_Q<") == 0) - || - (dynamic_cast(&x_source_fe) != 0), - typename FEL:: - ExcInterpolationNotImplemented()); - - Assert (interpolation_matrix.m() == this->dofs_per_cell, - ExcDimensionMismatch (interpolation_matrix.m(), - this->dofs_per_cell)); - Assert (interpolation_matrix.n() == x_source_fe.dofs_per_cell, - ExcDimensionMismatch (interpolation_matrix.m(), - x_source_fe.dofs_per_cell)); - - // ok, source is a Q element, so - // we will be able to do the work - const FE_Q &source_fe - = dynamic_cast&>(x_source_fe); - - // compute the interpolation - // matrices in much the same way as - // we do for the embedding matrices - // from mother to child. - FullMatrix cell_interpolation (this->dofs_per_cell, - this->dofs_per_cell); - FullMatrix source_interpolation (this->dofs_per_cell, - source_fe.dofs_per_cell); - FullMatrix tmp (this->dofs_per_cell, - source_fe.dofs_per_cell); - for (unsigned int j=0; jdofs_per_cell; ++j) - { - // read in a point on this - // cell and evaluate the - // shape functions there - const Point p = this->unit_support_points[j]; - for (unsigned int i=0; idofs_per_cell; ++i) - cell_interpolation(j,i) = this->poly_space.compute_value (i, p); - - for (unsigned int i=0; idegree*dim; - - // cut off very small values - for (unsigned int i=0; idofs_per_cell; ++i) - for (unsigned int j=0; jdofs_per_cell; ++i) - { - double sum = 0.; - for (unsigned int j=0; j -void -FE_Q<1>:: -get_face_interpolation_matrix (const FiniteElement<1,1> &/*x_source_fe*/, - FullMatrix &/*interpolation_matrix*/) const -{ - Assert (false, ExcImpossibleInDim(1)); -} - - - -template <> -void -FE_Q<1>:: -get_subface_interpolation_matrix (const FiniteElement<1,1> &/*x_source_fe*/, - const unsigned int /*subface*/, - FullMatrix &/*interpolation_matrix*/) const -{ - Assert (false, ExcImpossibleInDim(1)); -} - - -template <> -void -FE_Q<1,2>:: -get_face_interpolation_matrix (const FiniteElement<1,2> &/*x_source_fe*/, - FullMatrix &/*interpolation_matrix*/) const -{ - typedef FiniteElement<1,2> FEL; - Assert (false, - FEL:: - ExcInterpolationNotImplemented ()); -} - - -template <> -void -FE_Q<1,2>:: -get_subface_interpolation_matrix (const FiniteElement<1,2> &/*x_source_fe*/, - const unsigned int /*subface*/, - FullMatrix &/*interpolation_matrix*/) const -{ - typedef FiniteElement<1,2> FEL; - Assert (false, - FEL:: - ExcInterpolationNotImplemented ()); -} - - -template <> -void -FE_Q<1,3>:: -get_face_interpolation_matrix (const FiniteElement<1,3> &/*x_source_fe*/, - FullMatrix &/*interpolation_matrix*/) const -{ - typedef FiniteElement<1,3> FEL; - Assert (false, - FEL:: - ExcInterpolationNotImplemented ()); -} - - -template <> -void -FE_Q<1,3>:: -get_subface_interpolation_matrix (const FiniteElement<1,3> &/*x_source_fe*/, - const unsigned int /*subface*/, - FullMatrix &/*interpolation_matrix*/) const -{ - typedef FiniteElement<1,3> FEL; - Assert (false, - FEL:: - ExcInterpolationNotImplemented ()); -} - - - -template -void -FE_Q:: -get_face_interpolation_matrix (const FiniteElement &x_source_fe, - FullMatrix &interpolation_matrix) const -{ - // this is only implemented, if the - // source FE is also a - // Q element - typedef FE_Q FEQ; - typedef FiniteElement FEL; - AssertThrow ((x_source_fe.get_name().find ("FE_Q<") == 0) - || - (dynamic_cast(&x_source_fe) != 0), - typename FEL:: - ExcInterpolationNotImplemented()); - - Assert (interpolation_matrix.n() == this->dofs_per_face, - ExcDimensionMismatch (interpolation_matrix.n(), - this->dofs_per_face)); - Assert (interpolation_matrix.m() == x_source_fe.dofs_per_face, - ExcDimensionMismatch (interpolation_matrix.m(), - x_source_fe.dofs_per_face)); - - // ok, source is a Q element, so - // we will be able to do the work - const FE_Q &source_fe - = dynamic_cast&>(x_source_fe); - - // Make sure, that the element, - // for which the DoFs should be - // constrained is the one with - // the higher polynomial degree. - // Actually the procedure will work - // also if this assertion is not - // satisfied. But the matrices - // produced in that case might - // lead to problems in the - // hp procedures, which use this - // method. - Assert (this->dofs_per_face <= source_fe.dofs_per_face, - typename FEL:: - ExcInterpolationNotImplemented ()); - - // generate a quadrature - // with the unit support points. - // This is later based as a - // basis for the QProjector, - // which returns the support - // points on the face. - Quadrature quad_face_support (source_fe.get_unit_face_support_points ()); - - // Rule of thumb for FP accuracy, - // that can be expected for a - // given polynomial degree. - // This value is used to cut - // off values close to zero. - const double eps = 2e-13*this->degree*(dim-1); - - // compute the interpolation - // matrix by simply taking the - // value at the support points. -//TODO: Verify that all faces are the same with respect to -// these support points. Furthermore, check if something has to -// be done for the face orientation flag in 3D. - const Quadrature face_quadrature - = QProjector::project_to_face (quad_face_support, 0); - for (unsigned int i=0; i &p = face_quadrature.point (i); - - for (unsigned int j=0; jdofs_per_face; ++j) - { - double matrix_entry = this->shape_value (this->face_to_cell_index(j, 0), p); - - // Correct the interpolated - // value. I.e. if it is close - // to 1 or 0, make it exactly - // 1 or 0. Unfortunately, this - // is required to avoid problems - // with higher order elements. - if (std::fabs (matrix_entry - 1.0) < eps) - matrix_entry = 1.0; - if (std::fabs (matrix_entry) < eps) - matrix_entry = 0.0; - - interpolation_matrix(i,j) = matrix_entry; - } - } - - // make sure that the row sum of - // each of the matrices is 1 at - // this point. this must be so - // since the shape functions sum up - // to 1 - for (unsigned int j=0; jdofs_per_face; ++i) - sum += interpolation_matrix(j,i); - - Assert (std::fabs(sum-1) < 2e-13*this->degree*(dim-1), - ExcInternalError()); - } -} - - - -template -void -FE_Q:: -get_subface_interpolation_matrix (const FiniteElement &x_source_fe, - const unsigned int subface, - FullMatrix &interpolation_matrix) const -{ - Assert (interpolation_matrix.m() == x_source_fe.dofs_per_face, - ExcDimensionMismatch (interpolation_matrix.m(), - x_source_fe.dofs_per_face)); - - // see if source is a Q element - if (const FE_Q *source_fe - = dynamic_cast *>(&x_source_fe)) - { - // have this test in here since - // a table of size 2x0 reports - // its size as 0x0 - Assert (interpolation_matrix.n() == this->dofs_per_face, - ExcDimensionMismatch (interpolation_matrix.n(), - this->dofs_per_face)); - - // Make sure, that the element, - // for which the DoFs should be - // constrained is the one with - // the higher polynomial degree. - // Actually the procedure will work - // also if this assertion is not - // satisfied. But the matrices - // produced in that case might - // lead to problems in the - // hp procedures, which use this - // method. - Assert (this->dofs_per_face <= source_fe->dofs_per_face, - (typename FiniteElement:: - ExcInterpolationNotImplemented ())); - - // generate a point on this - // cell and evaluate the - // shape functions there - const Quadrature - quad_face_support (source_fe->get_unit_face_support_points ()); - - // Rule of thumb for FP accuracy, - // that can be expected for a - // given polynomial degree. - // This value is used to cut - // off values close to zero. - double eps = 2e-13*this->degree*(dim-1); - - // compute the interpolation - // matrix by simply taking the - // value at the support points. -//TODO: Verify that all faces are the same with respect to -// these support points. Furthermore, check if something has to -// be done for the face orientation flag in 3D. - const Quadrature subface_quadrature - = QProjector::project_to_subface (quad_face_support, 0, subface); - for (unsigned int i=0; idofs_per_face; ++i) - { - const Point &p = subface_quadrature.point (i); - - for (unsigned int j=0; jdofs_per_face; ++j) - { - double matrix_entry = this->shape_value (this->face_to_cell_index(j, 0), p); - - // Correct the interpolated - // value. I.e. if it is close - // to 1 or 0, make it exactly - // 1 or 0. Unfortunately, this - // is required to avoid problems - // with higher order elements. - if (std::fabs (matrix_entry - 1.0) < eps) - matrix_entry = 1.0; - if (std::fabs (matrix_entry) < eps) - matrix_entry = 0.0; - - interpolation_matrix(i,j) = matrix_entry; - } - } - - // make sure that the row sum of - // each of the matrices is 1 at - // this point. this must be so - // since the shape functions sum up - // to 1 - for (unsigned int j=0; jdofs_per_face; ++j) - { - double sum = 0.; - - for (unsigned int i=0; idofs_per_face; ++i) - sum += interpolation_matrix(j,i); - - Assert (std::fabs(sum-1) < 2e-13*this->degree*dim, - ExcInternalError()); - } - } - else if (dynamic_cast *>(&x_source_fe) != 0) - { - // nothing to do here, the - // FE_Nothing has no degrees of - // freedom anyway - } - else - AssertThrow (false, - (typename FiniteElement:: - ExcInterpolationNotImplemented())); -} - - - -template -bool -FE_Q::hp_constraints_are_implemented () const -{ - return true; -} - - - - -template -std::vector > -FE_Q:: -hp_vertex_dof_identities (const FiniteElement &fe_other) const -{ - // we can presently only compute - // these identities if both FEs are - // FE_Qs or if the other one is an - // FE_Nothing. in the first case, - // there should be exactly one - // single DoF of each FE at a - // vertex, and they should have - // identical value - if (dynamic_cast*>(&fe_other) != 0) - { - return - std::vector > - (1, std::make_pair (0U, 0U)); - } - else if (dynamic_cast*>(&fe_other) != 0) - { - // the FE_Nothing has no - // degrees of freedom, so there - // are no equivalencies to be - // recorded - return std::vector > (); - } - else - { - Assert (false, ExcNotImplemented()); - return std::vector > (); - } -} - - - -template -std::vector > -FE_Q:: -hp_line_dof_identities (const FiniteElement &fe_other) const -{ - // we can presently only compute - // these identities if both FEs are - // FE_Qs or if the other one is an - // FE_Nothing - if (const FE_Q *fe_q_other = dynamic_cast*>(&fe_other)) - { - // dofs are located along lines, so two - // dofs are identical if they are - // located at identical positions. if - // we had only equidistant points, we - // could simple check for similarity - // like (i+1)*q == (j+1)*p, but we - // might have other support points - // (e.g. Gauss-Lobatto - // points). Therefore, read the points - // in unit_support_points for the first - // coordinate direction. We take the - // lexicographic ordering of the points - // in the first direction (i.e., - // x-direction), which we access - // between index 1 and p-1 (index 0 and - // p are vertex dofs). - const unsigned int p = this->degree; - const unsigned int q = fe_q_other->degree; - - std::vector > identities; - - const std::vector &index_map_inverse= - this->poly_space.get_numbering_inverse(); - const std::vector &index_map_inverse_other= - fe_q_other->poly_space.get_numbering_inverse(); - - for (unsigned int i=0; iunit_support_points[index_map_inverse[i+1]][0]- - fe_q_other->unit_support_points[index_map_inverse_other[j+1]][0]) - < 1e-14) - identities.push_back (std::make_pair(i,j)); - - return identities; - } - else if (dynamic_cast*>(&fe_other) != 0) - { - // the FE_Nothing has no - // degrees of freedom, so there - // are no equivalencies to be - // recorded - return std::vector > (); - } - else - { - Assert (false, ExcNotImplemented()); - return std::vector > (); - } -} - - - -template -std::vector > -FE_Q:: -hp_quad_dof_identities (const FiniteElement &fe_other) const -{ - // we can presently only compute - // these identities if both FEs are - // FE_Qs or if the other one is an - // FE_Nothing - if (const FE_Q *fe_q_other = dynamic_cast*>(&fe_other)) - { - // this works exactly like the line - // case above, except that now we have - // to have two indices i1, i2 and j1, - // j2 to characterize the dofs on the - // face of each of the finite - // elements. since they are ordered - // lexicographically along the first - // line and we have a tensor product, - // the rest is rather straightforward - const unsigned int p = this->degree; - const unsigned int q = fe_q_other->degree; - - std::vector > identities; - - const std::vector &index_map_inverse= - this->poly_space.get_numbering_inverse(); - const std::vector &index_map_inverse_other= - fe_q_other->poly_space.get_numbering_inverse(); - - for (unsigned int i1=0; i1unit_support_points[index_map_inverse[i1+1]][0]- - fe_q_other->unit_support_points[index_map_inverse_other[j1+1]][0]) - < 1e-14) - && - (std::fabs(this->unit_support_points[index_map_inverse[i2+1]][0]- - fe_q_other->unit_support_points[index_map_inverse_other[j2+1]][0]) - < 1e-14)) - identities.push_back (std::make_pair(i1*(p-1)+i2, - j1*(q-1)+j2)); - - return identities; - } - else if (dynamic_cast*>(&fe_other) != 0) - { - // the FE_Nothing has no - // degrees of freedom, so there - // are no equivalencies to be - // recorded - return std::vector > (); - } - else - { - Assert (false, ExcNotImplemented()); - return std::vector > (); - } -} - - - -template -FiniteElementDomination::Domination -FE_Q:: -compare_for_face_domination (const FiniteElement &fe_other) const -{ - if (const FE_Q *fe_q_other - = dynamic_cast*>(&fe_other)) - { - if (this->degree < fe_q_other->degree) - return FiniteElementDomination::this_element_dominates; - else if (this->degree == fe_q_other->degree) - return FiniteElementDomination::either_element_can_dominate; - else - return FiniteElementDomination::other_element_dominates; - } - else if (dynamic_cast*>(&fe_other) != 0) - { - // the FE_Nothing has no - // degrees of freedom and it is - // typically used in a context - // where we don't require any - // continuity along the - // interface - return FiniteElementDomination::no_requirements; - } - - Assert (false, ExcNotImplemented()); - return FiniteElementDomination::neither_element_dominates; -} - - -//--------------------------------------------------------------------------- -// Auxiliary functions -//--------------------------------------------------------------------------- - - - -template -void FE_Q::initialize_unit_support_points () -{ - // number of points: (degree+1)^dim - unsigned int n = this->degree+1; - for (unsigned int i=1; idegree+1; - - this->unit_support_points.resize(n); - - const std::vector &index_map_inverse= - this->poly_space.get_numbering_inverse(); - - const double step = 1./this->degree; - Point p; - - unsigned int k=0; - for (unsigned int iz=0; iz <= ((dim>2) ? this->degree : 0) ; ++iz) - for (unsigned int iy=0; iy <= ((dim>1) ? this->degree : 0) ; ++iy) - for (unsigned int ix=0; ix<=this->degree; ++ix) - { - p(0) = ix * step; - if (dim>1) - p(1) = iy * step; - if (dim>2) - p(2) = iz * step; - - this->unit_support_points[index_map_inverse[k++]] = p; - } -} - - - -template -void FE_Q::initialize_unit_support_points (const Quadrature<1> &points) -{ - // number of points: (degree+1)^dim - unsigned int n = this->degree+1; - for (unsigned int i=1; idegree+1; - - this->unit_support_points.resize(n); - - const std::vector &index_map_inverse= - this->poly_space.get_numbering_inverse(); - - Quadrature support_quadrature(points); - - Point p; - - for (unsigned int k=0; kunit_support_points[index_map_inverse[k]] = support_quadrature.point(k); - } -} - - - -template <> -void FE_Q<1>::initialize_unit_face_support_points () -{ - // no faces in 1d, so nothing to do -} - -template <> -void FE_Q<1>::initialize_unit_face_support_points (const Quadrature<1> &/*points*/) -{ - // no faces in 1d, so nothing to do -} - -template <> -void FE_Q<1,2>::initialize_unit_face_support_points () -{ - // no faces in 1d, so nothing to do -} - -template <> -void FE_Q<1,2>::initialize_unit_face_support_points (const Quadrature<1> &/*points*/) -{ - // no faces in 1d, so nothing to do -} - -template <> -void FE_Q<1,3>::initialize_unit_face_support_points () -{ - // no faces in 1d, so nothing to do -} - -template <> -void FE_Q<1,3>::initialize_unit_face_support_points (const Quadrature<1> &/*points*/) -{ - // no faces in 1d, so nothing to do -} - -template -void FE_Q::initialize_unit_face_support_points () -{ - const unsigned int codim = dim-1; - this->unit_face_support_points.resize(Utilities::fixed_power(this->degree+1)); - - const std::vector &face_index_map_inverse= - FE_Q_Helper::invert_numbering(face_index_map); - - const double step = 1./this->degree; - Point p; - - unsigned int k=0; - for (unsigned int iz=0; iz <= ((codim>2) ? this->degree : 0) ; ++iz) - for (unsigned int iy=0; iy <= ((codim>1) ? this->degree : 0) ; ++iy) - for (unsigned int ix=0; ix<=this->degree; ++ix) - { - p(0) = ix * step; - if (codim>1) - p(1) = iy * step; - if (codim>2) - p(2) = iz * step; - - this->unit_face_support_points[face_index_map_inverse[k++]] = p; - } -} - - - -template -void FE_Q::initialize_unit_face_support_points (const Quadrature<1> &points) -{ - const unsigned int codim = dim-1; - this->unit_face_support_points.resize(Utilities::fixed_power(this->degree+1)); - - const std::vector< Point<1> > &edge = points.get_points(); - - const std::vector &face_index_map_inverse= - FE_Q_Helper::invert_numbering(face_index_map); - - Point p; - - unsigned int k=0; - for (unsigned int iz=0; iz <= ((codim>2) ? this->degree : 0) ; ++iz) - for (unsigned int iy=0; iy <= ((codim>1) ? this->degree : 0) ; ++iy) - for (unsigned int ix=0; ix<=this->degree; ++ix) - { - p(0) = edge[ix](0); - if (codim>1) - p(1) = edge[iy](0); - if (codim>2) - p(2) = edge[iz](0); - - this->unit_face_support_points[face_index_map_inverse[k++]] = p; - } -} - - - -template -void -FE_Q::initialize_quad_dof_index_permutation () -{ - // general template for 1D and 2D, do nothing -} - - - -template <> -void -FE_Q<3>::initialize_quad_dof_index_permutation () -{ - Assert (adjust_quad_dof_index_for_face_orientation_table.n_elements()==8*this->dofs_per_quad, - ExcInternalError()); - - const unsigned int n=this->degree-1; - Assert(n*n==this->dofs_per_quad, ExcInternalError()); - - // alias for the table to fill - Table<2,int> &data=this->adjust_quad_dof_index_for_face_orientation_table; - - // the dofs on a face are connected to a n x - // n matrix. for example, for degree==4 we - // have the following dofs on a quad - - // ___________ - // | | - // | 6 7 8 | - // | | - // | 3 4 5 | - // | | - // | 0 1 2 | - // |___________| - // - // we have dof_no=i+n*j with index i in - // x-direction and index j in y-direction - // running from 0 to n-1. to extract i and j - // we can use i=dof_no%n and j=dof_no/n. i - // and j can then be used to construct the - // rotated and mirrored numbers. - - - for (unsigned int local=0; localdofs_per_quad; ++local) - // face support points are in lexicographic - // ordering with x running fastest. invert - // that (y running fastest) - { - unsigned int i=local%n, - j=local/n; - - // face_orientation=false, face_flip=false, face_rotation=false - data(local,0)=j + i *n - local; - // face_orientation=false, face_flip=false, face_rotation=true - data(local,1)=i + (n-1-j)*n - local; - // face_orientation=false, face_flip=true, face_rotation=false - data(local,2)=(n-1-j) + (n-1-i)*n - local; - // face_orientation=false, face_flip=true, face_rotation=true - data(local,3)=(n-1-i) + j *n - local; - // face_orientation=true, face_flip=false, face_rotation=false - data(local,4)=0; - // face_orientation=true, face_flip=false, face_rotation=true - data(local,5)=j + (n-1-i)*n - local; - // face_orientation=true, face_flip=true, face_rotation=false - data(local,6)=(n-1-i) + (n-1-j)*n - local; - // face_orientation=true, face_flip=true, face_rotation=true - data(local,7)=(n-1-j) + i *n - local; - } - - // aditionally initialize reordering of line - // dofs - for (unsigned int i=0; idofs_per_line; ++i) - this->adjust_line_dof_index_for_line_orientation_table[i]=this->dofs_per_line-1-i - i; -} - - - - -template -std::vector -FE_Q::get_dpo_vector(const unsigned int deg) -{ - std::vector dpo(dim+1, 1U); - for (unsigned int i=1; i -std::vector -FE_Q::face_lexicographic_to_hierarchic_numbering (const unsigned int degree) -{ - const FiniteElementData face_data(FE_Q::get_dpo_vector(degree),1,degree); - std::vector face_renumber (face_data.dofs_per_cell); - FETools::lexicographic_to_hierarchic_numbering (face_data, face_renumber); - return face_renumber; -} - - - -template <> -std::vector -FE_Q<1>::face_lexicographic_to_hierarchic_numbering (const unsigned int) -{ - return std::vector(); -} - -template <> -std::vector -FE_Q<1,2>::face_lexicographic_to_hierarchic_numbering (const unsigned int) -{ - return std::vector(); -} - - -template <> -std::vector -FE_Q<1,3>::face_lexicographic_to_hierarchic_numbering (const unsigned int) -{ - return std::vector(); -} - - -template -void -FE_Q::initialize_constraints () -{ - QTrapez<1> trapez; - QIterated<1> points (trapez,this->degree); - Implementation::initialize_constraints (points, *this); -} - - - -template -void -FE_Q::initialize_embedding () -{ - // compute the interpolation matrices in - // much the same way as we do for the - // constraints. it's actually simpler - // here, since we don't have this weird - // renumbering stuff going on. The trick - // is again that we the interpolation - // matrix is formed by a permutation of - // the indices of the cell matrix. The - // value eps is used a threshold to - // decide when certain evaluations of the - // Lagrange polynomials are zero or one. - const double eps = 1e-15*this->degree*dim; - -#ifdef DEBUG - // in DEBUG mode, check that the evaluation of - // support points in the current numbering - // gives the identity operation - for (unsigned int i=0; idofs_per_cell; ++i) - { - Assert (std::fabs (1.-this->poly_space.compute_value - (i, this->unit_support_points[i])) < eps, - ExcInternalError()); - for (unsigned int j=0; jdofs_per_cell; ++j) - if (j!=i) - Assert (std::fabs (this->poly_space.compute_value - (i, this->unit_support_points[j])) < eps, - ExcInternalError()); - } -#endif - - // to efficiently evaluate the polynomial at - // the subcell, make use of the tensor product - // structure of this element and only evaluate - // 1D information from the polynomial. This - // makes the cost of this function almost - // negligible also for high order elements - const unsigned int dofs1d = this->degree+1; - std::vector > - subcell_evaluations (dim, Table<2,double>(dofs1d, dofs1d)); - const std::vector &index_map_inverse = - this->poly_space.get_numbering_inverse(); - - // recreate 1D polynomials - std::vector > poly_space1d = - FE_Q_Helper::generate_poly_space1d (this->unit_support_points, - index_map_inverse, dofs1d); - - // helper value: step size how to walk through - // diagonal and how many points we have left - // apart from the first dimension - unsigned int step_size_diag = 0; - { - unsigned int factor = 1; - for (unsigned int d=0; d::isotropic_refinement; ++ref) - for (unsigned int child=0; child::n_children(RefinementCase(ref+1)); ++child) - { - // go through the points in diagonal to - // capture variation in all directions - // simultaneously - for (unsigned int j=0; j p_subcell = this->unit_support_points[diag_comp]; - const Point p_cell = - GeometryInfo::child_to_cell_coordinates (p_subcell, child, - RefinementCase(ref+1)); - for (unsigned int i=0; i (j_indices); - for (unsigned int j=0; jdofs_per_cell; j+=dofs1d) - { - unsigned int i_indices[dim]; - FE_Q_Helper::zero_indices (i_indices); - for (unsigned int i=0; idofs_per_cell; i+=dofs1d) - { - double val_extra_dim = 1.; - for (unsigned int d=1; dprolongation[ref][child](j,i) = - // this->poly_space.compute_value (i, p_cell); - for (unsigned int jj=0; jjprolongation[ref][child](j_ind,index_map_inverse[i+ii]) - = val_extra_dim * subcell_evaluations[0](jj,ii); - } - - // update indices that denote the tensor - // product position. a bit fuzzy and therefore - // not done for innermost x_0 direction - FE_Q_Helper::increment_indices (i_indices, dofs1d); - } - Assert (i_indices[dim-1] == 1, ExcInternalError()); - FE_Q_Helper::increment_indices (j_indices, dofs1d); - } - - // and make sure that the row sum is - // 1. this must be so since for this - // element, the shape functions add up to - // one -#ifdef DEBUG - for (unsigned int row=0; rowdofs_per_cell; ++row) - { - double sum = 0; - for (unsigned int col=0; coldofs_per_cell; ++col) - sum += this->prolongation[ref][child](row,col); - Assert (std::fabs(sum-1.) < eps, ExcInternalError()); - } -#endif - } -} - - - -template -void -FE_Q::initialize_restriction () -{ - // for these Lagrange interpolation - // polynomials, construction of the - // restriction matrices is relatively - // simple. the reason is that the - // interpolation points on the mother cell - // are (except for the case with arbitrary - // nonequidistant nodes) always also - // interpolation points for some shape - // function on one or the other child, - // because we have chosen equidistant - // Lagrange interpolation points for the - // polynomials - // - // so the only thing we have to - // find out is: for each shape - // function on the mother cell, - // which is the child cell - // (possibly more than one) on - // which it is located, and which - // is the corresponding shape - // function there. rather than - // doing it for the shape functions - // on the mother cell, we take the - // interpolation points there - // - // note that the interpolation - // point of a shape function can be - // on the boundary between - // subcells. in that case, - // restriction from children to - // mother may produce two or more - // entries for a dof on the mother - // cell. however, this doesn't - // hurt: since the element is - // continuous, the contribution - // from each child should yield the - // same result, and since the - // element is non-additive we just - // overwrite one value (compute one - // one child) by the same value - // (compute on a later child), so - // we don't have to care about this - - const double eps = 1e-15*this->degree*dim; - const std::vector &index_map_inverse = - this->poly_space.get_numbering_inverse(); - - // recreate 1D polynomials for faster - // evaluation of polynomial - const unsigned int dofs1d = this->degree+1; - std::vector > poly_space1d = - FE_Q_Helper::generate_poly_space1d (this->unit_support_points, - index_map_inverse, dofs1d); - std::vector > evaluations1d (dofs1d); - - for (unsigned int i=0; idofs_per_cell; ++i) - { - unsigned int mother_dof = index_map_inverse[i]; - const Point p_cell = this->unit_support_points[mother_dof]; - - // then find the children on - // which the interpolation - // point is located - for (unsigned int ref=RefinementCase::cut_x; ref<=RefinementCase::isotropic_refinement; ++ref) - for (unsigned int child=0; child::n_children(RefinementCase(ref)); ++child) - { - // check whether this - // interpolation point is - // inside this child cell - const Point p_subcell - = GeometryInfo::cell_to_child_coordinates (p_cell, child, RefinementCase(ref)); - if (GeometryInfo::is_inside_unit_cell (p_subcell)) - { - // same logic as in initialize_embedding to - // evaluate the polynomial faster than from - // the tensor product: since we evaluate all - // polynomials, it is much faster to just - // compute the 1D values for all polynomials - // before and then get the dim-data. - for (unsigned int j=0; j (j_indices); - double sum_check = 0; - for (unsigned int j = 0; jdofs_per_cell; j += dofs1d) - { - double val_extra_dim = 1.; - for (unsigned int d=1; drestriction[ref-1][child](mother_dof,child_dof)=1.; - else if (std::fabs(val) > eps) - this->restriction[ref-1][child](mother_dof,child_dof)=val; - sum_check += val; - } - FE_Q_Helper::increment_indices (j_indices, dofs1d); - } - Assert (std::fabs(sum_check-1) < eps, - ExcInternalError()); - } - } - } -} - - - -//--------------------------------------------------------------------------- -// Data field initialization -//--------------------------------------------------------------------------- - - -template <> -bool -FE_Q<1>::has_support_on_face (const unsigned int shape_index, - const unsigned int face_index) const -{ - Assert (shape_index < this->dofs_per_cell, - ExcIndexRange (shape_index, 0, this->dofs_per_cell)); - Assert (face_index < GeometryInfo<1>::faces_per_cell, - ExcIndexRange (face_index, 0, GeometryInfo<1>::faces_per_cell)); - - - // in 1d, things are simple. since - // there is only one degree of - // freedom per vertex in this - // class, the first is on vertex 0 - // (==face 0 in some sense), the - // second on face 1: - return (((shape_index == 0) && (face_index == 0)) || - ((shape_index == 1) && (face_index == 1))); -} - - -template <> -bool -FE_Q<1,2>::has_support_on_face (const unsigned int shape_index, - const unsigned int face_index) const -{ - Assert (shape_index < this->dofs_per_cell, - ExcIndexRange (shape_index, 0, this->dofs_per_cell)); - Assert (face_index < GeometryInfo<1>::faces_per_cell, - ExcIndexRange (face_index, 0, GeometryInfo<1>::faces_per_cell)); - - - // in 1d, things are simple. since - // there is only one degree of - // freedom per vertex in this - // class, the first is on vertex 0 - // (==face 0 in some sense), the - // second on face 1: - return (((shape_index == 0) && (face_index == 0)) || - ((shape_index == 1) && (face_index == 1))); -} - -template <> -bool -FE_Q<1,3>::has_support_on_face (const unsigned int shape_index, - const unsigned int face_index) const -{ - Assert (shape_index < this->dofs_per_cell, - ExcIndexRange (shape_index, 0, this->dofs_per_cell)); - Assert (face_index < GeometryInfo<1>::faces_per_cell, - ExcIndexRange (face_index, 0, GeometryInfo<1>::faces_per_cell)); - - - // in 1d, things are simple. since - // there is only one degree of - // freedom per vertex in this - // class, the first is on vertex 0 - // (==face 0 in some sense), the - // second on face 1: - return (((shape_index == 0) && (face_index == 0)) || - ((shape_index == 1) && (face_index == 1))); -} - - -template -bool -FE_Q::has_support_on_face (const unsigned int shape_index, - const unsigned int face_index) const -{ - Assert (shape_index < this->dofs_per_cell, - ExcIndexRange (shape_index, 0, this->dofs_per_cell)); - Assert (face_index < GeometryInfo::faces_per_cell, - ExcIndexRange (face_index, 0, GeometryInfo::faces_per_cell)); - - - // first, special-case interior - // shape functions, since they - // have no support no-where on - // the boundary - if (((dim==2) && (shape_index>=this->first_quad_index)) - || - ((dim==3) && (shape_index>=this->first_hex_index))) - return false; - - // let's see whether this is a - // vertex - if (shape_index < this->first_line_index) - { - // for Q elements, there is - // one dof per vertex, so - // shape_index==vertex_number. check - // whether this vertex is - // on the given face. thus, - // for each face, give a - // list of vertices - const unsigned int vertex_no = shape_index; - Assert (vertex_no < GeometryInfo::vertices_per_cell, - ExcInternalError()); - - for (unsigned int v=0; v::vertices_per_face; ++v) - if (GeometryInfo::face_to_cell_vertices(face_index, v) == vertex_no) - return true; - - return false; - } - else if (shape_index < this->first_quad_index) - // ok, dof is on a line - { - const unsigned int line_index - = (shape_index - this->first_line_index) / this->dofs_per_line; - Assert (line_index < GeometryInfo::lines_per_cell, - ExcInternalError()); - - // in 2d, the line is the - // face, so get the line - // index - if (dim == 2) - return (line_index == face_index); - else if (dim == 3) - { - // see whether the - // given line is on the - // given face. - for (unsigned int l=0; l::lines_per_face; ++l) - if (GeometryInfo<3>::face_to_cell_lines(face_index, l) == line_index) - return true; - - return false; - } - else - Assert (false, ExcNotImplemented()); - } - else if (shape_index < this->first_hex_index) - // dof is on a quad - { - const unsigned int quad_index - = (shape_index - this->first_quad_index) / this->dofs_per_quad; - Assert (static_cast(quad_index) < - static_cast(GeometryInfo::quads_per_cell), - ExcInternalError()); - - // in 2d, cell bubble are - // zero on all faces. but - // we have treated this - // case above already - Assert (dim != 2, ExcInternalError()); - - // in 3d, - // quad_index=face_index - if (dim == 3) - return (quad_index == face_index); - else - Assert (false, ExcNotImplemented()); - } - else - // dof on hex - { - // can only happen in 3d, - // but this case has - // already been covered - // above - Assert (false, ExcNotImplemented()); - return false; - } - - // we should not have gotten here - Assert (false, ExcInternalError()); - return false; -} - - - -template -std::size_t -FE_Q::memory_consumption () const -{ - return (FiniteElement::memory_consumption() + - MemoryConsumption::memory_consumption (face_index_map)); -} - - - // explicit instantiations #include "fe_q.inst" diff --git a/deal.II/source/fe/fe_q_base.cc b/deal.II/source/fe/fe_q_base.cc new file mode 100644 index 0000000000..43d46d3dc9 --- /dev/null +++ b/deal.II/source/fe/fe_q_base.cc @@ -0,0 +1,1426 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//--------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +DEAL_II_NAMESPACE_OPEN + + +namespace FE_Q_Helper +{ + namespace + { + // get the renumbering for faces + template + inline + std::vector + face_lexicographic_to_hierarchic_numbering (const unsigned int degree) + { + std::vector dpo(dim, 1U); + for (unsigned int i=1; i face_data(dpo,1,degree); + std::vector face_renumber (face_data.dofs_per_cell); + FETools::lexicographic_to_hierarchic_numbering (face_data, face_renumber); + return face_renumber; + } + + // dummy specialization for dim == 1 to avoid linker errors + template <> + inline + std::vector + face_lexicographic_to_hierarchic_numbering<1> (const unsigned int) + { + return std::vector(); + } + + + + // in get_restriction_matrix() and get_prolongation_matrix(), want to undo + // tensorization on inner loops for performance reasons. this clears a + // dim-array + template + inline + void + zero_indices (unsigned int (&indices)[dim]) + { + for (unsigned int d=0; d + inline + void + increment_indices (unsigned int (&indices)[dim], + const unsigned int dofs1d) + { + ++indices[0]; + for (int d=0; d +struct FE_Q_Base::Implementation +{ + /** + * Initialize the hanging node constraints matrices. Called from the + * constructor in case the finite element is based on quadrature points. + */ + template + static + void initialize_constraints (const std::vector > &, + FE_Q_Base &) + { + // no constraints in 1d + } + + + template + static + void initialize_constraints (const std::vector > &points, + FE_Q_Base &fe) + { + const unsigned int dim = 2; + + // restricted to each face, the traces of the shape functions is an + // element of P_{k} (in 2d), or Q_{k} (in 3d), where k is the degree of + // the element. from this, we interpolate between mother and cell face. + + // the interpolation process works as follows: on each subface, we want + // that finite element solutions from both sides coincide. i.e. if a and b + // are expansion coefficients for the shape functions from both sides, we + // seek a relation between a and b such that + // sum_j a_j phi^c_j(x) == sum_j b_j phi_j(x) + // for all points x on the interface. here, phi^c_j are the shape + // functions on the small cell on one side of the face, and phi_j those on + // the big cell on the other side. To get this relation, it suffices to + // look at a sufficient number of points for which this has to hold. if + // there are n functions, then we need n evaluation points, and we choose + // them equidistantly. + // + // we obtain the matrix system + // A a == B b + // where + // A_ij = phi^c_j(x_i) + // B_ij = phi_j(x_i) + // and the relation we are looking for is + // a = A^-1 B b + // + // for the special case of Lagrange interpolation polynomials, A_ij + // reduces to delta_ij, and + // a_i = B_ij b_j + // Hence, interface_constraints(i,j)=B_ij. + // + // for the general case, where we don't have Lagrange interpolation + // polynomials, this is a little more complicated. Then we would evaluate + // at a number of points and invert the interpolation matrix A. + // + // Note, that we build up these matrices for all subfaces at once, rather + // than considering them separately. the reason is that we finally will + // want to have them in this order anyway, as this is the format we need + // inside deal.II + + // In the following the points x_i are constructed in following order + // (n=degree-1) + // *----------*---------* + // 1..n 0 n+1..2n + // i.e. first the midpoint of the line, then the support points on subface + // 0 and on subface 1 + std::vector > constraint_points; + // Add midpoint + constraint_points.push_back (Point (0.5)); + + if (fe.degree>1) + { + const unsigned int n=fe.degree-1; + const double step=1./fe.degree; + // subface 0 + for (unsigned int i=1; i<=n; ++i) + constraint_points.push_back ( + GeometryInfo::child_to_cell_coordinates(Point(i*step),0)); + // subface 1 + for (unsigned int i=1; i<=n; ++i) + constraint_points.push_back ( + GeometryInfo::child_to_cell_coordinates(Point(i*step),1)); + } + + // Now construct relation between destination (child) and source (mother) + // dofs. + + fe.interface_constraints + .TableBase<2,double>::reinit (fe.interface_constraints_size()); + + // use that the element evaluates to 1 at index 0 and along the line at + // zero + const std::vector &index_map_inverse = + fe.poly_space.get_numbering_inverse(); + const std::vector face_index_map = + FE_Q_Helper::face_lexicographic_to_hierarchic_numbering(fe.degree); + Assert(std::abs(fe.poly_space.compute_value(index_map_inverse[0],Point()) + - 1.) < 1e-14, + ExcInternalError()); + + for (unsigned int i=0; i p; + p[0] = constraint_points[i](0); + fe.interface_constraints(i,face_index_map[j]) = + fe.poly_space.compute_value(index_map_inverse[j], p); + + // if the value is small up to round-off, then simply set it to zero + // to avoid unwanted fill-in of the constraint matrices (which would + // then increase the number of other DoFs a constrained DoF would + // couple to) + if (std::fabs(fe.interface_constraints(i,j)) < 1e-14) + fe.interface_constraints(i,j) = 0; + } + } + + + template + static + void initialize_constraints (const std::vector > &points, + FE_Q_Base &fe) + { + const unsigned int dim = 3; + + // For a detailed documentation of the interpolation see the + // FE_Q_Base<2>::initialize_constraints function. + + // In the following the points x_i are constructed in the order as + // described in the documentation of the FiniteElement class (fe_base.h), + // i.e. + // *--15--4--16--* + // | | | + // 10 19 6 20 12 + // | | | + // 1--7---0--8---2 + // | | | + // 9 17 5 18 11 + // | | | + // *--13--3--14--* + std::vector > constraint_points; + + // Add midpoint + constraint_points.push_back (Point (0.5, 0.5)); + + // Add midpoints of lines of "mother-face" + constraint_points.push_back (Point (0, 0.5)); + constraint_points.push_back (Point (1, 0.5)); + constraint_points.push_back (Point (0.5, 0)); + constraint_points.push_back (Point (0.5, 1)); + + if (fe.degree>1) + { + const unsigned int n=fe.degree-1; + const double step=1./fe.degree; + std::vector > line_support_points(n); + for (unsigned int i=0; i qline(line_support_points); + + // auxiliary points in 2d + std::vector > p_line(n); + + // Add nodes of lines interior in the "mother-face" + + // line 5: use line 9 + QProjector::project_to_subface(qline, 0, 0, p_line); + for (unsigned int i=0; i (0.5, 0)); + // line 6: use line 10 + QProjector::project_to_subface(qline, 0, 1, p_line); + for (unsigned int i=0; i (0.5, 0)); + // line 7: use line 13 + QProjector::project_to_subface(qline, 2, 0, p_line); + for (unsigned int i=0; i (0, 0.5)); + // line 8: use line 14 + QProjector::project_to_subface(qline, 2, 1, p_line); + for (unsigned int i=0; i (0, 0.5)); + + // DoFs on bordering lines lines 9-16 + for (unsigned int face=0; face::faces_per_cell; ++face) + for (unsigned int subface=0; + subface::max_children_per_face; ++subface) + { + QProjector::project_to_subface(qline, face, subface, p_line); + constraint_points.insert(constraint_points.end(), + p_line.begin(), p_line.end()); + } + + // Create constraints for interior nodes + std::vector > inner_points(n*n); + for (unsigned int i=0, iy=1; iy<=n; ++iy) + for (unsigned int ix=1; ix<=n; ++ix) + inner_points[i++] = Point (ix*step, iy*step); + + // at the moment do this for isotropic face refinement only + for (unsigned int child=0; + child::max_children_per_cell; ++child) + for (unsigned int i=0; i::child_to_cell_coordinates(inner_points[i], child)); + } + + // Now construct relation between destination (child) and source (mother) + // dofs. + const unsigned int pnts=(fe.degree+1)*(fe.degree+1); + + // use that the element evaluates to 1 at index 0 and along the line at + // zero + const std::vector &index_map_inverse = + fe.poly_space.get_numbering_inverse(); + const std::vector face_index_map = + FE_Q_Helper::face_lexicographic_to_hierarchic_numbering(fe.degree); + Assert(std::abs(fe.poly_space.compute_value(index_map_inverse[0],Point()) + - 1.) < 1e-14, + ExcInternalError()); + + fe.interface_constraints + .TableBase<2,double>::reinit (fe.interface_constraints_size()); + + for (unsigned int i=0; i constraint_point; + + // Eliminate FP errors in constraint points. Due to their origin, they + // must all be fractions of the unit interval. If we have polynomial + // degree 4, the refined element has 8 intervals. Hence the + // coordinates must be 0, 0.125, 0.25, 0.375 etc. Now the coordinates + // of the constraint points will be multiplied by the inverse of the + // interval size (in the example by 8). After that the coordinates + // must be integral numbers. Hence a normal truncation is performed + // and the coordinates will be scaled back. The equal treatment of all + // coordinates should eliminate any FP errors. + for (unsigned int k=0; k (constraint_points[i](k) * interval + 0.25); + constraint_point(k) = 1.*coord_int / interval; + + // The following lines of code should eliminate the problems with + // the Constraint-Matrix, which appeared for P>=4. The + // ConstraintMatrix class complained about different constraints + // for the same entry of the Constraint-Matrix. Actually this + // difference could be attributed to FP errors, as it was in the + // range of 1.0e-16. These errors originate in the loss of + // symmetry in the FP approximation of the shape-functions. + // Considering a 3rd order shape function in 1D, we have + // N0(x)=N3(1-x) and N1(x)=N2(1-x). For higher order polynomials + // the FP approximations of the shape functions do not satisfy + // these equations any more! Thus in the following code + // everything is computed in the interval x \in [0..0.5], which is + // sufficient to express all values that could come out from a + // computation of any shape function in the full interval + // [0..1]. If x > 0.5 the computation is done for 1-x with the + // shape function N_{p-n} instead of N_n. Hence symmetry is + // preserved and everything works fine... + // + // For a different explanation of the problem, see the discussion + // in the FiniteElement class for constraint matrices in 3d. + mirror[k] = (constraint_point(k) > 0.5); + if (mirror[k]) + constraint_point(k) = 1.0 - constraint_point(k); + } + + for (unsigned int j=0; j +FE_Q_Base::FE_Q_Base (const POLY &poly_space, + const FiniteElementData &fe_data, + const std::vector &restriction_is_additive_flags) + : + FE_Poly(poly_space, fe_data, restriction_is_additive_flags, + std::vector(1, std::vector(1,true))) +{} + + + +template +void +FE_Q_Base::initialize (const std::vector > &points) +{ + Assert (points[0][0] == 0, + ExcMessage ("The first support point has to be zero.")); + Assert (points.back()[0] == 1, + ExcMessage ("The last support point has to be one.")); + + // distinguish q/q_dg0 case: need to be flexible enough to allow more + // degrees of freedom than there are FE_Q degrees of freedom for derived + // class FE_Q_DG0 that otherwise shares 95% of the code. + const unsigned int q_dofs_per_cell = Utilities::fixed_power(this->degree+1); + Assert(q_dofs_per_cell == this->dofs_per_cell || + q_dofs_per_cell+1 == this->dofs_per_cell, ExcInternalError()); + + { + std::vector renumber(q_dofs_per_cell); + const FiniteElementData fe(get_dpo_vector(this->degree),1, + this->degree); + FETools::hierarchic_to_lexicographic_numbering (fe, renumber); + if (this->dofs_per_cell > q_dofs_per_cell) + renumber.push_back(q_dofs_per_cell); + this->poly_space.set_numbering(renumber); + } + + // finally fill in support points on cell and face + initialize_unit_support_points (points); + initialize_unit_face_support_points (points); + + // reinit constraints + initialize_constraints (points); + + // do not initialize embedding and restriction here. these matrices are + // initialized on demand in get_restriction_matrix and + // get_prolongation_matrix + + this->initialize_quad_dof_index_permutation(); +} + + + +template +void +FE_Q_Base:: +get_interpolation_matrix (const FiniteElement &x_source_fe, + FullMatrix &interpolation_matrix) const +{ + // this is only implemented, if the source FE is also a Q element + AssertThrow ((dynamic_cast *>(&x_source_fe) != 0), + (typename FiniteElement::ExcInterpolationNotImplemented())); + + Assert (interpolation_matrix.m() == this->dofs_per_cell, + ExcDimensionMismatch (interpolation_matrix.m(), + this->dofs_per_cell)); + Assert (interpolation_matrix.n() == x_source_fe.dofs_per_cell, + ExcDimensionMismatch (interpolation_matrix.m(), + x_source_fe.dofs_per_cell)); + + // ok, source is a Q element, so we will be able to do the work + const FE_Q_Base &source_fe + = dynamic_cast&>(x_source_fe); + + // only evaluate Q dofs + const unsigned int q_dofs_per_cell = Utilities::fixed_power(this->degree+1); + const unsigned int source_q_dofs_per_cell = Utilities::fixed_power(source_fe.degree+1); + + // evaluation is simply done by evaluating the other FE's basis functions on + // the unit support points (FE_Q has the property that the cell + // interpolation matrix is a unit matrix, so no need to evaluate it and + // invert it) + for (unsigned int j=0; j p = this->unit_support_points[j]; + + // FE_Q element evaluates to 1 in unit support point and to zero in all + // other points by construction + Assert(std::abs(this->poly_space.compute_value (j, p)-1.)<1e-13, + ExcInternalError()); + + for (unsigned int i=0; idofs_per_cell) + { + AssertDimension(source_q_dofs_per_cell+1, source_fe.dofs_per_cell); + for (unsigned int i=0; idegree*dim; + for (unsigned int i=0; idofs_per_cell; ++i) + for (unsigned int j=0; jdofs_per_cell; ++i) + { + double sum = 0.; + for (unsigned int j=0; j +void +FE_Q_Base:: +get_face_interpolation_matrix (const FiniteElement &x_source_fe, + FullMatrix &interpolation_matrix) const +{ + Assert (dim > 1, ExcImpossibleInDim(1)); + + // this is only implemented, if the source FE is also a Q element + AssertThrow ((dynamic_cast *>(&x_source_fe) != 0), + (typename FiniteElement:: + ExcInterpolationNotImplemented())); + + Assert (interpolation_matrix.n() == this->dofs_per_face, + ExcDimensionMismatch (interpolation_matrix.n(), + this->dofs_per_face)); + Assert (interpolation_matrix.m() == x_source_fe.dofs_per_face, + ExcDimensionMismatch (interpolation_matrix.m(), + x_source_fe.dofs_per_face)); + + // ok, source is a Q element, so we will be able to do the work + const FE_Q_Base &source_fe + = dynamic_cast&>(x_source_fe); + + // Make sure that the element for which the DoFs should be constrained is + // the one with the higher polynomial degree. Actually the procedure will + // work also if this assertion is not satisfied. But the matrices produced + // in that case might lead to problems in the hp procedures, which use this + // method. + Assert (this->dofs_per_face <= source_fe.dofs_per_face, + (typename FiniteElement:: + ExcInterpolationNotImplemented ())); + + // generate a quadrature with the unit support points. This is later based + // as a basis for the QProjector, which returns the support points on the + // face. + Quadrature quad_face_support (source_fe.get_unit_face_support_points ()); + + // Rule of thumb for FP accuracy, that can be expected for a given + // polynomial degree. This value is used to cut off values close to zero. + const double eps = 2e-13*this->degree*(dim-1); + + // compute the interpolation matrix by simply taking the value at the + // support points. +//TODO: Verify that all faces are the same with respect to +// these support points. Furthermore, check if something has to +// be done for the face orientation flag in 3D. + const Quadrature face_quadrature + = QProjector::project_to_face (quad_face_support, 0); + for (unsigned int i=0; i &p = face_quadrature.point (i); + + for (unsigned int j=0; jdofs_per_face; ++j) + { + double matrix_entry = this->poly_space.compute_value (this->face_to_cell_index(j, 0), p); + + // Correct the interpolated value. I.e. if it is close to 1 or 0, + // make it exactly 1 or 0. Unfortunately, this is required to avoid + // problems with higher order elements. + if (std::fabs (matrix_entry - 1.0) < eps) + matrix_entry = 1.0; + if (std::fabs (matrix_entry) < eps) + matrix_entry = 0.0; + + interpolation_matrix(i,j) = matrix_entry; + } + } + + // make sure that the row sum of each of the matrices is 1 at this + // point. this must be so since the shape functions sum up to 1 + for (unsigned int j=0; jdofs_per_face; ++i) + sum += interpolation_matrix(j,i); + + Assert (std::fabs(sum-1) < 2e-13*this->degree*(dim-1), + ExcInternalError()); + } +} + + + +template +void +FE_Q_Base:: +get_subface_interpolation_matrix (const FiniteElement &x_source_fe, + const unsigned int subface, + FullMatrix &interpolation_matrix) const +{ + Assert (interpolation_matrix.m() == x_source_fe.dofs_per_face, + ExcDimensionMismatch (interpolation_matrix.m(), + x_source_fe.dofs_per_face)); + + // see if source is a Q element + if (const FE_Q_Base *source_fe + = dynamic_cast *>(&x_source_fe)) + { + // have this test in here since a table of size 2x0 reports its size as + // 0x0 + Assert (interpolation_matrix.n() == this->dofs_per_face, + ExcDimensionMismatch (interpolation_matrix.n(), + this->dofs_per_face)); + + // Make sure that the element for which the DoFs should be constrained + // is the one with the higher polynomial degree. Actually the procedure + // will work also if this assertion is not satisfied. But the matrices + // produced in that case might lead to problems in the hp procedures, + // which use this method. + Assert (this->dofs_per_face <= source_fe->dofs_per_face, + (typename FiniteElement:: + ExcInterpolationNotImplemented ())); + + // generate a point on this cell and evaluate the shape functions there + const Quadrature + quad_face_support (source_fe->get_unit_face_support_points ()); + + // Rule of thumb for FP accuracy, that can be expected for a given + // polynomial degree. This value is used to cut off values close to + // zero. + double eps = 2e-13*this->degree*(dim-1); + + // compute the interpolation matrix by simply taking the value at the + // support points. +//TODO: Verify that all faces are the same with respect to +// these support points. Furthermore, check if something has to +// be done for the face orientation flag in 3D. + const Quadrature subface_quadrature + = QProjector::project_to_subface (quad_face_support, 0, subface); + for (unsigned int i=0; idofs_per_face; ++i) + { + const Point &p = subface_quadrature.point (i); + + for (unsigned int j=0; jdofs_per_face; ++j) + { + double matrix_entry = this->shape_value (this->face_to_cell_index(j, 0), p); + + // Correct the interpolated value. I.e. if it is close to 1 or + // 0, make it exactly 1 or 0. Unfortunately, this is required to + // avoid problems with higher order elements. + if (std::fabs (matrix_entry - 1.0) < eps) + matrix_entry = 1.0; + if (std::fabs (matrix_entry) < eps) + matrix_entry = 0.0; + + interpolation_matrix(i,j) = matrix_entry; + } + } + + // make sure that the row sum of each of the matrices is 1 at this + // point. this must be so since the shape functions sum up to 1 + for (unsigned int j=0; jdofs_per_face; ++j) + { + double sum = 0.; + + for (unsigned int i=0; idofs_per_face; ++i) + sum += interpolation_matrix(j,i); + + Assert (std::fabs(sum-1) < 2e-13*this->degree*dim, + ExcInternalError()); + } + } + else if (dynamic_cast *>(&x_source_fe) != 0) + { + // nothing to do here, the FE_Nothing has no degrees of freedom anyway + } + else + AssertThrow (false,(typename FiniteElement:: + ExcInterpolationNotImplemented())); +} + + + +template +bool +FE_Q_Base::hp_constraints_are_implemented () const +{ + return true; +} + + + + +template +std::vector > +FE_Q_Base:: +hp_vertex_dof_identities (const FiniteElement &fe_other) const +{ + // we can presently only compute these identities if both FEs are FE_Qs or + // if the other one is an FE_Nothing. in the first case, there should be + // exactly one single DoF of each FE at a vertex, and they should have + // identical value + if (dynamic_cast*>(&fe_other) != 0) + { + return + std::vector > + (1, std::make_pair (0U, 0U)); + } + else if (dynamic_cast*>(&fe_other) != 0) + { + // the FE_Nothing has no degrees of freedom, so there are no + // equivalencies to be recorded + return std::vector > (); + } + else + { + Assert (false, ExcNotImplemented()); + return std::vector > (); + } +} + + + +template +std::vector > +FE_Q_Base:: +hp_line_dof_identities (const FiniteElement &fe_other) const +{ + // we can presently only compute these identities if both FEs are FE_Qs or + // if the other one is an FE_Nothing + if (const FE_Q_Base *fe_q_other = dynamic_cast*>(&fe_other)) + { + // dofs are located along lines, so two dofs are identical if they are + // located at identical positions. if we had only equidistant points, we + // could simple check for similarity like (i+1)*q == (j+1)*p, but we + // might have other support points (e.g. Gauss-Lobatto + // points). Therefore, read the points in unit_support_points for the + // first coordinate direction. We take the lexicographic ordering of the + // points in the first direction (i.e., x-direction), which we access + // between index 1 and p-1 (index 0 and p are vertex dofs). + const unsigned int p = this->degree; + const unsigned int q = fe_q_other->degree; + + std::vector > identities; + + const std::vector &index_map_inverse= + this->poly_space.get_numbering_inverse(); + const std::vector &index_map_inverse_other= + fe_q_other->poly_space.get_numbering_inverse(); + + for (unsigned int i=0; iunit_support_points[index_map_inverse[i+1]][0]- + fe_q_other->unit_support_points[index_map_inverse_other[j+1]][0]) + < 1e-14) + identities.push_back (std::make_pair(i,j)); + + return identities; + } + else if (dynamic_cast*>(&fe_other) != 0) + { + // the FE_Nothing has no degrees of freedom, so there are no + // equivalencies to be recorded + return std::vector > (); + } + else + { + Assert (false, ExcNotImplemented()); + return std::vector > (); + } +} + + + +template +std::vector > +FE_Q_Base:: +hp_quad_dof_identities (const FiniteElement &fe_other) const +{ + // we can presently only compute these identities if both FEs are FE_Qs or + // if the other one is an FE_Nothing + if (const FE_Q_Base *fe_q_other = dynamic_cast*>(&fe_other)) + { + // this works exactly like the line case above, except that now we have + // to have two indices i1, i2 and j1, j2 to characterize the dofs on the + // face of each of the finite elements. since they are ordered + // lexicographically along the first line and we have a tensor product, + // the rest is rather straightforward + const unsigned int p = this->degree; + const unsigned int q = fe_q_other->degree; + + std::vector > identities; + + const std::vector &index_map_inverse= + this->poly_space.get_numbering_inverse(); + const std::vector &index_map_inverse_other= + fe_q_other->poly_space.get_numbering_inverse(); + + for (unsigned int i1=0; i1unit_support_points[index_map_inverse[i1+1]][0]- + fe_q_other->unit_support_points[index_map_inverse_other[j1+1]][0]) + < 1e-14) + && + (std::fabs(this->unit_support_points[index_map_inverse[i2+1]][0]- + fe_q_other->unit_support_points[index_map_inverse_other[j2+1]][0]) + < 1e-14)) + identities.push_back (std::make_pair(i1*(p-1)+i2, + j1*(q-1)+j2)); + + return identities; + } + else if (dynamic_cast*>(&fe_other) != 0) + { + // the FE_Nothing has no degrees of freedom, so there are no + // equivalencies to be recorded + return std::vector > (); + } + else + { + Assert (false, ExcNotImplemented()); + return std::vector > (); + } +} + + + +template +FiniteElementDomination::Domination +FE_Q_Base:: +compare_for_face_domination (const FiniteElement &fe_other) const +{ + if (const FE_Q_Base *fe_q_other + = dynamic_cast*>(&fe_other)) + { + if (this->degree < fe_q_other->degree) + return FiniteElementDomination::this_element_dominates; + else if (this->degree == fe_q_other->degree) + return FiniteElementDomination::either_element_can_dominate; + else + return FiniteElementDomination::other_element_dominates; + } + else if (dynamic_cast*>(&fe_other) != 0) + { + // the FE_Nothing has no degrees of freedom and it is typically used in + // a context where we don't require any continuity along the interface + return FiniteElementDomination::no_requirements; + } + + Assert (false, ExcNotImplemented()); + return FiniteElementDomination::neither_element_dominates; +} + + +//--------------------------------------------------------------------------- +// Auxiliary functions +//--------------------------------------------------------------------------- + + + +template +void FE_Q_Base::initialize_unit_support_points (const std::vector > &points) +{ + const std::vector &index_map_inverse= + this->poly_space.get_numbering_inverse(); + + Quadrature<1> support_1d(points); + Quadrature support_quadrature(support_1d); + this->unit_support_points.resize(support_quadrature.size()); + + for (unsigned int k=0; kunit_support_points[index_map_inverse[k]] = support_quadrature.point(k); +} + + + +template +void FE_Q_Base::initialize_unit_face_support_points (const std::vector > &points) +{ + // no faces in 1d, so nothing to do + if (dim == 1) + return; + + const unsigned int codim = dim-1; + this->unit_face_support_points.resize(Utilities::fixed_power(this->degree+1)); + + // find renumbering of faces and assign from values of quadrature + std::vector face_index_map = + FE_Q_Helper::face_lexicographic_to_hierarchic_numbering(this->degree); + Quadrature<1> support_1d(points); + Quadrature support_quadrature(support_1d); + this->unit_face_support_points.resize(support_quadrature.size()); + + for (unsigned int k=0; kunit_face_support_points[face_index_map[k]] = support_quadrature.point(k); +} + + + +template +void +FE_Q_Base::initialize_quad_dof_index_permutation () +{ + // for 1D and 2D, do nothing + if (dim < 3) + return; + + Assert (this->adjust_quad_dof_index_for_face_orientation_table.n_elements()==8*this->dofs_per_quad, + ExcInternalError()); + + const unsigned int n=this->degree-1; + Assert(n*n==this->dofs_per_quad, ExcInternalError()); + + // alias for the table to fill + Table<2,int> &data=this->adjust_quad_dof_index_for_face_orientation_table; + + // the dofs on a face are connected to a n x n matrix. for example, for + // degree==4 we have the following dofs on a quad + + // ___________ + // | | + // | 6 7 8 | + // | | + // | 3 4 5 | + // | | + // | 0 1 2 | + // |___________| + // + // we have dof_no=i+n*j with index i in x-direction and index j in + // y-direction running from 0 to n-1. to extract i and j we can use + // i=dof_no%n and j=dof_no/n. i and j can then be used to construct the + // rotated and mirrored numbers. + + + for (unsigned int local=0; localdofs_per_quad; ++local) + // face support points are in lexicographic ordering with x running + // fastest. invert that (y running fastest) + { + unsigned int i=local%n, + j=local/n; + + // face_orientation=false, face_flip=false, face_rotation=false + data(local,0)=j + i *n - local; + // face_orientation=false, face_flip=false, face_rotation=true + data(local,1)=i + (n-1-j)*n - local; + // face_orientation=false, face_flip=true, face_rotation=false + data(local,2)=(n-1-j) + (n-1-i)*n - local; + // face_orientation=false, face_flip=true, face_rotation=true + data(local,3)=(n-1-i) + j *n - local; + // face_orientation=true, face_flip=false, face_rotation=false + data(local,4)=0; + // face_orientation=true, face_flip=false, face_rotation=true + data(local,5)=j + (n-1-i)*n - local; + // face_orientation=true, face_flip=true, face_rotation=false + data(local,6)=(n-1-i) + (n-1-j)*n - local; + // face_orientation=true, face_flip=true, face_rotation=true + data(local,7)=(n-1-j) + i *n - local; + } + + // aditionally initialize reordering of line dofs + for (unsigned int i=0; idofs_per_line; ++i) + this->adjust_line_dof_index_for_line_orientation_table[i]=this->dofs_per_line-1-i - i; +} + + + + +template +std::vector +FE_Q_Base::get_dpo_vector(const unsigned int deg) +{ + std::vector dpo(dim+1, 1U); + for (unsigned int i=1; i +void +FE_Q_Base::initialize_constraints (const std::vector > &points) +{ + Implementation::initialize_constraints (points, *this); +} + + + +template +const FullMatrix& +FE_Q_Base +::get_prolongation_matrix (const unsigned int child, + const RefinementCase &refinement_case) const +{ + Assert (refinement_case::isotropic_refinement+1, + ExcIndexRange(refinement_case,0,RefinementCase::isotropic_refinement+1)); + Assert (refinement_case!=RefinementCase::no_refinement, + ExcMessage("Prolongation matrices are only available for refined cells!")); + Assert (child::n_children(refinement_case), + ExcIndexRange(child,0,GeometryInfo::n_children(refinement_case))); + + // initialization upon first request + if (this->prolongation[refinement_case-1][child].n() == 0) + { + Threads::Mutex::ScopedLock lock(this->mutex); + + // if matrix got updated while waiting for the lock + if (this->prolongation[refinement_case-1][child].n() == + this->dofs_per_cell) + return this->prolongation[refinement_case-1][child]; + + // distinguish q/q_dg0 case: only treat Q dofs first + const unsigned int q_dofs_per_cell = Utilities::fixed_power(this->degree+1); + + // compute the interpolation matrices in much the same way as we do for + // the constraints. it's actually simpler here, since we don't have this + // weird renumbering stuff going on. The trick is again that we the + // interpolation matrix is formed by a permutation of the indices of the + // cell matrix. The value eps is used a threshold to decide when certain + // evaluations of the Lagrange polynomials are zero or one. + const double eps = 1e-15*this->degree*dim; + +#ifdef DEBUG + // in DEBUG mode, check that the evaluation of support points in the + // current numbering gives the identity operation + for (unsigned int i=0; ipoly_space.compute_value + (i, this->unit_support_points[i])) < eps, + ExcInternalError()); + for (unsigned int j=0; jpoly_space.compute_value + (i, this->unit_support_points[j])) < eps, + ExcInternalError()); + } +#endif + + // to efficiently evaluate the polynomial at the subcell, make use of + // the tensor product structure of this element and only evaluate 1D + // information from the polynomial. This makes the cost of this function + // almost negligible also for high order elements + const unsigned int dofs1d = this->degree+1; + std::vector > + subcell_evaluations (dim, Table<2,double>(dofs1d, dofs1d)); + const std::vector &index_map_inverse = + this->poly_space.get_numbering_inverse(); + + // helper value: step size how to walk through diagonal and how many + // points we have left apart from the first dimension + unsigned int step_size_diag = 0; + { + unsigned int factor = 1; + for (unsigned int d=0; d prolongate (this->dofs_per_cell, this->dofs_per_cell); + + // go through the points in diagonal to capture variation in all + // directions simultaneously + for (unsigned int j=0; j p_subcell = this->unit_support_points[diag_comp]; + const Point p_cell = + GeometryInfo::child_to_cell_coordinates (p_subcell, child, + refinement_case); + for (unsigned int i=0; i point; + point[0] = p_cell[d]; + const double cell_value = + this->poly_space.compute_value(index_map_inverse[i], point); + + // cut off values that are too small. note that we have here + // Lagrange interpolation functions, so they should be zero at + // almost all points, and one at the others, at least on the + // subcells. so set them to their exact values + // + // the actual cut-off value is somewhat fuzzy, but it works + // for 2e-13*degree*dim (see above), which is kind of + // reasonable given that we compute the values of the + // polynomials via an degree-step recursion and then multiply + // the 1d-values. this gives us a linear growth in degree*dim, + // times a small constant. + // + // the embedding matrix is given by applying the inverse of + // the subcell matrix on the cell_interpolation matrix. since + // the subcell matrix is actually only a permutation vector, + // all we need to do is to switch the rows we write the data + // into. moreover, cut off very small values here + if (std::fabs(cell_value) < eps) + subcell_evaluations[d](j,i) = 0; + else + subcell_evaluations[d](j,i) = cell_value; + } + } + + // now expand from 1D info. block innermost dimension (x_0) in order to + // avoid difficult checks at innermost loop + unsigned int j_indices[dim]; + FE_Q_Helper::zero_indices (j_indices); + for (unsigned int j=0; j (i_indices); + for (unsigned int i=0; ipoly_space.compute_value (i, p_cell) + for (unsigned int jj=0; jj (i_indices, dofs1d); + } + Assert (i_indices[dim-1] == 1, ExcInternalError()); + FE_Q_Helper::increment_indices (j_indices, dofs1d); + } + + // the discontinuous node is simply mapped on the discontinuous node on + // the child element + if (q_dofs_per_cell < this->dofs_per_cell) + prolongate(q_dofs_per_cell,q_dofs_per_cell) = 1.; + + // and make sure that the row sum is 1. this must be so since for this + // element, the shape functions add up to one +#ifdef DEBUG + for (unsigned int row=0; rowdofs_per_cell; ++row) + { + double sum = 0; + for (unsigned int col=0; coldofs_per_cell; ++col) + sum += prolongate(row,col); + Assert (std::fabs(sum-1.) < eps, ExcInternalError()); + } +#endif + + // swap matrices + std::swap(const_cast &> + (this->prolongation[refinement_case-1][child]), prolongate); + } + + // finally return the matrix + return this->prolongation[refinement_case-1][child]; +} + + + +template +const FullMatrix& +FE_Q_Base +::get_restriction_matrix (const unsigned int child, + const RefinementCase &refinement_case) const +{ + Assert (refinement_case::isotropic_refinement+1, + ExcIndexRange(refinement_case,0,RefinementCase::isotropic_refinement+1)); + Assert (refinement_case!=RefinementCase::no_refinement, + ExcMessage("Restriction matrices are only available for refined cells!")); + Assert (child::n_children(refinement_case), + ExcIndexRange(child,0,GeometryInfo::n_children(refinement_case))); + + // initialization upon first request + if (this->restriction[refinement_case-1][child].n() == 0) + { + Threads::Mutex::ScopedLock lock(this->mutex); + + // if matrix got updated while waiting for the lock... + if (this->restriction[refinement_case-1][child].n() == + this->dofs_per_cell) + return this->restriction[refinement_case-1][child]; + + FullMatrix restriction(this->dofs_per_cell, this->dofs_per_cell); + // distinguish q/q_dg0 case + const unsigned int q_dofs_per_cell = Utilities::fixed_power(this->degree+1); + + // for these Lagrange interpolation polynomials, construction of the + // restriction matrices is relatively simple. the reason is that the + // interpolation points on the mother cell are (except for the case with + // arbitrary nonequidistant nodes) always also interpolation points for + // some shape function on one or the other child, because we have chosen + // equidistant Lagrange interpolation points for the polynomials + // + // so the only thing we have to find out is: for each shape function on + // the mother cell, which is the child cell (possibly more than one) on + // which it is located, and which is the corresponding shape function + // there. rather than doing it for the shape functions on the mother + // cell, we take the interpolation points there + // + // note that the interpolation point of a shape function can be on the + // boundary between subcells. in that case, restriction from children to + // mother may produce two or more entries for a dof on the mother + // cell. however, this doesn't hurt: since the element is continuous, + // the contribution from each child should yield the same result, and + // since the element is non-additive we just overwrite one value + // (compute on one child) by the same value (compute on a later child), + // so we don't have to care about this + + const double eps = 1e-15*this->degree*dim; + const std::vector &index_map_inverse = + this->poly_space.get_numbering_inverse(); + + const unsigned int dofs1d = this->degree+1; + std::vector > evaluations1d (dofs1d); + + restriction.reinit(this->dofs_per_cell, this->dofs_per_cell); + + for (unsigned int i=0; i p_cell = this->unit_support_points[mother_dof]; + + // check whether this interpolation point is inside this child cell + const Point p_subcell + = GeometryInfo::cell_to_child_coordinates (p_cell, child, + refinement_case); + if (GeometryInfo::is_inside_unit_cell (p_subcell)) + { + // same logic as in initialize_embedding to evaluate the + // polynomial faster than from the tensor product: since we + // evaluate all polynomials, it is much faster to just compute + // the 1D values for all polynomials before and then get the + // dim-data. + for (unsigned int j=0; j point; + point[0] = p_subcell[d]; + evaluations1d[j][d] = + this->poly_space.compute_value(index_map_inverse[j], point); + } + unsigned int j_indices[dim]; + FE_Q_Helper::zero_indices (j_indices); + double sum_check = 0; + for (unsigned int j = 0; j eps) + restriction(mother_dof,child_dof)=val; + sum_check += val; + } + FE_Q_Helper::increment_indices (j_indices, dofs1d); + } + Assert (std::fabs(sum_check-1) < eps, + ExcInternalError()); + } + + // part for FE_Q_DG0 + if (q_dofs_per_cell < this->dofs_per_cell) + restriction(this->dofs_per_cell-1,this->dofs_per_cell-1) = + 1./GeometryInfo::n_children(RefinementCase(refinement_case)); + } + + // swap matrices + std::swap(const_cast &> + (this->restriction[refinement_case-1][child]), restriction); + + } + + return this->restriction[refinement_case-1][child]; +} + + + +//--------------------------------------------------------------------------- +// Data field initialization +//--------------------------------------------------------------------------- + + +template +bool +FE_Q_Base::has_support_on_face (const unsigned int shape_index, + const unsigned int face_index) const +{ + Assert (shape_index < this->dofs_per_cell, + ExcIndexRange (shape_index, 0, this->dofs_per_cell)); + Assert (face_index < GeometryInfo::faces_per_cell, + ExcIndexRange (face_index, 0, GeometryInfo::faces_per_cell)); + + // in 1d, things are simple. since there is only one degree of freedom per + // vertex in this class, the first is on vertex 0 (==face 0 in some sense), + // the second on face 1: + if (dim == 1) + return (((shape_index == 0) && (face_index == 0)) || + ((shape_index == 1) && (face_index == 1))); + + // first, special-case interior shape functions, since they have no support + // no-where on the boundary + if (((dim==2) && (shape_index>=this->first_quad_index)) + || + ((dim==3) && (shape_index>=this->first_hex_index))) + return false; + + // let's see whether this is a vertex + if (shape_index < this->first_line_index) + { + // for Q elements, there is one dof per vertex, so + // shape_index==vertex_number. check whether this vertex is on the given + // face. thus, for each face, give a list of vertices + const unsigned int vertex_no = shape_index; + Assert (vertex_no < GeometryInfo::vertices_per_cell, + ExcInternalError()); + + for (unsigned int v=0; v::vertices_per_face; ++v) + if (GeometryInfo::face_to_cell_vertices(face_index, v) == vertex_no) + return true; + + return false; + } + else if (shape_index < this->first_quad_index) + // ok, dof is on a line + { + const unsigned int line_index + = (shape_index - this->first_line_index) / this->dofs_per_line; + Assert (line_index < GeometryInfo::lines_per_cell, + ExcInternalError()); + + // in 2d, the line is the face, so get the line index + if (dim == 2) + return (line_index == face_index); + else if (dim == 3) + { + // silence compiler warning + const unsigned int lines_per_face = + dim == 3 ? GeometryInfo::lines_per_face : 1; + // see whether the given line is on the given face. + for (unsigned int l=0; l::face_to_cell_lines(face_index, l) == line_index) + return true; + + return false; + } + else + Assert (false, ExcNotImplemented()); + } + else if (shape_index < this->first_hex_index) + // dof is on a quad + { + const unsigned int quad_index + = (shape_index - this->first_quad_index) / this->dofs_per_quad; + Assert (static_cast(quad_index) < + static_cast(GeometryInfo::quads_per_cell), + ExcInternalError()); + + // in 2d, cell bubble are zero on all faces. but we have treated this + // case above already + Assert (dim != 2, ExcInternalError()); + + // in 3d, quad_index=face_index + if (dim == 3) + return (quad_index == face_index); + else + Assert (false, ExcNotImplemented()); + } + else + // dof on hex + { + // can only happen in 3d, but this case has already been covered above + Assert (false, ExcNotImplemented()); + return false; + } + + // we should not have gotten here + Assert (false, ExcInternalError()); + return false; +} + + + +// explicit instantiations +#include "fe_q_base.inst" + +DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/source/fe/fe_q_base.inst.in b/deal.II/source/fe/fe_q_base.inst.in new file mode 100644 index 0000000000..8d6c49215b --- /dev/null +++ b/deal.II/source/fe/fe_q_base.inst.in @@ -0,0 +1,24 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2013 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//--------------------------------------------------------------------------- + + +for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS) + { +#if deal_II_dimension <= deal_II_space_dimension + template class FE_Q_Base, deal_II_dimension, deal_II_space_dimension>; + template class FE_Q_Base, deal_II_dimension, deal_II_space_dimension>; + template class FE_Q_Base >, deal_II_dimension, deal_II_space_dimension>; +#endif + } + + diff --git a/deal.II/source/fe/fe_q_dg0.cc b/deal.II/source/fe/fe_q_dg0.cc index 50e01f5834..058ddf954d 100644 --- a/deal.II/source/fe/fe_q_dg0.cc +++ b/deal.II/source/fe/fe_q_dg0.cc @@ -1,9 +1,21 @@ +//--------------------------------------------------------------------------- +// $Id: fe_q.cc 28798 2013-03-07 17:00:02Z maier $ +// Version: $Name$ +// +// Copyright (C) 2012, 2013 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//--------------------------------------------------------------------------- + #include #include #include #include #include -#include #include #include @@ -14,523 +26,32 @@ DEAL_II_NAMESPACE_OPEN -// namespace for some functions that are used in this file. they are -// specific to numbering conventions used for the FE_Q element, and -// are thus not very interesting to the outside world. we'd like to -// simply put them into an anonymous namespace, but that triggers an -// odd error with icc which can't compile this small snippet if the -// function is static: -// -------------------- -// template struct int2type {}; -// -// namespace { -// static void SYMBOL (const int2type<1> & ) {} -// } -// -// template void g() { -// SYMBOL(int2type()); -// } -// -// template void g<1>(); -// -------------------- -// the function needs to be static because of another icc bug, though. -// work around this by packing everything into a namespace of its own -// and have the anonymous namespace inside -// -// this is now intel icc issue 216082 -namespace FE_Q_DG0_Helper -{ - namespace - { - // given a permutation array, - // compute and return the inverse - // permutation -#ifdef DEAL_II_ANON_NAMESPACE_BUG - static -#endif - inline - std::vector - invert_numbering (const std::vector &in) - { - std::vector out (in.size()); - for (unsigned int i=0; i - inline - void - zero_indices (unsigned int indices[dim]) - { - for (unsigned int d=0; d - inline - void - increment_indices (unsigned int indices[dim], - const unsigned int dofs1d) - { - ++indices[0]; - for (int d=0; d - inline - std::vector > - generate_poly_space1d (const std::vector > &unit_support_points, - const std::vector &index_map_inverse, - const unsigned int dofs1d) - { - AssertDimension (Utilities::fixed_power (dofs1d)+1, - unit_support_points.size()); - std::vector > points1d (dofs1d); - for (unsigned int i=0; i(unit_support_points[j](0)); - for (unsigned int d=1; d -struct FE_Q_DG0::Implementation -{ - /** - * Initialize the hanging node - * constraints matrices. Called from the - * constructor in case the finite element - * is based on quadrature points. - */ - template - static - void initialize_constraints (const Quadrature<1> &, - FE_Q_DG0<1,spacedim> &) - { - // no constraints in 1d - } - - - template - static - void initialize_constraints (const Quadrature<1> &points, - FE_Q_DG0<2,spacedim> &fe) - { - const unsigned int dim = 2; - - // restricted to each face, the - // traces of the shape functions is - // an element of P_{k} (in 2d), or - // Q_{k} (in 3d), where k is the - // degree of the element. from - // this, we interpolate between - // mother and cell face. - - // the interpolation process works - // as follows: on each subface, - // we want that finite element - // solutions from both sides - // coincide. i.e. if a and b are - // expansion coefficients for the - // shape functions from both sides, - // we seek a relation between a and - // b such that - // sum_j a_j phi^c_j(x) - // == sum_j b_j phi_j(x) - // for all points x on the - // interface. here, phi^c_j are the - // shape functions on the small - // cell on one side of the face, - // and phi_j those on the big cell - // on the other side. To get this - // relation, it suffices to look at - // a sufficient number of points - // for which this has to hold. if - // there are n functions, then we - // need n evaluation points, and we - // choose them equidistantly. - // - // we obtain the matrix system - // A a == B b - // where - // A_ij = phi^c_j(x_i) - // B_ij = phi_j(x_i) - // and the relation we are looking - // for is - // a = A^-1 B b - // - // for the special case of Lagrange - // interpolation polynomials, A_ij - // reduces to delta_ij, and - // a_i = B_ij b_j - // Hence, - // interface_constraints(i,j)=B_ij. - // - // for the general case, where we - // don't have Lagrange - // interpolation polynomials, this - // is a little more - // complicated. Then we would - // evaluate at a number of points - // and invert the interpolation - // matrix A. - // - // Note, that we build up these - // matrices for all subfaces at - // once, rather than considering - // them separately. the reason is - // that we finally will want to - // have them in this order anyway, - // as this is the format we need - // inside deal.II - - // In the following the points x_i - // are constructed in following - // order (n=degree-1) - // *----------*---------* - // 1..n 0 n+1..2n - // i.e. first the midpoint of the - // line, then the support points on - // subface 0 and on subface 1 - std::vector > constraint_points; - // Add midpoint - constraint_points.push_back (Point (0.5)); - - if (fe.degree>1) - { - const unsigned int n=fe.degree-1; - const double step=1./fe.degree; - // subface 0 - for (unsigned int i=1; i<=n; ++i) - constraint_points.push_back ( - GeometryInfo::child_to_cell_coordinates(Point(i*step),0)); - // subface 1 - for (unsigned int i=1; i<=n; ++i) - constraint_points.push_back ( - GeometryInfo::child_to_cell_coordinates(Point(i*step),1)); - } - - // Now construct relation between - // destination (child) and source (mother) - // dofs. - const std::vector > polynomials= - Polynomials::generate_complete_Lagrange_basis(points.get_points()); - - fe.interface_constraints - .TableBase<2,double>::reinit (fe.interface_constraints_size()); - - for (unsigned int i=0; i - static - void initialize_constraints (const Quadrature<1> &points, - FE_Q_DG0<3,spacedim> &fe) - { - const unsigned int dim = 3; - - // For a detailed documentation of - // the interpolation see the - // FE_Q<2>::initialize_constraints - // function. - - // In the following the points x_i - // are constructed in the order as - // described in the documentation - // of the FiniteElement class - // (fe_base.h), i.e. - // *--15--4--16--* - // | | | - // 10 19 6 20 12 - // | | | - // 1--7---0--8---2 - // | | | - // 9 17 5 18 11 - // | | | - // *--13--3--14--* - std::vector > constraint_points; - - // Add midpoint - constraint_points.push_back (Point (0.5, 0.5)); - - // Add midpoints of lines of - // "mother-face" - constraint_points.push_back (Point (0, 0.5)); - constraint_points.push_back (Point (1, 0.5)); - constraint_points.push_back (Point (0.5, 0)); - constraint_points.push_back (Point (0.5, 1)); - - if (fe.degree>1) - { - const unsigned int n=fe.degree-1; - const double step=1./fe.degree; - std::vector > line_support_points(n); - for (unsigned int i=0; i qline(line_support_points); - - // auxiliary points in 2d - std::vector > p_line(n); - - // Add nodes of lines interior - // in the "mother-face" - - // line 5: use line 9 - QProjector::project_to_subface(qline, 0, 0, p_line); - for (unsigned int i=0; i (0.5, 0)); - // line 6: use line 10 - QProjector::project_to_subface(qline, 0, 1, p_line); - for (unsigned int i=0; i (0.5, 0)); - // line 7: use line 13 - QProjector::project_to_subface(qline, 2, 0, p_line); - for (unsigned int i=0; i (0, 0.5)); - // line 8: use line 14 - QProjector::project_to_subface(qline, 2, 1, p_line); - for (unsigned int i=0; i (0, 0.5)); - - // DoFs on bordering lines - // lines 9-16 - for (unsigned int face=0; face::faces_per_cell; ++face) - for (unsigned int subface=0; - subface::max_children_per_face; ++subface) - { - QProjector::project_to_subface(qline, face, subface, p_line); - constraint_points.insert(constraint_points.end(), - p_line.begin(), p_line.end()); - } - - // Create constraints for - // interior nodes - std::vector > inner_points(n*n); - for (unsigned int i=0, iy=1; iy<=n; ++iy) - for (unsigned int ix=1; ix<=n; ++ix) - inner_points[i++] = Point (ix*step, iy*step); - - // at the moment do this for - // isotropic face refinement only - for (unsigned int child=0; - child::max_children_per_cell; ++child) - for (unsigned int i=0; i::child_to_cell_coordinates(inner_points[i], child)); - } - - // Now construct relation between - // destination (child) and source (mother) - // dofs. - const unsigned int pnts=(fe.degree+1)*(fe.degree+1); - const std::vector > polynomial_basis= - Polynomials::generate_complete_Lagrange_basis(points.get_points()); - - const TensorProductPolynomialsConst face_polynomials(polynomial_basis); - - fe.interface_constraints - .TableBase<2,double>::reinit (fe.interface_constraints_size()); - - for (unsigned int i=0; i constraint_point; - - // Eliminate FP errors in constraint - // points. Due to their origin, they - // must all be fractions of the unit - // interval. If we have polynomial - // degree 4, the refined element has 8 - // intervals. Hence the coordinates - // must be 0, 0.125, 0.25, 0.375 etc. - // Now the coordinates of the - // constraint points will be multiplied - // by the inverse of the interval size - // (in the example by 8). After that - // the coordinates must be integral - // numbers. Hence a normal truncation - // is performed and the coordinates - // will be scaled back. The equal - // treatment of all coordinates should - // eliminate any FP errors. - for (unsigned int k=0; k (constraint_points[i](k) * interval + 0.25); - constraint_point(k) = 1.*coord_int / interval; - - // The following lines of code - // should eliminate the problems - // with the Constraint-Matrix, - // which appeared for P>=4. The - // Constraint-Matrix class - // complained about different - // constraints for the same entry - // of the Constraint-Matrix. - // Actually this difference could - // be attributed to FP errors, as - // it was in the range of - // 1.0e-16. These errors originate - // in the loss of symmetry in the - // FP approximation of the - // shape-functions. Considering a - // 3rd order shape function in 1D, - // we have N0(x)=N3(1-x) and - // N1(x)=N2(1-x). For higher order - // polynomials the FP - // approximations of the shape - // functions do not satisfy these - // equations any more! Thus in the - // following code everything is - // computed in the interval x \in - // [0..0.5], which is sufficient to - // express all values that could - // come out from a computation of - // any shape function in the full - // interval [0..1]. If x > 0.5 the - // computation is done for 1-x with - // the shape function N_{p-n} - // instead of N_n. Hence symmetry - // is preserved and everything - // works fine... - // - // For a different explanation of - // the problem, see the discussion - // in the FiniteElement class - // for constraint matrices in 3d. - mirror[k] = (constraint_point(k) > 0.5); - if (mirror[k]) - constraint_point(k) = 1.0 - constraint_point(k); - } - - for (unsigned int j=0; j FE_Q_DG0::FE_Q_DG0 (const unsigned int degree) : - FE_Poly, dim, spacedim> ( + FE_Q_Base, dim, spacedim> ( TensorProductPolynomialsConst(Polynomials::LagrangeEquidistant::generate_complete_basis(degree)), FiniteElementData(get_dpo_vector(degree), 1, degree, FiniteElementData::L2), - get_riaf_vector(degree), - std::vector(1ll , std::vector(1,true))), - face_index_map(FE_Q_DG0_Helper::invert_numbering( - face_lexicographic_to_hierarchic_numbering (degree))) + get_riaf_vector(degree)) { Assert (degree > 0, ExcMessage ("This element can only be used for polynomial degrees " "greater than zero")); - std::vector renumber (this->dofs_per_cell-1); - const FiniteElementData fe(get_dpo_vector_q(degree),1,degree); - FETools::hierarchic_to_lexicographic_numbering (fe, renumber); - renumber.push_back(this->dofs_per_cell-1); - this->poly_space.set_numbering(renumber); - - // finally fill in support points - // on cell and face - initialize_unit_support_points (); - initialize_unit_face_support_points (); + std::vector > support_points_1d(degree+1); + for (unsigned int i=0; i<=degree; ++i) + support_points_1d[i][0] = static_cast(i)/degree; - // reinit constraints - initialize_constraints (); + this->initialize(support_points_1d); - // Reinit the vectors of restriction and prolongation matrices to the - // right sizes and compute the matrices - this->reinit_restriction_and_prolongation_matrices(); - initialize_embedding(); - initialize_restriction(); - - initialize_quad_dof_index_permutation(); + // adjust unit support point for discontinuous node + Point point; + for (unsigned int d=0; dunit_support_points.push_back(point); + AssertDimension(this->dofs_per_cell, this->unit_support_points.size()); } @@ -538,48 +59,27 @@ FE_Q_DG0::FE_Q_DG0 (const unsigned int degree) template FE_Q_DG0::FE_Q_DG0 (const Quadrature<1> &points) : - FE_Poly, dim, spacedim> ( + FE_Q_Base, dim, spacedim> ( TensorProductPolynomialsConst(Polynomials::generate_complete_Lagrange_basis(points.get_points())), FiniteElementData(get_dpo_vector(points.size()-1), 1, points.size()-1, FiniteElementData::L2), - get_riaf_vector(points.size()-1), - std::vector(1, std::vector(1,true))), - - face_index_map(FE_Q_DG0_Helper::invert_numbering( - face_lexicographic_to_hierarchic_numbering (points.size()-1))) + get_riaf_vector(points.size()-1)) { const int degree = points.size()-1; Assert (degree > 0, ExcMessage ("This element can only be used for polynomial degrees " "at least zero")); - Assert (points.point(0)(0) == 0, - ExcMessage ("The first support point has to be zero.")); - Assert (points.point(degree)(0) == 1, - ExcMessage ("The last support point has to be one.")); - - std::vector renumber (this->dofs_per_cell-1); - const FiniteElementData fe(get_dpo_vector_q(degree),1,degree); - FETools::hierarchic_to_lexicographic_numbering (fe, renumber); - renumber.push_back(this->dofs_per_cell-1); - this->poly_space.set_numbering(renumber); - - // finally fill in support points - // on cell and face - initialize_unit_support_points (points); - initialize_unit_face_support_points (points); - // reinit constraints - Implementation::initialize_constraints (points, *this); + this->initialize(points.get_points()); - // Reinit the vectors of restriction and prolongation matrices to the - // right sizes and compute the matrices - this->reinit_restriction_and_prolongation_matrices(); - initialize_embedding(); - initialize_restriction(); - - initialize_quad_dof_index_permutation(); + // adjust unit support point for discontinuous node + Point point; + for (unsigned int d=0; dunit_support_points.push_back(point); + AssertDimension(this->dofs_per_cell, this->unit_support_points.size()); } @@ -588,12 +88,9 @@ template std::string FE_Q_DG0::get_name () const { - // note that the - // FETools::get_fe_from_name - // function depends on the - // particular format of the string - // this function returns, so they - // have to be kept in synch + // note that the FETools::get_fe_from_name function depends on the + // particular format of the string this function returns, so they have to be + // kept in synch std::ostringstream namebuf; bool type = true; @@ -603,8 +100,7 @@ FE_Q_DG0::get_name () const const std::vector > &unit_support_points = this->unit_support_points; unsigned int index = 0; - // Decode the support points - // in one coordinate direction. + // Decode the support points in one coordinate direction. for (unsigned int j=0; j1) ? (unit_support_points[j](1)==0 && @@ -620,12 +116,11 @@ FE_Q_DG0::get_name () const index++; } } - //Do not consider the discontinuous node for dimension 1 + // Do not consider the discontinuous node for dimension 1 Assert (index == n_points || (dim==1 && index == n_points+1), ExcMessage ("Could not decode support points in one coordinate direction.")); - // Check whether the support - // points are equidistant. + // Check whether the support points are equidistant. for (unsigned int j=0; jdegree) > 1e-15) { @@ -638,8 +133,7 @@ FE_Q_DG0::get_name () const else { - // Check whether the support - // points come from QGaussLobatto. + // Check whether the support points come from QGaussLobatto. const QGaussLobatto<1> points_gl(n_points); type = true; for (unsigned int j=0; j::clone() const return new FE_Q_DG0(*this); } + + template void FE_Q_DG0::interpolate(std::vector &local_dofs, @@ -679,10 +175,13 @@ FE_Q_DG0::interpolate(std::vector &local_dofs, ExcDimensionMismatch(this->n_components(), 1)); std::copy(values.begin(), values.end(), local_dofs.begin()); - //We don't need the discontinuous function for local interpolation - local_dofs[local_dofs.size()-1]=0.; + + // We don't need the discontinuous function for local interpolation + local_dofs[local_dofs.size()-1] = 0.; } + + template void FE_Q_DG0::interpolate(std::vector &local_dofs, @@ -703,10 +202,10 @@ FE_Q_DG0::interpolate(std::vector &local_dofs, = this->system_to_component_index(i); local_dofs[i] = values[i](offset+index.first); } - //We don't need the discontinuous function for local interpolation - local_dofs[local_dofs.size()-1]=0.; -} + // We don't need the discontinuous function for local interpolation + local_dofs[local_dofs.size()-1] = 0.; +} @@ -730,19 +229,20 @@ FE_Q_DG0::interpolate( = this->system_to_component_index(i); local_dofs[i] = values[index.first][i]; } - //We don't need the discontinuous function for local interpolation - local_dofs[local_dofs.size()-1]=0.; + + // We don't need the discontinuous function for local interpolation + local_dofs[local_dofs.size()-1] = 0.; } + + template void FE_Q_DG0:: get_interpolation_matrix (const FiniteElement &x_source_fe, FullMatrix &interpolation_matrix) const { - // this is only implemented, if the - // source FE is also a - // Q_DG0 element + // this is only implemented, if the source FE is also a Q_DG0 element typedef FE_Q_DG0 FEQDG0; typedef FiniteElement FEL; @@ -759,1395 +259,50 @@ get_interpolation_matrix (const FiniteElement &x_source_fe, ExcDimensionMismatch (interpolation_matrix.m(), x_source_fe.dofs_per_cell)); - // ok, source is a Q_DG0 element, so - // we will be able to do the work - const FE_Q_DG0 &source_fe - = dynamic_cast&>(x_source_fe); - - // compute the interpolation - // matrices in much the same way as - // we do for the embedding matrices - // from mother to child. - FullMatrix cell_interpolation (this->dofs_per_cell, - this->dofs_per_cell); - FullMatrix source_interpolation (this->dofs_per_cell, - source_fe.dofs_per_cell); - FullMatrix tmp (this->dofs_per_cell, - source_fe.dofs_per_cell); - for (unsigned int j=0; jdofs_per_cell-1; ++j) - { - // read in a point on this - // cell and evaluate the - // shape functions there - const Point p = this->unit_support_points[j]; - for (unsigned int i=0; idofs_per_cell-1; ++i) - cell_interpolation(j,i) = this->poly_space.compute_value (i, p); - - for (unsigned int i=0; idofs_per_cell-1, - this->dofs_per_cell-1)=1.; - source_interpolation(this->dofs_per_cell-1, - source_fe.dofs_per_cell-1)=1.; - - // then compute the - // interpolation matrix - // for this coordinate - // direction - cell_interpolation.gauss_jordan (); - cell_interpolation.mmult (interpolation_matrix, - source_interpolation); - - const double eps = 2e-13*this->degree*dim; - - // cut off very small values - for (unsigned int i=0; idofs_per_cell; ++i) - for (unsigned int j=0; jdofs_per_cell; ++i) - { - double sum = 0.; - for (unsigned int j=0; j -void -FE_Q_DG0<1>:: -get_face_interpolation_matrix (const FiniteElement<1,1> &/*x_source_fe*/, - FullMatrix &/*interpolation_matrix*/) const -{ - Assert (false, ExcImpossibleInDim(1)); -} - - - -template <> -void -FE_Q_DG0<1>:: -get_subface_interpolation_matrix (const FiniteElement<1,1> &/*x_source_fe*/, - const unsigned int /*subface*/, - FullMatrix &/*interpolation_matrix*/) const -{ - Assert (false, ExcImpossibleInDim(1)); -} - - -template <> -void -FE_Q_DG0<1,2>:: -get_face_interpolation_matrix (const FiniteElement<1,2> &/*x_source_fe*/, - FullMatrix &/*interpolation_matrix*/) const -{ - typedef FiniteElement<1,2> FEL; - Assert (false, - FEL:: - ExcInterpolationNotImplemented ()); -} - - -template <> -void -FE_Q_DG0<1,2>:: -get_subface_interpolation_matrix (const FiniteElement<1,2> &/*x_source_fe*/, - const unsigned int /*subface*/, - FullMatrix &/*interpolation_matrix*/) const -{ - typedef FiniteElement<1,2> FEL; - Assert (false, - FEL:: - ExcInterpolationNotImplemented ()); -} - - -template <> -void -FE_Q_DG0<1,3>:: -get_face_interpolation_matrix (const FiniteElement<1,3> &/*x_source_fe*/, - FullMatrix &/*interpolation_matrix*/) const -{ - typedef FiniteElement<1,3> FEL; - Assert (false, - FEL:: - ExcInterpolationNotImplemented ()); -} - - -template <> -void -FE_Q_DG0<1,3>:: -get_subface_interpolation_matrix (const FiniteElement<1,3> &/*x_source_fe*/, - const unsigned int /*subface*/, - FullMatrix &/*interpolation_matrix*/) const -{ - typedef FiniteElement<1,3> FEL; - Assert (false, - FEL:: - ExcInterpolationNotImplemented ()); + this->FE_Q_Base,dim,spacedim>:: + get_interpolation_matrix(x_source_fe, interpolation_matrix); } template -void -FE_Q_DG0:: -get_face_interpolation_matrix (const FiniteElement &x_source_fe, - FullMatrix &interpolation_matrix) const +std::vector +FE_Q_DG0::get_riaf_vector(const unsigned int deg) { - // this is only implemented, if the - // source FE is also a - // Q_DG0 element - typedef FE_Q_DG0 FEQDG0; - typedef FiniteElement FEL; - AssertThrow ((x_source_fe.get_name().find ("FE_Q_DG0<") == 0) - || - (dynamic_cast(&x_source_fe) != 0), - typename FEL:: - ExcInterpolationNotImplemented()); - - Assert (interpolation_matrix.n() == this->dofs_per_face, - ExcDimensionMismatch (interpolation_matrix.n(), - this->dofs_per_face)); - Assert (interpolation_matrix.m() == x_source_fe.dofs_per_face, - ExcDimensionMismatch (interpolation_matrix.m(), - x_source_fe.dofs_per_face)); - - // ok, source is a Q_DG0 element, so - // we will be able to do the work - const FE_Q_DG0 &source_fe - = dynamic_cast&>(x_source_fe); - - // Make sure, that the element, - // for which the DoFs should be - // constrained is the one with - // the higher polynomial degree. - // Actually the procedure will work - // also if this assertion is not - // satisfied. But the matrices - // produced in that case might - // lead to problems in the - // hp procedures, which use this - // method. - Assert (this->dofs_per_face <= source_fe.dofs_per_face, - typename FEL:: - ExcInterpolationNotImplemented ()); - - // generate a quadrature - // with the unit support points. - // This is later based as a - // basis for the QProjector, - // which returns the support - // points on the face. - Quadrature quad_face_support (source_fe.get_unit_face_support_points ()); - - // Rule of thumb for FP accuracy, - // that can be expected for a - // given polynomial degree. - // This value is used to cut - // off values close to zero. - const double eps = 2e-13*this->degree*(dim-1); - - // compute the interpolation - // matrix by simply taking the - // value at the support points. -//TODO: Verify that all faces are the same with respect to -// these support points. Furthermore, check if something has to -// be done for the face orientation flag in 3D. - const Quadrature face_quadrature - = QProjector::project_to_face (quad_face_support, 0); - for (unsigned int i=0; i &p = face_quadrature.point (i); - - for (unsigned int j=0; jdofs_per_face; ++j) - { - double matrix_entry = this->shape_value (this->face_to_cell_index(j, 0), p); - - // Correct the interpolated - // value. I.e. if it is close - // to 1 or 0, make it exactly - // 1 or 0. Unfortunately, this - // is required to avoid problems - // with higher order elements. - if (std::fabs (matrix_entry - 1.0) < eps) - matrix_entry = 1.0; - if (std::fabs (matrix_entry) < eps) - matrix_entry = 0.0; - - interpolation_matrix(i,j) = matrix_entry; - } - } - - // make sure that the row sum of - // each of the matrices is 1 at - // this point. this must be so - // since the shape functions sum up - // to 1 - for (unsigned int j=0; jdofs_per_face; ++i) - sum += interpolation_matrix(j,i); - - Assert (std::fabs(sum-1) < 2e-13*this->degree*(dim-1), - ExcInternalError()); - } + std::vector riaf(Utilities::fixed_power(deg+1)+1,false); + riaf[riaf.size()-1]=true; + return riaf; } template -void -FE_Q_DG0:: -get_subface_interpolation_matrix (const FiniteElement &x_source_fe, - const unsigned int subface, - FullMatrix &interpolation_matrix) const +std::vector +FE_Q_DG0::get_dpo_vector(const unsigned int deg) { - Assert (interpolation_matrix.m() == x_source_fe.dofs_per_face, - ExcDimensionMismatch (interpolation_matrix.m(), - x_source_fe.dofs_per_face)); - - // see if source is a Q_DG0 element - if (const FE_Q_DG0 *source_fe - = dynamic_cast *>(&x_source_fe)) - { - // have this test in here since - // a table of size 2x0 reports - // its size as 0x0 - Assert (interpolation_matrix.n() == this->dofs_per_face, - ExcDimensionMismatch (interpolation_matrix.n(), - this->dofs_per_face)); - - // Make sure, that the element, - // for which the DoFs should be - // constrained is the one with - // the higher polynomial degree. - // Actually the procedure will work - // also if this assertion is not - // satisfied. But the matrices - // produced in that case might - // lead to problems in the - // hp procedures, which use this - // method. - Assert (this->dofs_per_face <= source_fe->dofs_per_face, - (typename FiniteElement:: - ExcInterpolationNotImplemented ())); - - // generate a point on this - // cell and evaluate the - // shape functions there - const Quadrature - quad_face_support (source_fe->get_unit_face_support_points ()); - - // Rule of thumb for FP accuracy, - // that can be expected for a - // given polynomial degree. - // This value is used to cut - // off values close to zero. - double eps = 2e-13*this->degree*(dim-1); - - // compute the interpolation - // matrix by simply taking the - // value at the support points. -//TODO: Verify that all faces are the same with respect to -// these support points. Furthermore, check if something has to -// be done for the face orientation flag in 3D. - const Quadrature subface_quadrature - = QProjector::project_to_subface (quad_face_support, 0, subface); - for (unsigned int i=0; idofs_per_face; ++i) - { - const Point &p = subface_quadrature.point (i); - - for (unsigned int j=0; jdofs_per_face; ++j) - { - double matrix_entry = this->shape_value (this->face_to_cell_index(j, 0), p); - - // Correct the interpolated - // value. I.e. if it is close - // to 1 or 0, make it exactly - // 1 or 0. Unfortunately, this - // is required to avoid problems - // with higher order elements. - if (std::fabs (matrix_entry - 1.0) < eps) - matrix_entry = 1.0; - if (std::fabs (matrix_entry) < eps) - matrix_entry = 0.0; - - interpolation_matrix(i,j) = matrix_entry; - } - } - - // make sure that the row sum of - // each of the matrices is 1 at - // this point. this must be so - // since the shape functions sum up - // to 1 - for (unsigned int j=0; jdofs_per_face; ++j) - { - double sum = 0.; - - for (unsigned int i=0; idofs_per_face; ++i) - sum += interpolation_matrix(j,i); + std::vector dpo(dim+1, 1U); + for (unsigned int i=1; idegree*dim, - ExcInternalError()); - } - } - else if (dynamic_cast *>(&x_source_fe) != 0) - { - // nothing to do here, the - // FE_Nothing has no degrees of - // freedom anyway - } - else - AssertThrow (false, - (typename FiniteElement:: - ExcInterpolationNotImplemented())); + dpo[dim]++;//we need an additional DG0-node for a dim-dimensional object + return dpo; } template bool -FE_Q_DG0::hp_constraints_are_implemented () const -{ - return true; -} - - - - -template -std::vector > -FE_Q_DG0:: -hp_vertex_dof_identities (const FiniteElement &fe_other) const -{ - // we can presently only compute - // these identities if both FEs are - // FE_Q_DG0s or if the other one is an - // FE_Nothing. in the first case, - // there should be exactly one - // single DoF of each FE at a - // vertex, and they should have - // identical value - if (dynamic_cast*>(&fe_other) != 0) - { - return - std::vector > - (1, std::make_pair (0U, 0U)); - } - else if (dynamic_cast*>(&fe_other) != 0) - { - // the FE_Nothing has no - // degrees of freedom, so there - // are no equivalencies to be - // recorded - return std::vector > (); - } - else - { - Assert (false, ExcNotImplemented()); - return std::vector > (); - } -} - - - -template -std::vector > -FE_Q_DG0:: -hp_line_dof_identities (const FiniteElement &fe_other) const -{ - // we can presently only compute - // these identities if both FEs are - // FE_Q_DG0s or if the other one is an - // FE_Nothing - if (const FE_Q_DG0 *fe_q_dg0_other = dynamic_cast *>(&fe_other)) - { - // dofs are located along lines, so two - // dofs are identical if they are - // located at identical positions. if - // we had only equidistant points, we - // could simple check for similarity - // like (i+1)*q == (j+1)*p, but we - // might have other support points - // (e.g. Gauss-Lobatto - // points). Therefore, read the points - // in unit_support_points for the first - // coordinate direction. We take the - // lexicographic ordering of the points - // in the first direction (i.e., - // x-direction), which we access - // between index 1 and p-1 (index 0 and - // p are vertex dofs). - const unsigned int p = this->degree; - const unsigned int q = fe_q_dg0_other->degree; - - std::vector > identities; - - const std::vector &index_map_inverse= - this->poly_space.get_numbering_inverse(); - const std::vector &index_map_inverse_other= - fe_q_dg0_other->poly_space.get_numbering_inverse(); - - for (unsigned int i=0; iunit_support_points[index_map_inverse[i+1]][0]- - - fe_q_dg0_other->unit_support_points[index_map_inverse_other[j+1]][0]) - < 1e-14) - identities.push_back (std::make_pair(i,j)); - - return identities; - } - else if (dynamic_cast*>(&fe_other) != 0) - { - // the FE_Nothing has no - // degrees of freedom, so there - // are no equivalencies to be - // recorded - return std::vector > (); - } - else - { - Assert (false, ExcNotImplemented()); - return std::vector > (); - } -} - - - -template -std::vector > -FE_Q_DG0:: -hp_quad_dof_identities (const FiniteElement &fe_other) const +FE_Q_DG0::has_support_on_face (const unsigned int shape_index, + const unsigned int face_index) const { - // we can presently only compute - // these identities if both FEs are - // FE_Q_DG0s or if the other one is an - // FE_Nothing - if (const FE_Q_DG0 *fe_q_dg0_other = dynamic_cast *>(&fe_other)) - { - // this works exactly like the line - // case above, except that now we have - // to have two indices i1, i2 and j1, - // j2 to characterize the dofs on the - // face of each of the finite - // elements. since they are ordered - // lexicographically along the first - // line and we have a tensor product, - // the rest is rather straightforward - const unsigned int p = this->degree; - const unsigned int q = fe_q_dg0_other->degree; - - std::vector > identities; - - const std::vector &index_map_inverse= - this->poly_space.get_numbering_inverse(); - const std::vector &index_map_inverse_other= - fe_q_dg0_other->poly_space.get_numbering_inverse(); - - for (unsigned int i1=0; i1unit_support_points[index_map_inverse[i1+1]][0]- - - fe_q_dg0_other->unit_support_points[index_map_inverse_other[j1+1]][0]) - < 1e-14) - && - (std::fabs(this->unit_support_points[index_map_inverse[i2+1]][0]- - - fe_q_dg0_other->unit_support_points[index_map_inverse_other[j2+1]][0]) - < 1e-14)) - identities.push_back (std::make_pair(i1*(p-1)+i2, - j1*(q-1)+j2)); - - return identities; - } - else if (dynamic_cast*>(&fe_other) != 0) - { - // the FE_Nothing has no - // degrees of freedom, so there - // are no equivalencies to be - // recorded - return std::vector > (); - } + // discontinuous function has support on all faces + if (shape_index == this->dofs_per_cell-1) + return true; else - { - Assert (false, ExcNotImplemented()); - return std::vector > (); - } + return FE_Q_Base,dim,spacedim>::has_support_on_face(shape_index, face_index); } - -template -FiniteElementDomination::Domination -FE_Q_DG0:: -compare_for_face_domination (const FiniteElement &fe_other) const -{ - if (const FE_Q_DG0 *fe_q_dg0_other - = dynamic_cast*>(&fe_other)) - { - if (this->degree < fe_q_dg0_other->degree) - return FiniteElementDomination::this_element_dominates; - else if (this->degree == fe_q_dg0_other->degree) - return FiniteElementDomination::either_element_can_dominate; - else - return FiniteElementDomination::other_element_dominates; - } - else if (dynamic_cast*>(&fe_other) != 0) - { - // the FE_Nothing has no - // degrees of freedom and it is - // typically used in a context - // where we don't require any - // continuity along the - // interface - return FiniteElementDomination::no_requirements; - } - - Assert (false, ExcNotImplemented()); - return FiniteElementDomination::neither_element_dominates; -} - - -//--------------------------------------------------------------------------- -// Auxiliary functions -//--------------------------------------------------------------------------- - - - -template -void FE_Q_DG0::initialize_unit_support_points () -{ - // number of points: (degree+1)^dim+1 - unsigned int n = this->degree+1; - for (unsigned int i=1; idegree+1; - n++; - - this->unit_support_points.resize(n); - - const std::vector &index_map_inverse= - this->poly_space.get_numbering_inverse(); - - const double step = 1./this->degree; - Point p; - - unsigned int k=0; - for (unsigned int iz=0; iz <= ((dim>2) ? this->degree : 0) ; ++iz) - for (unsigned int iy=0; iy <= ((dim>1) ? this->degree : 0) ; ++iy) - for (unsigned int ix=0; ix<=this->degree; ++ix) - { - p(0) = ix * step; - if (dim>1) - p(1) = iy * step; - if (dim>2) - p(2) = iz * step; - - this->unit_support_points[index_map_inverse[k++]] = p; - } - //dg0 support point in the center of the cell - p(0) = .5; - if (dim>1) - { - p(1) = .5; - if (dim>2) - p(2) = .5; - } - - this->unit_support_points[index_map_inverse[k]] = p; -} - - - -template -void FE_Q_DG0::initialize_unit_support_points (const Quadrature<1> &points) -{ - // number of points: (degree+1)^dim+1 - unsigned int n = this->degree+1; - for (unsigned int i=1; idegree+1; - n++; - - this->unit_support_points.resize(n); - - const std::vector &index_map_inverse= - this->poly_space.get_numbering_inverse(); - - Quadrature support_quadrature(points); - - Point p; - - for (unsigned int k=0; kunit_support_points[index_map_inverse[k]] = support_quadrature.point(k); - } -} - - - -template <> -void FE_Q_DG0<1>::initialize_unit_face_support_points () -{ - // no faces in 1d, so nothing to do -} - -template <> -void FE_Q_DG0<1>::initialize_unit_face_support_points (const Quadrature<1> &/*points*/) -{ - // no faces in 1d, so nothing to do -} - -template <> -void FE_Q_DG0<1,2>::initialize_unit_face_support_points () -{ - // no faces in 1d, so nothing to do -} - -template <> -void FE_Q_DG0<1,2>::initialize_unit_face_support_points (const Quadrature<1> &/*points*/) -{ - // no faces in 1d, so nothing to do -} - -template <> -void FE_Q_DG0<1,3>::initialize_unit_face_support_points () -{ - // no faces in 1d, so nothing to do -} - -template <> -void FE_Q_DG0<1,3>::initialize_unit_face_support_points (const Quadrature<1> &/*points*/) -{ - // no faces in 1d, so nothing to do -} - -template -void FE_Q_DG0::initialize_unit_face_support_points () -{ - const unsigned int codim = dim-1; - - // number of points: (degree+1)^codim - unsigned int n = this->degree+1; - for (unsigned int i=1; idegree+1; - - this->unit_face_support_points.resize(n); - - const std::vector &face_index_map_inverse= - FE_Q_DG0_Helper::invert_numbering(face_index_map); - - const double step = 1./this->degree; - Point p; - - unsigned int k=0; - for (unsigned int iz=0; iz <= ((codim>2) ? this->degree : 0) ; ++iz) - for (unsigned int iy=0; iy <= ((codim>1) ? this->degree : 0) ; ++iy) - for (unsigned int ix=0; ix<=this->degree; ++ix) - { - p(0) = ix * step; - if (codim>1) - p(1) = iy * step; - if (codim>2) - p(2) = iz * step; - - this->unit_face_support_points[face_index_map_inverse[k++]] = p; - } -} - - - -template -void FE_Q_DG0::initialize_unit_face_support_points (const Quadrature<1> &points) -{ - const unsigned int codim = dim-1; - - // number of points: (degree+1)^codim - unsigned int n = this->degree+1; - for (unsigned int i=1; idegree+1; - - this->unit_face_support_points.resize(n); - - const std::vector< Point<1> > edge = points.get_points(); - - const std::vector &face_index_map_inverse= - FE_Q_DG0_Helper::invert_numbering(face_index_map); - - Point p; - - unsigned int k=0; - for (unsigned int iz=0; iz <= ((codim>2) ? this->degree : 0) ; ++iz) - for (unsigned int iy=0; iy <= ((codim>1) ? this->degree : 0) ; ++iy) - for (unsigned int ix=0; ix<=this->degree; ++ix) - { - p(0) = edge[ix](0); - if (codim>1) - p(1) = edge[iy](0); - if (codim>2) - p(2) = edge[iz](0); - - this->unit_face_support_points[face_index_map_inverse[k++]] = p; - } -} - - - -template -void -FE_Q_DG0::initialize_quad_dof_index_permutation () -{ - // general template for 1D and 2D, do nothing -} - - - -template <> -void -FE_Q_DG0<3>::initialize_quad_dof_index_permutation () -{ - Assert (adjust_quad_dof_index_for_face_orientation_table.n_elements()==8*this->dofs_per_quad, - ExcInternalError()); - - const unsigned int n=this->degree-1; - Assert(n*n==this->dofs_per_quad, ExcInternalError()); - - // alias for the table to fill - Table<2,int> &data=this->adjust_quad_dof_index_for_face_orientation_table; - - // the dofs on a face are connected to a n x - // n matrix. for example, for degree==4 we - // have the following dofs on a quad - - // ___________ - // | | - // | 6 7 8 | - // | | - // | 3 4 5 | - // | | - // | 0 1 2 | - // |___________| - // - // we have dof_no=i+n*j with index i in - // x-direction and index j in y-direction - // running from 0 to n-1. to extract i and j - // we can use i=dof_no%n and j=dof_no/n. i - // and j can then be used to construct the - // rotated and mirrored numbers. - - - for (unsigned int local=0; localdofs_per_quad; ++local) - // face support points are in lexicographic - // ordering with x running fastest. invert - // that (y running fastest) - { - unsigned int i=local%n, - j=local/n; - - // face_orientation=false, face_flip=false, face_rotation=false - data(local,0)=j + i *n - local; - // face_orientation=false, face_flip=false, face_rotation=true - data(local,1)=i + (n-1-j)*n - local; - // face_orientation=false, face_flip=true, face_rotation=false - data(local,2)=(n-1-j) + (n-1-i)*n - local; - // face_orientation=false, face_flip=true, face_rotation=true - data(local,3)=(n-1-i) + j *n - local; - // face_orientation=true, face_flip=false, face_rotation=false - data(local,4)=0; - // face_orientation=true, face_flip=false, face_rotation=true - data(local,5)=j + (n-1-i)*n - local; - // face_orientation=true, face_flip=true, face_rotation=false - data(local,6)=(n-1-i) + (n-1-j)*n - local; - // face_orientation=true, face_flip=true, face_rotation=true - data(local,7)=(n-1-j) + i *n - local; - } - - // aditionally initialize reordering of line - // dofs - for (unsigned int i=0; idofs_per_line; ++i) - this->adjust_line_dof_index_for_line_orientation_table[i]=this->dofs_per_line-1-i - i; -} - -template -std::vector -FE_Q_DG0::get_riaf_vector(const unsigned int deg) -{ - std::vector riaf - (FiniteElementData (get_dpo_vector(deg),1,deg).dofs_per_cell, - false); - riaf[riaf.size()-1]=true; - return riaf; -} - - -template -std::vector -FE_Q_DG0::get_dpo_vector(const unsigned int deg) -{ - std::vector dpo(dim+1, 1U); - for (unsigned int i=1; i -std::vector -FE_Q_DG0::get_dpo_vector_q(const unsigned int deg) -{ - std::vector dpo(dim+1, 1U); - for (unsigned int i=1; i -std::vector -FE_Q_DG0::face_lexicographic_to_hierarchic_numbering (const unsigned int degree) -{ - const FiniteElementData face_data(FE_Q_DG0::get_dpo_vector_q(degree),1,degree); - std::vector face_renumber (face_data.dofs_per_cell); - FETools::lexicographic_to_hierarchic_numbering (face_data, face_renumber); - return face_renumber; -} - - - -template <> -std::vector -FE_Q_DG0<1>::face_lexicographic_to_hierarchic_numbering (const unsigned int) -{ - return std::vector(); -} - -template <> -std::vector -FE_Q_DG0<1,2>::face_lexicographic_to_hierarchic_numbering (const unsigned int) -{ - return std::vector(); -} - - -template <> -std::vector -FE_Q_DG0<1,3>::face_lexicographic_to_hierarchic_numbering (const unsigned int) -{ - return std::vector(); -} - - -template -void -FE_Q_DG0::initialize_constraints () -{ - QTrapez<1> trapez; - QIterated<1> points (trapez,this->degree); - Implementation::initialize_constraints (points, *this); -} - - -template -void -FE_Q_DG0::initialize_embedding () -{ - // compute the interpolation matrices in much the same way as we do for - // the constraints. it's actually simpler here, since we don't have this - // weird renumbering stuff going on. The trick is again that the - // interpolation matrix is formed by a permutation of the indices of the - // cell matrix. The value eps is used a threshold to decide when certain - // evaluations of the Lagrange polynomials are zero or one. - const double eps = 1e-15*this->degree*dim; - -#ifdef DEBUG - // in DEBUG mode, check that the evaluation of support points (except for - // the discontinuous node) in the current numbering gives the identity - // operation - for (unsigned int i=0; idofs_per_cell-1; ++i) - { - Assert (std::fabs (1.-this->poly_space.compute_value - (i, this->unit_support_points[i])) < eps, - ExcInternalError()); - for (unsigned int j=0; jdofs_per_cell-1; ++j) - if (j!=i) - Assert (std::fabs (this->poly_space.compute_value - (i, this->unit_support_points[j])) < eps, - ExcInternalError()); - } -#endif - - // to efficiently evaluate the polynomial at the subcell, make use of the - // tensor product structure of this element and only evaluate 1D - // information from the polynomial. This makes the cost of this function - // almost negligible also for high order elements - const unsigned int dofs1d = this->degree+1; - std::vector > subcell_evaluations - (dim, Table<2,double>(dofs1d, dofs1d)); - const std::vector &index_map_inverse = - this->poly_space.get_numbering_inverse(); - - // recreate 1D polynomials, the last entry is not used - std::vector > poly_space1d = - FE_Q_DG0_Helper::generate_poly_space1d (this->unit_support_points, - index_map_inverse, dofs1d); - - // helper value: step size how to walk through diagonal and how many - // points we have left apart from the first dimension - unsigned int step_size_diag = 0; - { - unsigned int factor = 1; - for (unsigned int d=0; d::isotropic_refinement; ++ref) - for (unsigned int child=0; - child::n_children(RefinementCase(ref+1)); - ++child) - { - // go through the points in diagonal to capture variation in all - // directions simultaneously - for (unsigned int j=0; j p_subcell = this->unit_support_points[diag_comp]; - const Point p_cell = - GeometryInfo::child_to_cell_coordinates - (p_subcell, child, RefinementCase(ref+1)); - for (unsigned int i=0; i (j_indices); - for (unsigned int j=0; jdofs_per_cell-1; j+=dofs1d) - { - unsigned int i_indices[dim]; - FE_Q_DG0_Helper::zero_indices (i_indices); - for (unsigned int i=0; idofs_per_cell-1; i+=dofs1d) - { - double val_extra_dim = 1.; - for (unsigned int d=1; dprolongation[ref][child](j,i) = - // this->poly_space.compute_value (i, p_cell); - for (unsigned int jj=0; jjprolongation[ref][child](j_ind,index_map_inverse[i+ii]) - = val_extra_dim * subcell_evaluations[0](jj,ii); - } - - // update indices that denote the tensor product position. a bit - // fuzzy and therefore not done for innermost x_0 direction - FE_Q_DG0_Helper::increment_indices (i_indices, dofs1d); - } - Assert (i_indices[dim-1] == 1, ExcInternalError()); - FE_Q_DG0_Helper::increment_indices (j_indices, dofs1d); - } - - // the discontinuous node is simply mapped on the discontinuous node - // on the child cell - for (unsigned int j=0; jdofs_per_cell-1; j++) - { - this->prolongation[ref][child](j,this->dofs_per_cell-1) = 0.; - this->prolongation[ref][child](this->dofs_per_cell-1,j) = 0.; - } - this->prolongation[ref][child](this->dofs_per_cell-1, - this->dofs_per_cell-1) = 1.; - - // make sure that the row sum is one. this must be so since for this - // element, the continuous shape functions add up to one and the - // discontinuous node is mapped to the discontinuous node on the - // child cell -#ifdef DEBUG - for (unsigned int row=0; rowdofs_per_cell; ++row) - { - double sum = 0; - for (unsigned int col=0; coldofs_per_cell; ++col) - sum += this->prolongation[ref][child](row,col); - Assert (std::fabs(sum-1.) < eps, ExcInternalError()); - } -#endif - } -} - - - -template -void -FE_Q_DG0::initialize_restriction () -{ - // for these Lagrange interpolation polynomials, construction of the - // restriction matrices is relatively simple. the reason is that the - // interpolation points on the mother cell are (except for the case with - // arbitrary nonequidistant nodes and for the discontinuous node) always also - // interpolation points for some shape function on one or the other child, - // because we have chosen equidistant Lagrange interpolation points for the - // polynomials - // - // so the only thing we have to find out is: for each shape function on - // the mother cell, which is the child cell (possibly more than one) on - // which it is located, and which is the corresponding shape function there. - // rather than doing it for the shape functions on the mother cell, we take - // the interpolation points there - // - // note that the interpolation point of a shape function can be on the - // boundary between subcells. in that case, restriction from children to - // mother may produce two or more entries for a dof on the mother cell. - // however, this doesn't hurt: since the element is continuous (apart from - // the discontinuous node), the contribution from each child should yield - // the same result, and since the element (apart from the discontinuous - // node) is non-additive we just overwrite one value (compute one one child) - // by the same value (compute on a later child), so we don't have to care - // about this. - // for the discontinuous node we just take the mean of all the child cells' - // contributions. - - const double eps = 1e-15*this->degree*dim; - const std::vector &index_map_inverse = - this->poly_space.get_numbering_inverse(); - - // recreate 1D polynomials for faster evaluation of polynomial, the last - // entry is not used - const unsigned int dofs1d = this->degree+1; - std::vector > poly_space1d = - FE_Q_DG0_Helper::generate_poly_space1d (this->unit_support_points, - index_map_inverse, dofs1d); - std::vector > evaluations1d (dofs1d); - - for (unsigned int i=0; idofs_per_cell-1; ++i) - { - unsigned int mother_dof = index_map_inverse[i]; - const Point p_cell = this->unit_support_points[mother_dof]; - - // then find the children on which the interpolation point is located - for (unsigned int ref=RefinementCase::cut_x; - ref<=RefinementCase::isotropic_refinement; ++ref) - for (unsigned int child=0; - child::n_children(RefinementCase(ref)); - ++child) - { - // check whether this interpolation point is inside this child - // cell - const Point p_subcell = - GeometryInfo::cell_to_child_coordinates - (p_cell, child, RefinementCase(ref)); - if (GeometryInfo::is_inside_unit_cell (p_subcell)) - { - // same logic as in initialize_embedding to evaluate the - // polynomial faster than from the tensor product: since we - // evaluate all polynomials, it is much faster to just - // compute the 1D values for all polynomials before and then - // get the dim-data. - for (unsigned int j=0; j (j_indices); - double sum_check = 0; - for (unsigned int j = 0; jdofs_per_cell-1; j += dofs1d) - { - double val_extra_dim = 1.; - for (unsigned int d=1; drestriction[ref-1][child] - (mother_dof,child_dof)=1.; - else if (std::fabs(val) > eps) - this->restriction[ref-1][child] - (mother_dof,child_dof)=val; - sum_check += val; - } - FE_Q_DG0_Helper::increment_indices (j_indices, dofs1d); - } - Assert (std::fabs(sum_check-1) < eps, - ExcInternalError()); - } - } - } - // for the discontinuous node we take the mean of all the child cells' - // contributions. so there is also a diagonal entry equal to the inverse of - // n_children. - for (unsigned int ref=RefinementCase::cut_x; - ref<=RefinementCase::isotropic_refinement; ++ref) - for (unsigned int child=0; - child::n_children(RefinementCase(ref)); - ++child) - { - for (unsigned int i=0; idofs_per_cell-1; ++i) - { - this->restriction[ref-1][child](i,this->dofs_per_cell-1)=0.; - this->restriction[ref-1][child](this->dofs_per_cell-1,i)=0.; - } - this->restriction[ref-1][child] - (this->dofs_per_cell-1,this->dofs_per_cell-1) = - 1./(double)GeometryInfo::n_children - (RefinementCase(ref)); - } - -} - - - -//--------------------------------------------------------------------------- -// Data field initialization -//--------------------------------------------------------------------------- - - -template <> -bool -FE_Q_DG0<1>::has_support_on_face (const unsigned int shape_index, - const unsigned int face_index) const -{ - Assert (shape_index < this->dofs_per_cell, - ExcIndexRange (shape_index, 0, this->dofs_per_cell)); - Assert (face_index < GeometryInfo<1>::faces_per_cell, - ExcIndexRange (face_index, 0, GeometryInfo<1>::faces_per_cell)); - - - // in 1d, things are simple. since - // there is only one degree of - // freedom per vertex in this - // class, the first is on vertex 0 - // (==face 0 in some sense), the - // second on face 1: - return (((shape_index == 0) && (face_index == 0)) || - ((shape_index == 1) && (face_index == 1))); -} - - -template <> -bool -FE_Q_DG0<1,2>::has_support_on_face (const unsigned int shape_index, - const unsigned int face_index) const -{ - Assert (shape_index < this->dofs_per_cell, - ExcIndexRange (shape_index, 0, this->dofs_per_cell)); - Assert (face_index < GeometryInfo<1>::faces_per_cell, - ExcIndexRange (face_index, 0, GeometryInfo<1>::faces_per_cell)); - - - // in 1d, things are simple. since - // there is only one degree of - // freedom per vertex in this - // class, the first is on vertex 0 - // (==face 0 in some sense), the - // second on face 1: - return (((shape_index == 0) && (face_index == 0)) || - ((shape_index == 1) && (face_index == 1))); -} - -template <> -bool -FE_Q_DG0<1,3>::has_support_on_face (const unsigned int shape_index, - const unsigned int face_index) const -{ - Assert (shape_index < this->dofs_per_cell, - ExcIndexRange (shape_index, 0, this->dofs_per_cell)); - Assert (face_index < GeometryInfo<1>::faces_per_cell, - ExcIndexRange (face_index, 0, GeometryInfo<1>::faces_per_cell)); - - - // in 1d, things are simple. since - // there is only one degree of - // freedom per vertex in this - // class, the first is on vertex 0 - // (==face 0 in some sense), the - // second on face 1: - return (((shape_index == 0) && (face_index == 0)) || - ((shape_index == 1) && (face_index == 1))); -} - - -template -bool -FE_Q_DG0::has_support_on_face (const unsigned int shape_index, - const unsigned int face_index) const -{ - Assert (shape_index < this->dofs_per_cell, - ExcIndexRange (shape_index, 0, this->dofs_per_cell)); - Assert (face_index < GeometryInfo::faces_per_cell, - ExcIndexRange (face_index, 0, GeometryInfo::faces_per_cell)); - - - // first, special-case interior - // shape functions, since they - // have no support no-where on - // the boundary - if (((dim==2) && (shape_index>=this->first_quad_index)) - || - ((dim==3) && (shape_index>=this->first_hex_index))) - return false; - - // let's see whether this is a - // vertex - if (shape_index < this->first_line_index) - { - // for Q_DG0 elements, there is - // one dof per vertex, so - // shape_index==vertex_number. check - // whether this vertex is - // on the given face. thus, - // for each face, give a - // list of vertices - const unsigned int vertex_no = shape_index; - Assert (vertex_no < GeometryInfo::vertices_per_cell, - ExcInternalError()); - - for (unsigned int v=0; v::vertices_per_face; ++v) - if (GeometryInfo::face_to_cell_vertices(face_index, v) == vertex_no) - return true; - - return false; - } - else if (shape_index < this->first_quad_index) - // ok, dof is on a line - { - const unsigned int line_index - = (shape_index - this->first_line_index) / this->dofs_per_line; - Assert (line_index < GeometryInfo::lines_per_cell, - ExcInternalError()); - - // in 2d, the line is the - // face, so get the line - // index - if (dim == 2) - return (line_index == face_index); - else if (dim == 3) - { - // see whether the - // given line is on the - // given face. - for (unsigned int l=0; l::lines_per_face; ++l) - if (GeometryInfo<3>::face_to_cell_lines(face_index, l) == line_index) - return true; - - return false; - } - else - Assert (false, ExcNotImplemented()); - } - else if (shape_index < this->first_hex_index) - // dof is on a quad - { - const unsigned int quad_index - = (shape_index - this->first_quad_index) / this->dofs_per_quad; - Assert (static_cast(quad_index) < - static_cast(GeometryInfo::quads_per_cell), - ExcInternalError()); - - // in 2d, cell bubble are - // zero on all faces. but - // we have treated this - // case above already - Assert (dim != 2, ExcInternalError()); - - // in 3d, - // quad_index=face_index - if (dim == 3) - return (quad_index == face_index); - else - Assert (false, ExcNotImplemented()); - } - else - // dof on hex - { - // can only happen in 3d, - // but this case has - // already been covered - // above - Assert (false, ExcNotImplemented()); - return false; - } - - // we should not have gotten here - Assert (false, ExcInternalError()); - return false; -} - - - -template -std::size_t -FE_Q_DG0::memory_consumption () const -{ - return (FiniteElement::memory_consumption() + - MemoryConsumption::memory_consumption (face_index_map)); -} - - - // explicit instantiations #include "fe_q_dg0.inst" diff --git a/deal.II/source/fe/fe_q_iso_q1.cc b/deal.II/source/fe/fe_q_iso_q1.cc new file mode 100644 index 0000000000..925e4c7c45 --- /dev/null +++ b/deal.II/source/fe/fe_q_iso_q1.cc @@ -0,0 +1,111 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//--------------------------------------------------------------------------- + +#include +#include +#include + +#include +#include + +DEAL_II_NAMESPACE_OPEN + + + + + +template +FE_Q_iso_Q1::FE_Q_iso_Q1 (const unsigned int subdivisions) + : + FE_Q_Base >, dim, spacedim> ( + TensorProductPolynomials > + (Polynomials::generate_complete_Lagrange_basis_on_subdivisions(subdivisions, 1)), + FiniteElementData(this->get_dpo_vector(subdivisions), + 1, subdivisions, + FiniteElementData::H1), + std::vector (1, false)) +{ + Assert (subdivisions > 0, + ExcMessage ("This element can only be used with a positive number of " + "subelements")); + + QTrapez<1> trapez; + QIterated<1> points (trapez, subdivisions); + + this->initialize(points.get_points()); +} + + + +template +std::string +FE_Q_iso_Q1::get_name () const +{ + // note that the FETools::get_fe_from_name function depends on the + // particular format of the string this function returns, so they have to be + // kept in synch + + std::ostringstream namebuf; + namebuf << "FE_Q_iso_Q1<" << dim << ">(" << this->degree << ")"; + return namebuf.str(); +} + + + +template +FiniteElement * +FE_Q_iso_Q1::clone() const +{ + return new FE_Q_iso_Q1(*this); +} + + + +template +FiniteElementDomination::Domination +FE_Q_iso_Q1:: +compare_for_face_domination (const FiniteElement &fe_other) const +{ + if (const FE_Q_iso_Q1 *fe_q_iso_q1_other + = dynamic_cast*>(&fe_other)) + { + // different behavior as in FE_Q: as FE_Q_iso_Q1(2) is not a subspace of + // FE_Q_iso_Q1(3), need that the element degrees are multiples of each + // other + if (this->degree < fe_q_iso_q1_other->degree && + fe_q_iso_q1_other->degree % this->degree == 0) + return FiniteElementDomination::this_element_dominates; + else if (this->degree == fe_q_iso_q1_other->degree) + return FiniteElementDomination::either_element_can_dominate; + else if (this->degree > fe_q_iso_q1_other->degree && + this->degree % fe_q_iso_q1_other->degree == 0) + return FiniteElementDomination::other_element_dominates; + else + return FiniteElementDomination::neither_element_dominates; + } + else if (dynamic_cast*>(&fe_other) != 0) + { + // the FE_Nothing has no degrees of freedom and it is typically used in + // a context where we don't require any continuity along the interface + return FiniteElementDomination::no_requirements; + } + + Assert (false, ExcNotImplemented()); + return FiniteElementDomination::neither_element_dominates; +} + + +// explicit instantiations +#include "fe_q_iso_q1.inst" + +DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/source/fe/fe_q_iso_q1.inst.in b/deal.II/source/fe/fe_q_iso_q1.inst.in new file mode 100644 index 0000000000..785a122589 --- /dev/null +++ b/deal.II/source/fe/fe_q_iso_q1.inst.in @@ -0,0 +1,22 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2013 by the deal.II authors +// +// This file is subject to QPL and may not be distributed +// without copyright and license information. Please refer +// to the file deal.II/doc/license.html for the text and +// further information on this license. +// +//--------------------------------------------------------------------------- + + +for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS) + { +#if deal_II_dimension <= deal_II_space_dimension + template class FE_Q_iso_Q1; +#endif + } + + diff --git a/deal.II/source/fe/fe_system.cc b/deal.II/source/fe/fe_system.cc index e8d2e75cb7..5d12365cc9 100644 --- a/deal.II/source/fe/fe_system.cc +++ b/deal.II/source/fe/fe_system.cc @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 by the deal.II authors +// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -54,8 +54,7 @@ FESystem::InternalData::InternalData(const unsigned int n_base_ele template FESystem::InternalData::~InternalData() { - // delete pointers and set them to - // zero to avoid inadvertent use + // delete pointers and set them to zero to avoid inadvertent use for (unsigned int i=0; i::shape_grad_component (const unsigned int i, Assert (component < this->n_components(), ExcIndexRange (component, 0, this->n_components())); - // if this value is supposed to be - // zero, then return right away... + // if this value is supposed to be zero, then return right away... if (this->nonzero_components[i][component] == false) return Tensor<1,dim>(); - // ...otherwise: first find out to - // which of the base elements this - // desired component belongs, and - // which component within this base - // element it is + // ...otherwise: first find out to which of the base elements this desired + // component belongs, and which component within this base element it is const unsigned int base = this->component_to_base_index(component).first; const unsigned int component_in_base = this->component_to_base_index(component).second; - // then get value from base - // element. note that that will - // throw an error should the - // respective shape function not be - // primitive; thus, there is no + // then get value from base element. note that that will throw an error + // should the respective shape function not be primitive; thus, there is no // need to check this here return (base_element(base). shape_grad_component(this->system_to_base_table[i].second, @@ -505,24 +497,17 @@ FESystem::shape_grad_grad_component (const unsigned int i, Assert (component < this->n_components(), ExcIndexRange (component, 0, this->n_components())); - // if this value is supposed to be - // zero, then return right away... + // if this value is supposed to be zero, then return right away... if (this->nonzero_components[i][component] == false) return Tensor<2,dim>(); - // ...otherwise: first find out to - // which of the base elements this - // desired component belongs, and - // which component within this base - // element it is + // ...otherwise: first find out to which of the base elements this desired + // component belongs, and which component within this base element it is const unsigned int base = this->component_to_base_index(component).first; const unsigned int component_in_base = this->component_to_base_index(component).second; - // then get value from base - // element. note that that will - // throw an error should the - // respective shape function not be - // primitive; thus, there is no + // then get value from base element. note that that will throw an error + // should the respective shape function not be primitive; thus, there is no // need to check this here return (base_element(base). shape_grad_grad_component(this->system_to_base_table[i].second, @@ -545,12 +530,10 @@ FESystem::get_interpolation_matrix ( ExcDimensionMismatch (interpolation_matrix.m(), x_source_fe.dofs_per_cell)); - // there are certain conditions - // that the two elements have to - // satisfy so that this can work. + // there are certain conditions that the two elements have to satisfy so + // that this can work. // - // condition 1: the other element - // must also be a system element + // condition 1: the other element must also be a system element typedef FiniteElement FEL; AssertThrow ((x_source_fe.get_name().find ("FESystem<") == 0) @@ -559,38 +542,29 @@ FESystem::get_interpolation_matrix ( typename FEL:: ExcInterpolationNotImplemented()); - // ok, source is a system element, - // so we may be able to do the work + // ok, source is a system element, so we may be able to do the work const FESystem &source_fe = dynamic_cast&>(x_source_fe); - // condition 2: same number of - // basis elements + // condition 2: same number of basis elements AssertThrow (this->n_base_elements() == source_fe.n_base_elements(), typename FEL:: ExcInterpolationNotImplemented()); - // condition 3: same number of - // basis elements + // condition 3: same number of basis elements for (unsigned int i=0; i< this->n_base_elements(); ++i) AssertThrow (this->element_multiplicity(i) == source_fe.element_multiplicity(i), typename FEL:: ExcInterpolationNotImplemented()); - // ok, so let's try whether it - // works: - - // first let's see whether all the - // basis elements actually generate - // their interpolation matrices. if - // we get past the following loop, - // then apparently none of the - // called base elements threw an - // exception, so we're fine - // continuing and assembling the - // one big matrix from the small - // ones of the base elements + // ok, so let's try whether it works: + + // first let's see whether all the basis elements actually generate their + // interpolation matrices. if we get past the following loop, then + // apparently none of the called base elements threw an exception, so we're + // fine continuing and assembling the one big matrix from the small ones of + // the base elements std::vector > base_matrices (this->n_base_elements()); for (unsigned int i=0; in_base_elements(); ++i) { @@ -600,11 +574,9 @@ FESystem::get_interpolation_matrix ( base_matrices[i]); } - // first clear big matrix, to make - // sure that entries that would - // couple different bases (or - // multiplicity indices) are really - // zero. then assign entries + // first clear big matrix, to make sure that entries that would couple + // different bases (or multiplicity indices) are really zero. then assign + // entries interpolation_matrix = 0; for (unsigned int i=0; idofs_per_cell; ++i) for (unsigned int j=0; j::get_interpolation_matrix ( +template +const FullMatrix& +FESystem +::get_restriction_matrix (const unsigned int child, + const RefinementCase &refinement_case) const +{ + Assert (refinement_case::isotropic_refinement+1, + ExcIndexRange(refinement_case,0,RefinementCase::isotropic_refinement+1)); + Assert (refinement_case!=RefinementCase::no_refinement, + ExcMessage("Restriction matrices are only available for refined cells!")); + Assert (child::n_children(refinement_case), + ExcIndexRange(child,0,GeometryInfo::n_children(refinement_case))); + + // initialization upon first request + if (this->restriction[refinement_case-1][child].n() == 0) + { + Threads::Mutex::ScopedLock lock(this->mutex); + + // check if updated while waiting for lock + if (this->restriction[refinement_case-1][child].n() == + this->dofs_per_cell) + return this->restriction[refinement_case-1][child]; + + // Check if some of the matrices of the base elements are void. + bool do_restriction = true; + + // shortcut for accessing local restrictions further down + std::vector *> + base_matrices(this->n_base_elements()); + + for (unsigned int i=0; in_base_elements(); ++i) + { + base_matrices[i] = + &base_element(i).get_restriction_matrix(child, refinement_case); + if (base_matrices[i]->n() != base_element(i).dofs_per_cell) + do_restriction = false; + } + Assert(do_restriction, + (typename FiniteElement::ExcProjectionVoid())); + + // if we did not encounter void matrices, initialize the matrix sizes + if (do_restriction) + { + FullMatrix restriction(this->dofs_per_cell, + this->dofs_per_cell); + + // distribute the matrices of the base finite elements to the + // matrices of this object. for this, loop over all degrees of + // freedom and take the respective entry of the underlying base + // element. + // + // note that we by definition of a base element, they are + // independent, i.e. do not couple. only DoFs that belong to the + // same instance of a base element may couple + for (unsigned int i=0; idofs_per_cell; ++i) + for (unsigned int j=0; jdofs_per_cell; ++j) + { + // first find out to which base element indices i and j + // belong, and which instance thereof in case the base element + // has a multiplicity greater than one. if they should not + // happen to belong to the same instance of a base element, + // then they cannot couple, so go on with the next index + if (this->system_to_base_table[i].first != + this->system_to_base_table[j].first) + continue; + + // so get the common base element and the indices therein: + const unsigned int + base = this->system_to_base_table[i].first.first; + + const unsigned int + base_index_i = this->system_to_base_table[i].second, + base_index_j = this->system_to_base_table[j].second; + + // if we are sure that DoFs i and j may couple, then copy + // entries of the matrices: + restriction(i,j) = (*base_matrices[base])(base_index_i,base_index_j); + } + + std::swap(restriction, const_cast &> + (this->restriction[refinement_case-1][child])); + } + } + + return this->restriction[refinement_case-1][child]; +} + + + +template +const FullMatrix& +FESystem +::get_prolongation_matrix (const unsigned int child, + const RefinementCase &refinement_case) const +{ + Assert (refinement_case::isotropic_refinement+1, + ExcIndexRange(refinement_case,0,RefinementCase::isotropic_refinement+1)); + Assert (refinement_case!=RefinementCase::no_refinement, + ExcMessage("Restriction matrices are only available for refined cells!")); + Assert (child::n_children(refinement_case), + ExcIndexRange(child,0,GeometryInfo::n_children(refinement_case))); + + // initialization upon first request, construction completely analogous to + // restriction matrix + if (this->prolongation[refinement_case-1][child].n() == 0) + { + Threads::Mutex::ScopedLock lock(this->mutex); + + if (this->prolongation[refinement_case-1][child].n() == + this->dofs_per_cell) + return this->prolongation[refinement_case-1][child]; + + bool do_prolongation = true; + std::vector *> + base_matrices(this->n_base_elements()); + for (unsigned int i=0; in_base_elements(); ++i) + { + base_matrices[i] = + &base_element(i).get_prolongation_matrix(child, refinement_case); + if (base_matrices[i]->n() != base_element(i).dofs_per_cell) + do_prolongation = false; + } + Assert(do_prolongation, + (typename FiniteElement::ExcEmbeddingVoid())); + + if (do_prolongation) + { + FullMatrix prolongate (this->dofs_per_cell, + this->dofs_per_cell); + + for (unsigned int i=0; idofs_per_cell; ++i) + for (unsigned int j=0; jdofs_per_cell; ++j) + { + if (this->system_to_base_table[i].first != + this->system_to_base_table[j].first) + continue; + const unsigned int + base = this->system_to_base_table[i].first.first; + + const unsigned int + base_index_i = this->system_to_base_table[i].second, + base_index_j = this->system_to_base_table[j].second; + prolongate(i,j) = (*base_matrices[base])(base_index_i,base_index_j); + } + std::swap(prolongate, const_cast &> + (this->prolongation[refinement_case-1][child])); + } + } + + return this->prolongation[refinement_case-1][child]; +} + + + //--------------------------------------------------------------------------- // Data field initialization //--------------------------------------------------------------------------- @@ -941,25 +1067,14 @@ FESystem::compute_fill ( { if (fe_data.is_first_cell()) { - // Initialize the - // FEValuesDatas for the - // base elements. - // Originally this was the - // task of - // FEValues::FEValues() but - // the latter initializes - // the FEValuesDatas only - // of the FESystem, not of - // the FEValuesDatas needed - // by the base elements - // (and: how should it know - // of their existence, - // after all). + // Initialize the FEValuesDatas for the base elements. Originally + // this was the task of FEValues::FEValues() but the latter + // initializes the FEValuesDatas only of the FESystem, not of the + // FEValuesDatas needed by the base elements (and: how should it + // know of their existence, after all). for (unsigned int base_no=0; base_non_base_elements(); ++base_no) { - // Pointer needed to get - // the update flags of the - // base element + // Pointer needed to get the update flags of the base element typename Mapping::InternalDataBase & base_fe_data = fe_data.get_fe_data(base_no); @@ -967,28 +1082,20 @@ FESystem::compute_fill ( const UpdateFlags base_update_flags = mapping_data.update_flags | base_fe_data.update_flags; - // Initialize the FEValuesDatas - // for the base elements. + // Initialize the FEValuesDatas for the base elements. FEValuesData &base_data=fe_data.get_fe_values_data(base_no); const FiniteElement &base_fe=base_element(base_no); base_data.initialize (n_q_points, base_fe, base_update_flags); } } - // fill_fe_face_values needs - // argument Quadrature - // for both cases - // dim_1==dim-1 and - // dim_1=dim. Hence the - // following workaround + // fill_fe_face_values needs argument Quadrature for both cases + // dim_1==dim-1 and dim_1=dim. Hence the following workaround const Quadrature *cell_quadrature = 0; const Quadrature *face_quadrature = 0; - // static cast to the - // common base class of - // quadrature being either - // Quadrature or - // Quadrature: + // static cast to the common base class of quadrature being either + // Quadrature or Quadrature: const Subscriptor *quadrature_base_pointer = &quadrature; if (face_no==invalid_face_number) @@ -1010,8 +1117,7 @@ FESystem::compute_fill ( = static_cast *>(quadrature_base_pointer); } - // let base elements update the - // necessary data + // let base elements update the necessary data for (unsigned int base_no=0; base_non_base_elements(); ++base_no) { const FiniteElement & @@ -1021,34 +1127,21 @@ FESystem::compute_fill ( FEValuesData & base_data = fe_data.get_fe_values_data(base_no); - //TODO: Think about a smarter alternative - // Copy quadrature points. These - // are required for computing the - // determinant in the FEPolyTensor - // class. The determinant is - // one ingredient of the Piola - // transformation, which is applied - // to correctly map the RT space from - // the reference element to the global - // coordinate system. + //TODO: Think about a smarter alternative Copy quadrature + // points. These are required for computing the determinant in the + // FEPolyTensor class. The determinant is one ingredient of the + // Piola transformation, which is applied to correctly map the RT + // space from the reference element to the global coordinate system. if (cell_similarity != CellSimilarity::translation) base_data.JxW_values = data.JxW_values; - // Make sure that in the - // case of fill_fe_values - // the data is only copied - // from base_data to data - // if base_data is - // changed. therefore use - // fe_fe_data.current_update_flags() + // Make sure that in the case of fill_fe_values the data is only + // copied from base_data to data if base_data is changed. therefore + // use fe_fe_data.current_update_flags() // - // for the case of - // fill_fe_(sub)face_values - // the data needs to be - // copied from base_data to - // data on each face, - // therefore use + // for the case of fill_fe_(sub)face_values the data needs to be + // copied from base_data to data on each face, therefore use // base_fe_data.update_flags. if (face_no==invalid_face_number) base_fe.fill_fe_values(mapping, cell, *cell_quadrature, mapping_data, @@ -1060,45 +1153,25 @@ FESystem::compute_fill ( base_fe.fill_fe_subface_values(mapping, cell, face_no, sub_no, *face_quadrature, mapping_data, base_fe_data, base_data); - // now data has been - // generated, so copy - // it. we used to work by - // looping over all base - // elements (i.e. this - // outer loop), then over - // multiplicity, then over - // the shape functions from - // that base element, but - // that requires that we - // can infer the global - // number of a shape - // function from its number - // in the base element. for - // that we had the - // component_to_system_table. + // now data has been generated, so copy it. we used to work by + // looping over all base elements (i.e. this outer loop), then over + // multiplicity, then over the shape functions from that base + // element, but that requires that we can infer the global number of + // a shape function from its number in the base element. for that we + // had the component_to_system_table. // - // however, this does of - // course no longer work - // since we have - // non-primitive - // elements. so we go the - // other way round: loop - // over all shape functions - // of the composed element, - // and here only treat - // those shape functions - // that belong to a given - // base element + // however, this does of course no longer work since we have + // non-primitive elements. so we go the other way round: loop over + // all shape functions of the composed element, and here only treat + // those shape functions that belong to a given base element //TODO: Introduce the needed table and loop only over base element shape functions. This here is not efficient at all AND very bad style const UpdateFlags base_flags(dim_1==dim ? base_fe_data.current_update_flags() : base_fe_data.update_flags); - // if the current cell is just a - // translation of the previous - // one, the underlying data has - // not changed, and we don't even - // need to enter this section + // if the current cell is just a translation of the previous one, + // the underlying data has not changed, and we don't even need to + // enter this section if (cell_similarity != CellSimilarity::translation) for (unsigned int system_index=0; system_indexdofs_per_cell; ++system_index) @@ -1108,22 +1181,12 @@ FESystem::compute_fill ( base_index = this->system_to_base_table[system_index].second; Assert (base_indexn_nonzero_components(i); @@ -1131,9 +1194,7 @@ FESystem::compute_fill ( for (unsigned int i=0; in_nonzero_components(system_index) == base_fe.n_nonzero_components(base_index), ExcInternalError()); @@ -1149,15 +1210,8 @@ FESystem::compute_fill ( data.shape_gradients[out_index+s][q]= base_data.shape_gradients[in_index+s][q]; - // _we_ handle - // computation of - // second - // derivatives, - // so the base - // elements - // should not - // have computed - // them! + // _we_ handle computation of second derivatives, so the + // base elements should not have computed them! Assert (!(base_flags & update_hessians), ExcInternalError()); }; @@ -1197,9 +1251,8 @@ template void FESystem::build_cell_tables() { - // If the system is not primitive, - // these have not been initialized - // by FiniteElement + // If the system is not primitive, these have not been initialized by + // FiniteElement this->system_to_component_table.resize(this->dofs_per_cell); this->face_system_to_component_table.resize(this->dofs_per_face); @@ -1217,20 +1270,16 @@ FESystem::build_cell_tables() Assert (total_index == this->component_to_base_table.size(), ExcInternalError()); - // Initialize index tables. - // Multi-component base elements - // have to be thought of. For - // non-primitive shape functions, - // have a special invalid index. + // Initialize index tables. Multi-component base elements have to be + // thought of. For non-primitive shape functions, have a special invalid + // index. const std::pair non_primitive_index (numbers::invalid_unsigned_int, numbers::invalid_unsigned_int); - // First enumerate vertex indices, - // where we first enumerate all - // indices on the first vertex in - // the order of the base elements, - // then of the second vertex, etc + // First enumerate vertex indices, where we first enumerate all indices on + // the first vertex in the order of the base elements, then of the second + // vertex, etc total_index = 0; for (unsigned int vertex_number=0; vertex_number::vertices_per_cell; @@ -1388,14 +1437,10 @@ template void FESystem::build_face_tables() { - // Initialize index tables. do this - // in the same way as done for the - // cell tables, except that we now - // loop over the objects of faces - - // For non-primitive shape - // functions, have a special - // invalid index + // Initialize index tables. do this in the same way as done for the cell + // tables, except that we now loop over the objects of faces + + // For non-primitive shape functions, have a special invalid index const std::pair non_primitive_index (numbers::invalid_unsigned_int, numbers::invalid_unsigned_int); @@ -1414,30 +1459,13 @@ FESystem::build_face_tables() local_index < base_element(base).dofs_per_vertex; ++local_index, ++total_index) { - // get (cell) index of - // this shape function - // inside the base - // element to see - // whether the shape - // function is - // primitive (assume - // that all shape - // functions on - // vertices share the - // same primitivity - // property; assume - // likewise for all - // shape functions - // located on lines, - // quads, etc. this - // way, we can ask for - // primitivity of only - // _one_ shape - // function, which is - // taken as - // representative for - // all others located - // on the same type of + // get (cell) index of this shape function inside the base + // element to see whether the shape function is primitive + // (assume that all shape functions on vertices share the same + // primitivity property; assume likewise for all shape functions + // located on lines, quads, etc. this way, we can ask for + // primitivity of only _one_ shape function, which is taken as + // representative for all others located on the same type of // object): const unsigned int index_in_base = (base_element(base).dofs_per_vertex*vertex_number + @@ -1480,9 +1508,7 @@ FESystem::build_face_tables() local_index < base_element(base).dofs_per_line; ++local_index, ++total_index) { - // do everything - // alike for this - // type of object + // do everything alike for this type of object const unsigned int index_in_base = (base_element(base).dofs_per_line*line_number + local_index + @@ -1526,9 +1552,7 @@ FESystem::build_face_tables() local_index < base_element(base).dofs_per_quad; ++local_index, ++total_index) { - // do everything - // alike for this - // type of object + // do everything alike for this type of object const unsigned int index_in_base = (base_element(base).dofs_per_quad*quad_number + local_index + @@ -1569,14 +1593,10 @@ FESystem::build_face_tables() template void FESystem::build_interface_constraints () { - // check whether all base elements - // implement their interface - // constraint matrices. if this is - // not the case, then leave the - // interface costraints of this - // composed element empty as well; - // however, the rest of the element - // is usable + // check whether all base elements implement their interface constraint + // matrices. if this is not the case, then leave the interface costraints of + // this composed element empty as well; however, the rest of the element is + // usable for (unsigned int base=0; basen_base_elements(); ++base) if (base_element(base).constraints_are_implemented() == false) return; @@ -1584,58 +1604,35 @@ void FESystem::build_interface_constraints () this->interface_constraints. TableBase<2,double>::reinit (this->interface_constraints_size()); - // the layout of the constraints - // matrix is described in the - // FiniteElement class. you may - // want to look there first before - // trying to understand the - // following, especially the - // mapping of the @p{m} index. + // the layout of the constraints matrix is described in the FiniteElement + // class. you may want to look there first before trying to understand the + // following, especially the mapping of the @p{m} index. // - // in order to map it to the - // fe-system class, we have to know - // which base element a degree of - // freedom within a vertex, line, - // etc belongs to. this can be - // accomplished by the - // system_to_component_index - // function in conjunction with the - // numbers - // first_{line,quad,...}_index + // in order to map it to the fe-system class, we have to know which base + // element a degree of freedom within a vertex, line, etc belongs to. this + // can be accomplished by the system_to_component_index function in + // conjunction with the numbers first_{line,quad,...}_index for (unsigned int n=0; ninterface_constraints.n(); ++n) for (unsigned int m=0; minterface_constraints.m(); ++m) { - // for the pair (n,m) find - // out which base element - // they belong to and the - // number therein + // for the pair (n,m) find out which base element they belong to and + // the number therein // - // first for the n - // index. this is simple - // since the n indices are in - // the same order as they are - // usually on a face. note - // that for the data type, - // first value in pair is - // (base element,instance of - // base element), second is - // index within this instance + // first for the n index. this is simple since the n indices are in + // the same order as they are usually on a face. note that for the + // data type, first value in pair is (base element,instance of base + // element), second is index within this instance const std::pair, unsigned int> n_index = this->face_system_to_base_table[n]; - // likewise for the m - // index. this is more - // complicated due to the - // strange ordering we have - // for the dofs on the - // refined faces. + // likewise for the m index. this is more complicated due to the + // strange ordering we have for the dofs on the refined faces. std::pair, unsigned int> m_index; switch (dim) { case 1: { - // we should never get here! - // (in 1d, the constraints matrix + // we should never get here! (in 1d, the constraints matrix // should be of size zero) Assert (false, ExcInternalError()); break; @@ -1643,21 +1640,14 @@ void FESystem::build_interface_constraints () case 2: { - // the indices m=0..d_v-1 are - // from the center vertex. - // their order is the same - // as for the first vertex - // of the whole cell, so we - // can use the - // system_to_base_table - // variable (using the - // face_s_t_base_t function would - // yield the same) + // the indices m=0..d_v-1 are from the center vertex. their order + // is the same as for the first vertex of the whole cell, so we + // can use the system_to_base_table variable (using the + // face_s_t_base_t function would yield the same) if (m < this->dofs_per_vertex) m_index = this->system_to_base_table[m]; else - // then come the two sets of - // line indices + // then come the two sets of line indices { const unsigned int index_in_line = (m-this->dofs_per_vertex) % this->dofs_per_line; @@ -1665,49 +1655,21 @@ void FESystem::build_interface_constraints () = (m-this->dofs_per_vertex) / this->dofs_per_line; Assert (sub_line < 2, ExcInternalError()); - // from this - // information, try - // to get base - // element and - // instance of base - // element. we do - // so by - // constructing the - // corresponding - // face index of m - // in the present - // element, then - // use + // from this information, try to get base element and instance + // of base element. we do so by constructing the corresponding + // face index of m in the present element, then use // face_system_to_base_table const unsigned int tmp1 = 2*this->dofs_per_vertex+index_in_line; m_index.first = this->face_system_to_base_table[tmp1].first; - // what we are - // still missing is - // the index of m - // within the base - // elements - // interface_constraints - // table + // what we are still missing is the index of m within the base + // elements interface_constraints table // - // here, the second - // value of - // face_system_to_base_table - // can help: it - // denotes the face - // index of that - // shape function - // within the base - // element. since - // we know that it - // is a line dof, - // we can construct - // the rest: tmp2 - // will denote the - // index of this - // shape function - // among the line - // shape functions: + // here, the second value of face_system_to_base_table can + // help: it denotes the face index of that shape function + // within the base element. since we know that it is a line + // dof, we can construct the rest: tmp2 will denote the index + // of this shape function among the line shape functions: Assert (this->face_system_to_base_table[tmp1].second >= 2*base_element(m_index.first.first).dofs_per_vertex, ExcInternalError()); @@ -1724,31 +1686,18 @@ void FESystem::build_interface_constraints () case 3: { - // same way as above, - // although a little - // more complicated... - - // the indices - // m=0..5*d_v-1 are - // from the center and - // the four subline - // vertices. their - // order is the same as - // for the first vertex - // of the whole cell, - // so we can use the - // simple arithmetic + // same way as above, although a little more complicated... + + // the indices m=0..5*d_v-1 are from the center and the four + // subline vertices. their order is the same as for the first + // vertex of the whole cell, so we can use the simple arithmetic if (m < 5*this->dofs_per_vertex) m_index = this->system_to_base_table[m]; else - // then come the 12 sets of - // line indices + // then come the 12 sets of line indices if (m < 5*this->dofs_per_vertex + 12*this->dofs_per_line) { - // for the - // meaning of all - // this, see the - // 2d part + // for the meaning of all this, see the 2d part const unsigned int index_in_line = (m-5*this->dofs_per_vertex) % this->dofs_per_line; const unsigned int sub_line @@ -1772,10 +1721,7 @@ void FESystem::build_interface_constraints () else // on one of the four sub-quads { - // for the - // meaning of all - // this, see the - // 2d part + // for the meaning of all this, see the 2d part const unsigned int index_in_quad = (m-5*this->dofs_per_vertex-12*this->dofs_per_line) % this->dofs_per_quad; @@ -1815,14 +1761,9 @@ void FESystem::build_interface_constraints () Assert (false, ExcNotImplemented()); }; - // now that we gathered all - // information: use it to - // build the matrix. note - // that if n and m belong to - // different base elements or - // instances, then there - // definitely will be no - // coupling + // now that we gathered all information: use it to build the + // matrix. note that if n and m belong to different base elements or + // instances, then there definitely will be no coupling if (n_index.first == m_index.first) this->interface_constraints(m,n) = (base_element(n_index.first.first).constraints()(m_index.second, @@ -1869,106 +1810,13 @@ void FESystem::initialize (const std::vector::cut_x; - ref_case::isotropic_refinement+1; - ++ref_case) - { - // Check if some of the matrices of - // the base elements are void. - // repeat this check for each RefineCase - bool do_restriction = true; - bool do_prolongation = true; + // restriction and prolongation matrices are build on demand - for (unsigned int i=0; in_base_elements(); ++i) - { - if (base_element(i).restriction[ref_case-1][0].n() != base_element(i).dofs_per_cell) - do_restriction = false; - if (base_element(i).prolongation[ref_case-1][0].n() != base_element(i).dofs_per_cell) - do_prolongation = false; - } - - // if we did not encounter void - // matrices, initialize the - // respective matrix sizes - if (do_restriction) - for (unsigned int i=0; i::n_children(RefinementCase(ref_case)); ++i) - this->restriction[ref_case-1][i].reinit(this->dofs_per_cell, - this->dofs_per_cell); - if (do_prolongation) - for (unsigned int i=0; i::n_children(RefinementCase(ref_case)); ++i) - this->prolongation[ref_case-1][i].reinit(this->dofs_per_cell, - this->dofs_per_cell); - - // distribute the matrices of the - // base finite elements to the - // matrices of this object. for - // this, loop over all degrees of - // freedom and take the respective - // entry of the underlying base - // element. - // - // note that we by definition of a - // base element, they are - // independent, i.e. do not - // couple. only DoFs that belong to - // the same instance of a base - // element may couple - for (unsigned int i=0; idofs_per_cell; ++i) - for (unsigned int j=0; jdofs_per_cell; ++j) - { - // first find out to which - // base element indices i and - // j belong, and which - // instance thereof in case - // the base element has a - // multiplicity greater than - // one. if they should not - // happen to belong to the - // same instance of a base - // element, then they cannot - // couple, so go on with the - // next index - if (this->system_to_base_table[i].first != - this->system_to_base_table[j].first) - continue; - - // so get the common base - // element and the indices - // therein: - const unsigned int - base = this->system_to_base_table[i].first.first; - - const unsigned int - base_index_i = this->system_to_base_table[i].second, - base_index_j = this->system_to_base_table[j].second; - - // if we are sure that DoFs i - // and j may couple, then - // copy entries of the - // matrices: - for (unsigned int child=0; child::n_children(RefinementCase(ref_case)); ++child) - { - if (do_restriction) - this->restriction[ref_case-1][child] (i,j) - = (base_element(base) - .get_restriction_matrix(child, RefinementCase(ref_case))( - base_index_i,base_index_j)); - - if (do_prolongation) - this->prolongation[ref_case-1][child] (i,j) - = (base_element(base) - .get_prolongation_matrix(child, RefinementCase(ref_case))( - base_index_i,base_index_j)); - }; - }; - } - // now set up the interface constraints. - // this is kind'o hairy, so don't try + // now set up the interface constraints. this is kind'o hairy, so don't try // to do it dimension independent build_interface_constraints (); - // finally fill in support points - // on cell and face + // finally fill in support points on cell and face initialize_unit_support_points (); initialize_unit_face_support_points (); @@ -1985,14 +1833,9 @@ void FESystem:: initialize_unit_support_points () { - // if one of the base elements - // has no support points, then - // it makes no sense to define - // support points for the - // composed element, so return - // an empty array to - // demonstrate that - // fact + // if one of the base elements has no support points, then it makes no sense + // to define support points for the composed element, so return an empty + // array to demonstrate that fact for (unsigned int base_el=0; base_eln_base_elements(); ++base_el) if (!base_element(base_el).has_support_points()) { @@ -2000,9 +1843,7 @@ initialize_unit_support_points () return; }; - // generate unit support points - // from unit support points of sub - // elements + // generate unit support points from unit support points of sub elements this->unit_support_points.resize(this->dofs_per_cell); for (unsigned int i=0; idofs_per_cell; ++i) @@ -2028,22 +1869,15 @@ initialize_unit_face_support_points () if (dim == 1) return; - // if one of the base elements has no - // support points, then it makes no sense - // to define support points for the - // composed element. In that case, return - // an empty array to demonstrate that fact - // (note that we ask whether the base - // element has no support points at all, - // not only none on the face!) + // if one of the base elements has no support points, then it makes no sense + // to define support points for the composed element. In that case, return + // an empty array to demonstrate that fact (note that we ask whether the + // base element has no support points at all, not only none on the face!) // - // on the other hand, if there is an - // element that simply has no degrees of - // freedom on the face at all, then we - // don't care whether it has support points - // or not. this is, for example, the case - // for the stable Stokes element Q(p)^dim - // \times DGP(p-1). + // on the other hand, if there is an element that simply has no degrees of + // freedom on the face at all, then we don't care whether it has support + // points or not. this is, for example, the case for the stable Stokes + // element Q(p)^dim \times DGP(p-1). for (unsigned int base_el=0; base_eln_base_elements(); ++base_el) if (!base_element(base_el).has_support_points() && @@ -2054,8 +1888,7 @@ initialize_unit_face_support_points () } - // generate unit face support points - // from unit support points of sub + // generate unit face support points from unit support points of sub // elements this->unit_face_support_points.resize(this->dofs_per_face); @@ -2082,14 +1915,13 @@ FESystem::initialize_quad_dof_index_permutation () if (dim < 3) return; - // the array into which we want to write - // should have the correct size already. + // the array into which we want to write should have the correct size + // already. Assert (this->adjust_quad_dof_index_for_face_orientation_table.n_elements()== 8*this->dofs_per_quad, ExcInternalError()); - // to obtain the shifts for this composed - // element, copy the shift information of - // the base elements + // to obtain the shifts for this composed element, copy the shift + // information of the base elements unsigned int index = 0; for (unsigned int b=0; bn_base_elements(); ++b) { @@ -2107,8 +1939,7 @@ FESystem::initialize_quad_dof_index_permutation () Assert (index == this->dofs_per_quad, ExcInternalError()); - // aditionally compose the permutation - // information for lines + // aditionally compose the permutation information for lines Assert (this->adjust_line_dof_index_for_line_orientation_table.size()== this->dofs_per_line, ExcInternalError()); index = 0; @@ -2164,31 +1995,23 @@ get_face_interpolation_matrix (const FiniteElement &x_source_fe, ExcDimensionMismatch (interpolation_matrix.m(), x_source_fe.dofs_per_face)); - // since dofs for each base are - // independent, we only have to stack - // things up from base element to base - // element + // since dofs for each base are independent, we only have to stack things up + // from base element to base element // - // the problem is that we have to work with - // two FEs (this and fe_other). only deal - // with the case that both are FESystems - // and that they both have the same number - // of bases (counting multiplicity) each of - // which match in their number of - // components. this covers - // FESystem(FE_Q(p),1,FE_Q(q),2) vs - // FESystem(FE_Q(r),2,FE_Q(s),1), but not + // the problem is that we have to work with two FEs (this and + // fe_other). only deal with the case that both are FESystems and that they + // both have the same number of bases (counting multiplicity) each of which + // match in their number of components. this covers + // FESystem(FE_Q(p),1,FE_Q(q),2) vs FESystem(FE_Q(r),2,FE_Q(s),1), but not // FESystem(FE_Q(p),1,FE_Q(q),2) vs // FESystem(FESystem(FE_Q(r),2),1,FE_Q(s),1) const FESystem *fe_other_system = dynamic_cast*>(&x_source_fe); - // clear matrix, since we will not get to - // set all elements + // clear matrix, since we will not get to set all elements interpolation_matrix = 0; - // loop over all the base elements of - // this and the other element, counting + // loop over all the base elements of this and the other element, counting // their multiplicities unsigned int base_index = 0, base_index_other = 0; @@ -2212,11 +2035,9 @@ get_face_interpolation_matrix (const FiniteElement &x_source_fe, base.get_face_interpolation_matrix (base_other, base_to_base_interpolation); - // now translate entries. we'd like to - // have something like - // face_base_to_system_index, but that - // doesn't exist. rather, all we have - // is the reverse. well, use that then + // now translate entries. we'd like to have something like + // face_base_to_system_index, but that doesn't exist. rather, all we + // have is the reverse. well, use that then for (unsigned int i=0; idofs_per_face; ++i) if (this->face_system_to_base_index(i).first == @@ -2229,12 +2050,9 @@ get_face_interpolation_matrix (const FiniteElement &x_source_fe, = base_to_base_interpolation(fe_other_system->face_system_to_base_index(j).second, this->face_system_to_base_index(i).second); - // advance to the next base element - // for this and the other - // fe_system; see if we can simply - // advance the multiplicity by one, - // or if have to move on to the - // next base element + // advance to the next base element for this and the other fe_system; + // see if we can simply advance the multiplicity by one, or if have to + // move on to the next base element ++multiplicity; if (multiplicity == this->element_multiplicity(base_index)) { @@ -2249,10 +2067,8 @@ get_face_interpolation_matrix (const FiniteElement &x_source_fe, ++base_index_other; } - // see if we have reached the end - // of the present element. if so, - // we should have reached the end - // of the other one as well + // see if we have reached the end of the present element. if so, we + // should have reached the end of the other one as well if (base_index == this->n_base_elements()) { Assert (base_index_other == fe_other_system->n_base_elements(), @@ -2260,10 +2076,8 @@ get_face_interpolation_matrix (const FiniteElement &x_source_fe, break; } - // if we haven't reached the end of - // this element, we shouldn't have - // reached the end of the other one - // either + // if we haven't reached the end of this element, we shouldn't have + // reached the end of the other one either Assert (base_index_other != fe_other_system->n_base_elements(), ExcInternalError()); } @@ -2292,31 +2106,23 @@ get_subface_interpolation_matrix (const FiniteElement &x_source_fe ExcDimensionMismatch (interpolation_matrix.m(), x_source_fe.dofs_per_face)); - // since dofs for each base are - // independent, we only have to stack - // things up from base element to base - // element + // since dofs for each base are independent, we only have to stack things up + // from base element to base element // - // the problem is that we have to work with - // two FEs (this and fe_other). only deal - // with the case that both are FESystems - // and that they both have the same number - // of bases (counting multiplicity) each of - // which match in their number of - // components. this covers - // FESystem(FE_Q(p),1,FE_Q(q),2) vs - // FESystem(FE_Q(r),2,FE_Q(s),1), but not + // the problem is that we have to work with two FEs (this and + // fe_other). only deal with the case that both are FESystems and that they + // both have the same number of bases (counting multiplicity) each of which + // match in their number of components. this covers + // FESystem(FE_Q(p),1,FE_Q(q),2) vs FESystem(FE_Q(r),2,FE_Q(s),1), but not // FESystem(FE_Q(p),1,FE_Q(q),2) vs // FESystem(FESystem(FE_Q(r),2),1,FE_Q(s),1) const FESystem *fe_other_system = dynamic_cast*>(&x_source_fe); - // clear matrix, since we will not get to - // set all elements + // clear matrix, since we will not get to set all elements interpolation_matrix = 0; - // loop over all the base elements of - // this and the other element, counting + // loop over all the base elements of this and the other element, counting // their multiplicities unsigned int base_index = 0, base_index_other = 0; @@ -2341,11 +2147,9 @@ get_subface_interpolation_matrix (const FiniteElement &x_source_fe subface, base_to_base_interpolation); - // now translate entries. we'd like to - // have something like - // face_base_to_system_index, but that - // doesn't exist. rather, all we have - // is the reverse. well, use that then + // now translate entries. we'd like to have something like + // face_base_to_system_index, but that doesn't exist. rather, all we + // have is the reverse. well, use that then for (unsigned int i=0; idofs_per_face; ++i) if (this->face_system_to_base_index(i).first == @@ -2358,12 +2162,9 @@ get_subface_interpolation_matrix (const FiniteElement &x_source_fe = base_to_base_interpolation(fe_other_system->face_system_to_base_index(j).second, this->face_system_to_base_index(i).second); - // advance to the next base element - // for this and the other - // fe_system; see if we can simply - // advance the multiplicity by one, - // or if have to move on to the - // next base element + // advance to the next base element for this and the other fe_system; + // see if we can simply advance the multiplicity by one, or if have to + // move on to the next base element ++multiplicity; if (multiplicity == this->element_multiplicity(base_index)) { @@ -2378,10 +2179,8 @@ get_subface_interpolation_matrix (const FiniteElement &x_source_fe ++base_index_other; } - // see if we have reached the end - // of the present element. if so, - // we should have reached the end - // of the other one as well + // see if we have reached the end of the present element. if so, we + // should have reached the end of the other one as well if (base_index == this->n_base_elements()) { Assert (base_index_other == fe_other_system->n_base_elements(), @@ -2389,10 +2188,8 @@ get_subface_interpolation_matrix (const FiniteElement &x_source_fe break; } - // if we haven't reached the end of - // this element, we shouldn't have - // reached the end of the other one - // either + // if we haven't reached the end of this element, we shouldn't have + // reached the end of the other one either Assert (base_index_other != fe_other_system->n_base_elements(), ExcInternalError()); } @@ -2405,38 +2202,29 @@ template std::vector > FESystem::hp_object_dof_identities (const FiniteElement &fe_other) const { - // since dofs on each subobject (vertex, - // line, ...) are ordered such that first - // come all from the first base element all - // multiplicities, then second base element - // all multiplicities, etc., we simply have - // to stack all the identities after each - // other + // since dofs on each subobject (vertex, line, ...) are ordered such that + // first come all from the first base element all multiplicities, then + // second base element all multiplicities, etc., we simply have to stack all + // the identities after each other // - // the problem is that we have to work with - // two FEs (this and fe_other). only deal - // with the case that both are FESystems - // and that they both have the same number - // of bases (counting multiplicity) each of - // which match in their number of - // components. this covers - // FESystem(FE_Q(p),1,FE_Q(q),2) vs - // FESystem(FE_Q(r),2,FE_Q(s),1), but not + // the problem is that we have to work with two FEs (this and + // fe_other). only deal with the case that both are FESystems and that they + // both have the same number of bases (counting multiplicity) each of which + // match in their number of components. this covers + // FESystem(FE_Q(p),1,FE_Q(q),2) vs FESystem(FE_Q(r),2,FE_Q(s),1), but not // FESystem(FE_Q(p),1,FE_Q(q),2) vs // FESystem(FESystem(FE_Q(r),2),1,FE_Q(s),1) if (const FESystem *fe_other_system = dynamic_cast*>(&fe_other)) { - // loop over all the base elements of - // this and the other element, counting - // their multiplicities + // loop over all the base elements of this and the other element, + // counting their multiplicities unsigned int base_index = 0, base_index_other = 0; unsigned int multiplicity = 0, multiplicity_other = 0; - // we also need to keep track of the - // number of dofs already treated for + // we also need to keep track of the number of dofs already treated for // each of the elements unsigned int dof_offset = 0, dof_offset_other = 0; @@ -2452,10 +2240,8 @@ FESystem::hp_object_dof_identities (const FiniteElement > base_identities; switch (structdim) { @@ -2477,17 +2263,13 @@ FESystem::hp_object_dof_identities (const FiniteElement(); dof_offset_other += base_other.template n_dofs_per_object(); - // advance to the next base element - // for this and the other - // fe_system; see if we can simply - // advance the multiplicity by one, - // or if have to move on to the - // next base element + // advance to the next base element for this and the other + // fe_system; see if we can simply advance the multiplicity by one, + // or if have to move on to the next base element ++multiplicity; if (multiplicity == this->element_multiplicity(base_index)) { @@ -2502,10 +2284,8 @@ FESystem::hp_object_dof_identities (const FiniteElementn_base_elements()) { Assert (base_index_other == fe_other_system->n_base_elements(), @@ -2513,10 +2293,8 @@ FESystem::hp_object_dof_identities (const FiniteElementn_base_elements(), ExcInternalError()); } @@ -2562,9 +2340,8 @@ FiniteElementDomination::Domination FESystem:: compare_for_face_domination (const FiniteElement &fe_other) const { - // at present all we can do is to compare - // with other FESystems that have the same - // number of components and bases + // at present all we can do is to compare with other FESystems that have the + // same number of components and bases if (const FESystem *fe_sys_other = dynamic_cast*>(&fe_other)) { @@ -2576,8 +2353,7 @@ compare_for_face_domination (const FiniteElement &fe_other) const FiniteElementDomination::Domination domination = FiniteElementDomination::no_requirements; - // loop over all base elements and do - // some sanity checks + // loop over all base elements and do some sanity checks for (unsigned int b=0; bn_base_elements(); ++b) { Assert (this->base_element(b).n_components() == @@ -2587,8 +2363,7 @@ compare_for_face_domination (const FiniteElement &fe_other) const fe_sys_other->element_multiplicity(b), ExcNotImplemented()); - // for this pair of base elements, - // check who dominates and combine + // for this pair of base elements, check who dominates and combine // with previous result const FiniteElementDomination::Domination base_domination = (this->base_element(b) @@ -2596,8 +2371,7 @@ compare_for_face_domination (const FiniteElement &fe_other) const domination = domination & base_domination; } - // if we've gotten here, then we've - // either found a winner or either + // if we've gotten here, then we've either found a winner or either // element is fine being dominated Assert (domination != FiniteElementDomination::neither_element_dominates, @@ -2677,11 +2451,27 @@ FESystem::multiply_dof_numbers ( summed_multiplicities += multiplicities[i]; } - unsigned char total_conformity = 0xff;; - - for (unsigned int i=0; i0) - total_conformity &= fes[i]->conforming_space; + // assume conformity of the first finite element and then take away + // bits as indicated by the base elements. if all multiplicities + // happen to be zero, then it doesn't matter what we set it to. + typename FiniteElementData::Conformity total_conformity + = typename FiniteElementData::Conformity(); + { + unsigned int index = 0; + for (unsigned int index=0; index0) + { + total_conformity = fes[index]->conforming_space; + break; + } + + for (; index0) + total_conformity = + typename FiniteElementData::Conformity(total_conformity + & + fes[index]->conforming_space); + } std::vector dpo; dpo.push_back(multiplied_dofs_per_vertex); @@ -2689,12 +2479,11 @@ FESystem::multiply_dof_numbers ( if (dim>1) dpo.push_back(multiplied_dofs_per_quad); if (dim>2) dpo.push_back(multiplied_dofs_per_hex); - return FiniteElementData ( - dpo, - multiplied_n_components, - degree, - typename FiniteElementData::Conformity(total_conformity), - summed_multiplicities); + return FiniteElementData (dpo, + multiplied_n_components, + degree, + total_conformity, + summed_multiplicities); } @@ -2741,34 +2530,23 @@ compute_restriction_is_additive_flags (const std::vector0) // check needed as fe might be NULL n_shape_functions += fes[i]->dofs_per_cell * multiplicities[i]; - // generate the array that will - // hold the output + // generate the array that will hold the output std::vector retval (n_shape_functions, false); - // finally go through all the shape - // functions of the base elements, - // and copy their flags. this - // somehow copies the code in - // build_cell_table, which is not - // nice as it uses too much - // implicit knowledge about the - // layout of the individual bases - // in the composed FE, but there - // seems no way around... + // finally go through all the shape functions of the base elements, and copy + // their flags. this somehow copies the code in build_cell_table, which is + // not nice as it uses too much implicit knowledge about the layout of the + // individual bases in the composed FE, but there seems no way around... // - // for each shape function, copy - // the flags from the base element - // to this one, taking into account - // multiplicities, and other - // complications + // for each shape function, copy the flags from the base element to this + // one, taking into account multiplicities, and other complications unsigned int total_index = 0; for (unsigned int vertex_number=0; vertex_number::vertices_per_cell; @@ -2910,9 +2688,8 @@ compute_nonzero_components (const std::vector* { Assert (fes.size() == multiplicities.size(), ExcInternalError()); - // first count the number of dofs - // and components that will emerge - // from the given FEs + // first count the number of dofs and components that will emerge from the + // given FEs unsigned int n_shape_functions = 0; for (unsigned int i=0; i0) //needed because fe might be NULL @@ -2923,28 +2700,18 @@ compute_nonzero_components (const std::vector* if (multiplicities[i]>0) //needed because fe might be NULL n_components += fes[i]->n_components() * multiplicities[i]; - // generate the array that will - // hold the output + // generate the array that will hold the output std::vector > retval (n_shape_functions, std::vector (n_components, false)); - // finally go through all the shape - // functions of the base elements, - // and copy their flags. this - // somehow copies the code in - // build_cell_table, which is not - // nice as it uses too much - // implicit knowledge about the - // layout of the individual bases - // in the composed FE, but there - // seems no way around... + // finally go through all the shape functions of the base elements, and copy + // their flags. this somehow copies the code in build_cell_table, which is + // not nice as it uses too much implicit knowledge about the layout of the + // individual bases in the composed FE, but there seems no way around... // - // for each shape function, copy - // the non-zero flags from the base - // element to this one, taking into - // account multiplicities, multiple - // components in base elements, and - // other complications + // for each shape function, copy the non-zero flags from the base element to + // this one, taking into account multiplicities, multiple components in base + // elements, and other complications unsigned int total_index = 0; for (unsigned int vertex_number=0; vertex_number::vertices_per_cell; @@ -3128,14 +2895,12 @@ FESystem::unit_support_point (const unsigned int index) const (this->unit_support_points.size() == 0), typename FEL::ExcFEHasNoSupportPoints ()); - // let's see whether we have the - // information pre-computed + // let's see whether we have the information pre-computed if (this->unit_support_points.size() != 0) return this->unit_support_points[index]; else - // no. ask the base element - // whether it would like to - // provide this information + // no. ask the base element whether it would like to provide this + // information return (base_element(this->system_to_base_index(index).first.first) .unit_support_point(this->system_to_base_index(index).second)); } @@ -3153,14 +2918,12 @@ FESystem::unit_face_support_point (const unsigned int index) const (this->unit_face_support_points.size() == 0), typename FEL::ExcFEHasNoSupportPoints ()); - // let's see whether we have the - // information pre-computed + // let's see whether we have the information pre-computed if (this->unit_face_support_points.size() != 0) return this->unit_face_support_points[index]; else - // no. ask the base element - // whether it would like to - // provide this information + // no. ask the base element whether it would like to provide this + // information return (base_element(this->face_system_to_base_index(index).first.first) .unit_face_support_point(this->face_system_to_base_index(index).second)); } @@ -3171,11 +2934,8 @@ template std::size_t FESystem::memory_consumption () const { - // neglect size of data stored in - // @p{base_elements} due to some - // problems with the - // compiler. should be neglectable - // after all, considering the size + // neglect size of data stored in @p{base_elements} due to some problems + // with the compiler. should be neglectable after all, considering the size // of the data of the subelements std::size_t mem = (FiniteElement::memory_consumption () + sizeof (base_elements)); diff --git a/deal.II/source/fe/fe_tools.cc b/deal.II/source/fe/fe_tools.cc index 0589b9a3b4..ccff1cf311 100644 --- a/deal.II/source/fe/fe_tools.cc +++ b/deal.II/source/fe/fe_tools.cc @@ -1630,6 +1630,79 @@ namespace FETools + namespace internal + { + namespace + { + template + void back_interpolate (const DoFHandler &dof1, + const ConstraintMatrix &constraints1, + const InVector &u1, + const DoFHandler &dof2, + const ConstraintMatrix &constraints2, + InVector &u1_interpolated) + { + Vector u2(dof2.n_dofs()); + interpolate(dof1, u1, dof2, constraints2, u2); + interpolate(dof2, u2, dof1, constraints1, u1_interpolated); + } + + // special version for PETSc +#ifdef DEAL_II_WITH_PETSC + template + void back_interpolate (const DoFHandler &dof1, + const ConstraintMatrix &constraints1, + const PETScWrappers::MPI::Vector &u1, + const DoFHandler &dof2, + const ConstraintMatrix &constraints2, + PETScWrappers::MPI::Vector &u1_interpolated) + { + // if u1 is a parallel distributed PETSc vector, we create a + // vector u2 with based on the sets of locally owned and relevant + // dofs of dof2 + IndexSet dof2_locally_owned_dofs = dof2.locally_owned_dofs(); + IndexSet dof2_locally_relevant_dofs; + DoFTools::extract_locally_relevant_dofs (dof2, + dof2_locally_relevant_dofs); + + PETScWrappers::MPI::Vector u2_out (u1.get_mpi_communicator(), + dof2_locally_owned_dofs); + interpolate(dof1, u1, dof2, constraints2, u2_out); + PETScWrappers::MPI::Vector u2 (u1.get_mpi_communicator(), + dof2_locally_owned_dofs, + dof2_locally_relevant_dofs); + u2 = u2_out; + interpolate(dof2, u2, dof1, constraints1, u1_interpolated); + } +#endif + + // special version for parallel::distributed::Vector + template + void back_interpolate (const DoFHandler &dof1, + const ConstraintMatrix &constraints1, + const parallel::distributed::Vector &u1, + const DoFHandler &dof2, + const ConstraintMatrix &constraints2, + parallel::distributed::Vector &u1_interpolated) + { + IndexSet dof2_locally_owned_dofs = dof2.locally_owned_dofs(); + IndexSet dof2_locally_relevant_dofs; + DoFTools::extract_locally_relevant_dofs (dof2, + dof2_locally_relevant_dofs); + + parallel::distributed::Vector + u2 (dof2_locally_owned_dofs, + dof2_locally_relevant_dofs, + u1.get_mpi_communicator()); + + interpolate(dof1, u1, dof2, constraints2, u2); + u2.update_ghost_values (); + interpolate(dof2, u2, dof1, constraints1, u1_interpolated); + } + } + } + + template void back_interpolate(const DoFHandler &dof1, const ConstraintMatrix &constraints1, @@ -1638,10 +1711,8 @@ namespace FETools const ConstraintMatrix &constraints2, OutVector &u1_interpolated) { - // For discontinuous elements - // without constraints take the - // simpler version of the - // back_interpolate function. + // For discontinuous elements without constraints take the simpler version + // of the back_interpolate function. if (dof1.get_fe().dofs_per_vertex==0 && dof2.get_fe().dofs_per_vertex==0 && constraints1.n_constraints()==0 && constraints2.n_constraints()==0) back_interpolate(dof1, u1, dof2.get_fe(), u1_interpolated); @@ -1653,44 +1724,11 @@ namespace FETools Assert(u1_interpolated.size()==dof1.n_dofs(), ExcDimensionMismatch(u1_interpolated.size(), dof1.n_dofs())); - // For continuous elements - // first interpolate to dof2, - // taking into account - // constraints2, and then - // interpolate back to dof1 - // taking into account - // constraints1 -#ifdef DEAL_II_WITH_PETSC - if (dynamic_cast(&u1) != 0) - { - AssertThrow (dynamic_cast(&u1_interpolated) != 0, - ExcMessage("Interpolation can only be done between vectors of same type!")); - - // if u1 is a parallel distributed - // PETSc vector, we create a vector u2 - // with based on the sets of locally owned - // and relevant dofs of dof2 - IndexSet dof2_locally_owned_dofs = dof2.locally_owned_dofs(); - IndexSet dof2_locally_relevant_dofs; - DoFTools::extract_locally_relevant_dofs (dof2, - dof2_locally_relevant_dofs); - - PETScWrappers::MPI::Vector u2_out (dynamic_cast (&u1)->get_mpi_communicator(), - dof2_locally_owned_dofs); - interpolate(dof1, u1, dof2, constraints2, u2_out); - PETScWrappers::MPI::Vector u2 (dynamic_cast (&u1)->get_mpi_communicator(), - dof2_locally_owned_dofs, - dof2_locally_relevant_dofs); - u2 = u2_out; - interpolate(dof2, u2, dof1, constraints1, u1_interpolated); - } - else -#endif - { - Vector u2(dof2.n_dofs()); - interpolate(dof1, u1, dof2, constraints2, u2); - interpolate(dof2, u2, dof1, constraints1, u1_interpolated); - } + // For continuous elements first interpolate to dof2, taking into + // account constraints2, and then interpolate back to dof1 taking into + // account constraints1 + internal::back_interpolate(dof1, constraints1, u1, dof2, constraints2, + u1_interpolated); } } @@ -2630,22 +2668,15 @@ namespace FETools const unsigned int dofs_per_cell = fe.dofs_per_cell; // polynomial degree const unsigned int degree = fe.dofs_per_line+1; - // number of grid points in each - // direction + // number of grid points in each direction const unsigned int n = degree+1; - // the following lines of code are - // somewhat odd, due to the way the - // hierarchic numbering is - // organized. if someone would - // really want to understand these - // lines, you better draw some - // pictures where you indicate the - // indices and orders of vertices, - // lines, etc, along with the - // numbers of the degrees of - // freedom in hierarchical and - // lexicographical order + // the following lines of code are somewhat odd, due to the way the + // hierarchic numbering is organized. if someone would really want to + // understand these lines, you better draw some pictures where you + // indicate the indices and orders of vertices, lines, etc, along with the + // numbers of the degrees of freedom in hierarchical and lexicographical + // order switch (dim) { case 1: diff --git a/deal.II/source/fe/fe_tools.inst.in b/deal.II/source/fe/fe_tools.inst.in index ab7eafa183..22ee6025b0 100644 --- a/deal.II/source/fe/fe_tools.inst.in +++ b/deal.II/source/fe/fe_tools.inst.in @@ -12,6 +12,25 @@ //--------------------------------------------------------------------------- +for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS; Vector : SERIAL_VECTORS) + { + namespace FETools + \{ +#if deal_II_dimension <= deal_II_space_dimension + template + void interpolate + (const DoFHandler &, const Vector &, + const DoFHandler &, Vector &); + + template + void interpolate + (const DoFHandler &, const Vector &, + const DoFHandler &, const ConstraintMatrix &, + Vector &); +#endif + \} + } + for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS) { namespace FETools @@ -30,52 +49,7 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS template void compute_embedding_matrices (const FiniteElement &, - std::vector > >&,bool); - - template - void interpolate - (const DoFHandler &, const Vector &, - const DoFHandler &, Vector &); - - template - void interpolate - (const DoFHandler &, const Vector &, - const DoFHandler &, const ConstraintMatrix &, - Vector &); - - template - void interpolate - (const DoFHandler &, const Vector &, - const DoFHandler &, Vector &); - template - - void interpolate - (const DoFHandler &, const Vector &, - const DoFHandler &, const ConstraintMatrix &, - Vector &); - - - template - void interpolate - (const DoFHandler &, const BlockVector &, - const DoFHandler &, BlockVector &); - - template - void interpolate - (const DoFHandler &, const BlockVector &, - const DoFHandler &, const ConstraintMatrix &, - BlockVector &); - - template - void interpolate - (const DoFHandler &, const BlockVector &, - const DoFHandler &, BlockVector &); - - template - void interpolate - (const DoFHandler &, const BlockVector &, - const DoFHandler &, const ConstraintMatrix &, - BlockVector &); + std::vector > >&,bool); #endif \} } diff --git a/deal.II/source/grid/grid_out.cc b/deal.II/source/grid/grid_out.cc index df21f100a8..837063c9ef 100644 --- a/deal.II/source/grid/grid_out.cc +++ b/deal.II/source/grid/grid_out.cc @@ -1362,8 +1362,8 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const unsigned int n_materials = 0; unsigned int n_levels = 0; unsigned int n_subdomains = 0; - //unsigned int n_level_subdomains = 0; // TODO [CW]: CellAccessor does not seem to possess this attribute ... - + unsigned int n_level_subdomains = 0; + unsigned int n = 0; unsigned int min_level, max_level; @@ -1384,7 +1384,7 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const unsigned int cell_label_font_size; // font size for date, time, legend, and colorbar - unsigned int font_size = static_cast(.5 + (height/100.) * 2.); + unsigned int font_size = static_cast(.5 + (height/100.) * 1.75); // get date and time // time_t time_stamp; @@ -1407,7 +1407,7 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const float x_dimension_perspective, y_dimension_perspective; - Triangulation<2,2>::active_cell_iterator cell = tria.begin_active(), endc = tria.end(); + Triangulation<2,2>::cell_iterator cell = tria.begin(), endc = tria.end(); // auxiliary variables for the bounding box and the range of cell levels double x_min = cell->vertex(0)[0]; @@ -1435,12 +1435,10 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const for (unsigned int subdomain_index = 0; subdomain_index < 256; subdomain_index++) subdomains[subdomain_index] = 0; - // TODO [CW]: not yet implemented ... - /* // auxiliary array for the level subdomains being used int level_subdomains[256]; - for(int level_subdomain_index = 0; level_subdomain_index < 256; level_subdomain_index++) level_subdomains[level_subdomain_index] = 0; - */ + for(int level_subdomain_index = 0; level_subdomain_index < 256; level_subdomain_index++) + level_subdomains[level_subdomain_index] = 0; // We use an active cell iterator to determine the // bounding box of the given triangulation and check @@ -1460,17 +1458,10 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const if ((unsigned int)cell->level() < min_level) min_level = cell->level(); if ((unsigned int)cell->level() > max_level) max_level = cell->level(); - if ((unsigned int)cell->material_id()) materials[(unsigned int)cell->material_id()] = 1; - else if ((unsigned int)cell->material_id() == 0) materials[0] = 1; - - if ((int)cell->level()) levels[(unsigned int)cell->level()] = 1; - else if ((unsigned int)cell->level() == 0) levels[0] = 1; - - if ((unsigned int)cell->subdomain_id()) subdomains[(unsigned int)cell->subdomain_id()] = 1; - else if ((unsigned int)cell->subdomain_id() == 0) subdomains[0] = 1; - - // if((unsigned int)(cell->level_subdomain_id())) level_subdomains[(unsigned int)cell->level_subdomain_id()] = 1; - // else if((unsigned int)(cell->level_subdomain_id()) == 0) level_subdomains[0] = 1; + materials[(unsigned int)cell->material_id()] = 1; + levels[(unsigned int)cell->level()] = 1; + subdomains[cell->subdomain_id()+2] = 1; + level_subdomains[cell->level_subdomain_id()+2] = 1; } x_dimension = x_max - x_min; @@ -1494,13 +1485,11 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const if (subdomains[subdomain_index]) n_subdomains++; } - /* // count the level subdomains being used for(int level_subdomain_index = 0; level_subdomain_index < 256; level_subdomain_index++) { if(level_subdomains[level_subdomain_index]) n_level_subdomains++; } - */ switch (svg_flags.coloring) { @@ -1513,13 +1502,12 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const case GridOutFlags::Svg::subdomain_id: n = n_subdomains; break; - // case GridOutFlags::Svg::level_subdomain_id: n = n_level_subdomains; - // break; + case GridOutFlags::Svg::level_subdomain_id: n = n_level_subdomains; + break; default: break; } - // set the camera position to top view, targeting at the origin camera_position[0] = 0; camera_position[1] = 0; @@ -1535,9 +1523,9 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const camera_focus = .5 * std::max(x_dimension, y_dimension); - float *camera_position_temp = new float[3]; - float *camera_direction_temp = new float[3]; - float *camera_horizontal_temp = new float[3]; + Point<3> camera_position_temp; + Point<3> camera_direction_temp; + Point<3> camera_horizontal_temp; const float angle_factor = 3.14159265 / 180.; @@ -1579,10 +1567,6 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const camera_horizontal[0] = camera_horizontal_temp[0]; camera_horizontal[1] = camera_horizontal_temp[1]; - delete camera_position_temp; - delete camera_direction_temp; - delete camera_horizontal_temp; - // translate the camera to the given triangulation camera_position[0] = x_min + .5 * x_dimension; camera_position[1] = y_min + .5 * y_dimension; @@ -1592,7 +1576,7 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const // determine the bounding box of the given triangulation on the projection plane of the camera viewing system - cell = tria.begin_active(); + cell = tria.begin(); endc = tria.end(); point[0] = cell->vertex(0)[0]; @@ -1672,14 +1656,14 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const x_dimension_perspective = x_max_perspective - x_min_perspective; y_dimension_perspective = y_max_perspective - y_min_perspective; - cell_label_font_size = static_cast(.5 + (height/100.) * 2.25) * 9. * (min_level_min_vertex_distance / std::min(x_dimension, y_dimension)); + cell_label_font_size = static_cast(.5 + (height/100.) * 2.75) * 9. * (min_level_min_vertex_distance / std::min(x_dimension, y_dimension)); // create the svg file with an internal style sheet width = static_cast(.5 + height * (x_dimension_perspective / y_dimension_perspective)); unsigned int additional_width = 0; - if (svg_flags.draw_legend && (svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id)) // || svg_flags.label_level_subdomain_id )) + if (svg_flags.draw_legend && (svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id || svg_flags.label_level_subdomain_id )) { additional_width = static_cast(.5 + height * .4); // additional width for legend } @@ -1783,8 +1767,9 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const case GridOutFlags::Svg::subdomain_id: while (!subdomains[labeling_index]) labeling_index++; break; - // case GridOutFlags::Svg::level_subdomain_id: while(!level_subdomains[labeling_index]) labeling_index++; - // break; + case GridOutFlags::Svg::level_subdomain_id: + while(!level_subdomains[labeling_index]) labeling_index++; + break; default: break; } @@ -1861,10 +1846,11 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const out << (unsigned int)cell->level(); break; case GridOutFlags::Svg::subdomain_id: - out << (unsigned int)cell->subdomain_id(); + out << cell->subdomain_id() + 2; break; - // case GridOutFlags::Svg::level_subdomain_id: out << (unsigned int)cell->level_subdomain_id(); - // break; + case GridOutFlags::Svg::level_subdomain_id: + out << cell->level_subdomain_id() + 2; + break; default: break; } @@ -1931,7 +1917,7 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const out << "\"/>" << '\n'; // label the current cell - if (svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id) // || svg_flags.label_level_subdomain_id) + if (svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id || svg_flags.label_level_subdomain_id) { point[0] = cell->center()[0]; point[1] = cell->center()[1]; @@ -1973,14 +1959,18 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const if (svg_flags.label_subdomain_id) { if (svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id) out << ','; - out << cell->subdomain_id(); + out << static_cast(cell->subdomain_id()); } - // if(svg_flags.label_level_subdomain_id) - // { - // if(svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id) out << ','; - // out << cell->level_subdomain_id(); - // } + if(svg_flags.label_level_subdomain_id) + { + if(svg_flags.label_level_number + || svg_flags.label_cell_index + || svg_flags.label_material_id + || svg_flags.label_subdomain_id) + out << ','; + out << static_cast(cell->level_subdomain_id()); + } out << "" << '\n'; } @@ -2041,7 +2031,7 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const if (!svg_flags.margin) additional_width = static_cast(.5 + (height/100.) * 2.5); // explanation of the cell labeling - if (svg_flags.draw_legend && (svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id)) // || svg_flags.label_level_subdomain_id )) + if (svg_flags.draw_legend && (svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id || svg_flags.label_level_subdomain_id )) { out << " (.5 + (height/100.) * margin_in_percent) << "\" width=\"" << static_cast(.5 + (height/100.) * (40. - margin_in_percent)) << "\" height=\"" << static_cast(.5 + height * .165) << "\"/>" << '\n'; @@ -2059,7 +2049,7 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const << "\" style=\"text-anchor:start; font-style:oblique; font-size:" << font_size << "\">" << "level_number"; - if (svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id) // || svg_flags.label_level_subdomain_id) + if (svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id || svg_flags.label_level_subdomain_id) out << ','; out << "" << '\n'; @@ -2073,7 +2063,7 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const << "\">" << "cell_index"; - if (svg_flags.label_material_id || svg_flags.label_subdomain_id) // || svg_flags.label_level_subdomain_id) + if (svg_flags.label_material_id || svg_flags.label_subdomain_id || svg_flags.label_level_subdomain_id) out << ','; out << "" << '\n'; @@ -2087,7 +2077,7 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const << "\">" << "material_id"; - if (svg_flags.label_subdomain_id) // || svg_flags.label_level_subdomain_id) + if (svg_flags.label_subdomain_id || svg_flags.label_level_subdomain_id) out << ','; out << "" << '\n'; @@ -2101,21 +2091,21 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const << "\">" << "subdomain_id"; - // if(svg_flags.label_level_subdomain_id) - // out << ','; + if(svg_flags.label_level_subdomain_id) + out << ','; out << "" << '\n'; } - // if(svg_flags.label_level_subdomain_id) - // { - // out << " (.5 + (height/100.) * 2.) - // << "\" y=\"" << static_cast(.5 + (height/100.) * margin_in_percent + (++line_offset) * 1.5 * font_size ) - // << "\" style=\"text-anchor:start; font-style:oblique; font-size:" << font_size - // << "\">" - // << "level_subdomain_id" - // << "" << '\n'; - // } + if(svg_flags.label_level_subdomain_id) + { + out << " (.5 + (height/100.) * 2.) + << "\" y=\"" << static_cast(.5 + (height/100.) * margin_in_percent + (++line_offset) * 1.5 * font_size ) + << "\" style=\"text-anchor:start; font-style:oblique; font-size:" << font_size + << "\">" + << "level_subdomain_id" + << "" << '\n'; + } } // show azimuth angle and polar angle as text below the explanation of the cell labeling @@ -2148,8 +2138,8 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const case 3: out << "subdomain_id"; break; - // case 4: out << "level_subdomain_id"; - // break; + case 4: out << "level_subdomain_id"; + break; default: break; } @@ -2174,8 +2164,8 @@ void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const case GridOutFlags::Svg::subdomain_id: while (!subdomains[labeling_index]) labeling_index++; break; - // case GridOutFlags::Svg::level_subdomain_id: while(!level_subdomains[labeling_index]) labeling_index++; - // break; + case GridOutFlags::Svg::level_subdomain_id: while(!level_subdomains[labeling_index]) labeling_index++; + break; default: break; } @@ -2216,6 +2206,7 @@ template <> void GridOut::write_mathgl (const Triangulation<1> &, std::ostream &) const { + // 1d specialization not done yet Assert (false, ExcNotImplemented()); } @@ -2225,7 +2216,6 @@ void GridOut::write_mathgl (const Triangulation &tria, std::ostream &out) const { AssertThrow (out, ExcIO ()); - Assert (dim!=3, ExcNotImplemented ()); // (i) write header if (true) @@ -2255,7 +2245,6 @@ void GridOut::write_mathgl (const Triangulation &tria, // define a helper to keep loops approximately dim-independent // since MathGL labels axes as x, y, z - Assert (dim<=3, ExcInternalError ()); const std::string axes = "xyz"; // (ii) write preamble and graphing tweaks @@ -2266,9 +2255,21 @@ void GridOut::write_mathgl (const Triangulation &tria, if (mathgl_flags.draw_bounding_box) out << "\nbox"; - // Default, cf. MathGL / gnuplot. - out << "\nsetsize 800 800"; - + // deal with dimension dependent preamble; eg. default sizes and + // views for MathGL (cf. gnuplot). + switch (dim) + { + case 2: + out << "\nsetsize 800 800"; + out << "\nrotate 0 0"; + break; + case 3: + out << "\nsetsize 800 800"; + out << "\nrotate 60 40"; + break; + default: + Assert (false, ExcNotImplemented ()); + }; out << "\n"; // (iii) write vertex ordering @@ -2280,8 +2281,19 @@ void GridOut::write_mathgl (const Triangulation &tria, // todo: This denotes the natural ordering of vertices, but it needs // to check this is really always true for a given grid (it's not // true in step-1 grid-2 for instance). - out << "\nlist f 0 1 2 3" - << "\n"; + switch (dim) + { + case 2: + out << "\nlist f 0 1 2 3" + << "\n"; + break; + case 3: + out << "\nlist f 0 2 4 6 | 1 3 5 7 | 0 4 1 5 | 2 6 3 7 | 0 1 2 3 | 4 5 6 7" + << "\n"; + break; + default: + Assert (false, ExcNotImplemented ()); + }; // (iv) write a list of vertices of cells out << "\n#" @@ -2744,7 +2756,7 @@ void GridOut::write_ucd_lines (const Triangulation &tria, } -Point<2> GridOut::svg_project_point(Point<3> point, Point<3> camera_position, Point<3> camera_direction, Point<3> camera_horizontal, float camera_focus) const +Point<2> GridOut::svg_project_point(Point<3> point, Point<3> camera_position, Point<3> camera_direction, Point<3> camera_horizontal, float camera_focus) { // ... Point<3> camera_vertical; diff --git a/deal.II/source/lac/block_sparsity_pattern.cc b/deal.II/source/lac/block_sparsity_pattern.cc index 74db66d190..b09aeb3df9 100644 --- a/deal.II/source/lac/block_sparsity_pattern.cc +++ b/deal.II/source/lac/block_sparsity_pattern.cc @@ -2,7 +2,7 @@ // $Id$ // Version: $Name$ // -// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2011, 2012 by the deal.II authors +// Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2011, 2012, 2013 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -307,7 +307,7 @@ BlockSparsityPatternBase::print_gnuplot(std::ostream &out) const SparsityPatternBase &b = block(ib,jb); for (unsigned int j=0; j(i+k) << std::endl;; + out << l+j << " " << -static_cast(i+k) << std::endl; l += b.n_cols(); } } diff --git a/deal.II/source/lac/chunk_sparsity_pattern.cc b/deal.II/source/lac/chunk_sparsity_pattern.cc index 788ed489b9..5bcb7c0470 100644 --- a/deal.II/source/lac/chunk_sparsity_pattern.cc +++ b/deal.II/source/lac/chunk_sparsity_pattern.cc @@ -138,8 +138,7 @@ ChunkSparsityPattern::operator = (const ChunkSparsityPattern &s) Assert (s.rows == 0, ExcInvalidConstructorCall()); Assert (s.cols == 0, ExcInvalidConstructorCall()); - // perform the checks in the underlying - // object as well + // perform the checks in the underlying object as well sparsity_pattern = s.sparsity_pattern; return *this; @@ -166,8 +165,7 @@ ChunkSparsityPattern::reinit (const unsigned int m, { Assert (chunk_size > 0, ExcInvalidNumber (chunk_size)); - // simply map this function to the - // other @p{reinit} function + // simply map this function to the other @p{reinit} function const std::vector row_lengths (m, max_per_row); reinit (m, n, row_lengths, chunk_size); } @@ -201,31 +199,30 @@ ChunkSparsityPattern::reinit ( this->chunk_size = chunk_size; - // pass down to the necessary information - // to the underlying object. we need to - // calculate how many chunks we need: we - // need to round up (m/chunk_size) and - // (n/chunk_size). rounding up in integer - // arithmetic equals + // pass down to the necessary information to the underlying object. we need + // to calculate how many chunks we need: we need to round up (m/chunk_size) + // and (n/chunk_size). rounding up in integer arithmetic equals // ((m+chunk_size-1)/chunk_size): const unsigned int m_chunks = (m+chunk_size-1) / chunk_size, n_chunks = (n+chunk_size-1) / chunk_size; - // compute the maximum number of chunks in - // each row. the passed array denotes the - // number of entries in each row of the big - // matrix -- in the worst case, these are - // all in independent chunks, so we have to - // calculate it as follows (as an example: - // let chunk_size==2, - // row_lengths={2,2,...}, and entries in - // row zero at columns {0,2} and for row - // one at {4,6} --> we'll need 4 chunks for - // the first chunk row!) : + // compute the maximum number of chunks in each row. the passed array + // denotes the number of entries in each row of the big matrix -- in the + // worst case, these are all in independent chunks, so we have to calculate + // it as follows (as an example: let chunk_size==2, row_lengths={2,2,...}, + // and entries in row zero at columns {0,2} and for row one at {4,6} --> + // we'll need 4 chunks for the first chunk row!) : std::vector chunk_row_lengths (m_chunks, 0); for (unsigned int i=0; i 0, ExcInvalidNumber (chunk_size)); - // count number of entries per row, then - // initialize the underlying sparsity + // count number of entries per row, then initialize the underlying sparsity // pattern std::vector entries_per_row (csp.n_rows(), 0); for (unsigned int row = 0; row + void copy_sparsity (const SP &src, + SparsityPattern &dst) + { + dst.copy_from(src); + } + + void copy_sparsity (const SparsityPattern &src, + SparsityPattern &dst) + { + dst = src; + } + } +} + + + +template +void +ChunkSparsityPattern::create_from +(const unsigned int m, + const unsigned int n, + const Sparsity &sparsity_pattern_for_chunks, + const unsigned int chunk_size_in, + const bool) +{ + Assert (m > (sparsity_pattern_for_chunks.n_rows()-1) * chunk_size_in && + m <= sparsity_pattern_for_chunks.n_rows() * chunk_size_in, + ExcMessage("Number of rows m is not compatible with chunk size " + "and number of rows in sparsity pattern for the chunks.")); + Assert (n > (sparsity_pattern_for_chunks.n_cols()-1) * chunk_size_in && + n <= sparsity_pattern_for_chunks.n_cols() * chunk_size_in, + ExcMessage("Number of columns m is not compatible with chunk size " + "and number of columns in sparsity pattern for the chunks.")); + + internal::copy_sparsity(sparsity_pattern_for_chunks, sparsity_pattern); + chunk_size = chunk_size_in; + rows = m; + cols = n; +} + + + bool ChunkSparsityPattern::empty () const { @@ -422,27 +465,41 @@ ChunkSparsityPattern::exists (const unsigned int i, -unsigned int -ChunkSparsityPattern::row_length (const unsigned int i) const +void +ChunkSparsityPattern::symmetrize () { - Assert (icolumn() != sparsity_pattern.n_cols() - 1) + n += chunk_size; + else + n += (n_cols() % chunk_size); + return n; + } } @@ -457,18 +514,15 @@ ChunkSparsityPattern::n_nonzero_elements () const chunk_size * chunk_size); else - // some of the chunks reach beyond the - // extent of this matrix. this requires a - // somewhat more complicated - // computations, in particular if the + // some of the chunks reach beyond the extent of this matrix. this + // requires a somewhat more complicated computations, in particular if the // columns don't align { if ((n_rows() % chunk_size != 0) && (n_cols() % chunk_size == 0)) { - // columns align with chunks, but - // not rows + // columns align with chunks, but not rows unsigned int n = sparsity_pattern.n_nonzero_elements() * chunk_size * chunk_size; @@ -480,10 +534,9 @@ ChunkSparsityPattern::n_nonzero_elements () const else { - // if columns don't align, then - // just iterate over all chunks and - // see what this leads to. follow the advice in the documentation of - // the sparsity pattern iterators to do the loop over individual rows, + // if columns don't align, then just iterate over all chunks and see + // what this leads to. follow the advice in the documentation of the + // sparsity pattern iterators to do the loop over individual rows, // rather than all elements unsigned int n = 0; @@ -498,17 +551,13 @@ ChunkSparsityPattern::n_nonzero_elements () const else if ((row == sparsity_pattern.n_rows() - 1) && (p->column() != sparsity_pattern.n_cols() - 1)) - // last chunk row, but not - // last chunk column. only a - // smaller number (n_rows % - // chunk_size) of rows - // actually exist + // last chunk row, but not last chunk column. only a smaller + // number (n_rows % chunk_size) of rows actually exist n += (n_rows() % chunk_size) * chunk_size; else if ((row != sparsity_pattern.n_rows() - 1) && (p->column() == sparsity_pattern.n_cols() - 1)) - // last chunk column, but - // not row + // last chunk column, but not row n += (n_cols() % chunk_size) * chunk_size; else // bottom right chunk @@ -561,8 +610,7 @@ ChunkSparsityPattern::print_gnuplot (std::ostream &out) const AssertThrow (out, ExcIO()); - // for each entry in the underlying - // sparsity pattern, repeat everything + // for each entry in the underlying sparsity pattern, repeat everything // chunk_size x chunk_size times for (unsigned int i=0; i(i*chunk_size+e) << std::endl; @@ -593,13 +638,10 @@ ChunkSparsityPattern::print_gnuplot (std::ostream &out) const unsigned int ChunkSparsityPattern::bandwidth () const { - // calculate the bandwidth from that of the - // underlying sparsity pattern. note that - // even if the bandwidth of that is zero, - // then the bandwidth of the chunky pattern - // is chunk_size-1, if it is 1 then the - // chunky pattern has - // chunk_size+(chunk_size-1), etc + // calculate the bandwidth from that of the underlying sparsity + // pattern. note that even if the bandwidth of that is zero, then the + // bandwidth of the chunky pattern is chunk_size-1, if it is 1 then the + // chunky pattern has chunk_size+(chunk_size-1), etc // // we'll cut it off at max(n(),m()) return std::min (sparsity_pattern.bandwidth()*chunk_size @@ -625,8 +667,7 @@ ChunkSparsityPattern::block_write (std::ostream &out) const { AssertThrow (out, ExcIO()); - // first the simple objects, - // bracketed in [...] + // first the simple objects, bracketed in [...] out << '[' << rows << ' ' << cols << ' ' @@ -660,8 +701,7 @@ ChunkSparsityPattern::block_read (std::istream &in) in >> c; AssertThrow (c == '[', ExcIO()); - // then read the underlying sparsity - // pattern + // then read the underlying sparsity pattern sparsity_pattern.block_read (in); in >> c; @@ -681,6 +721,10 @@ ChunkSparsityPattern::memory_consumption () const // explicit instantiations template +void ChunkSparsityPattern::copy_from (const SparsityPattern &, + const unsigned int, + const bool); +template void ChunkSparsityPattern::copy_from (const CompressedSparsityPattern &, const unsigned int, const bool); @@ -693,6 +737,34 @@ void ChunkSparsityPattern::copy_from (const Com const unsigned int, const bool); template +void ChunkSparsityPattern::create_from +(const unsigned int, + const unsigned int, + const SparsityPattern &, + const unsigned int, + const bool); +template +void ChunkSparsityPattern::create_from +(const unsigned int, + const unsigned int, + const CompressedSparsityPattern &, + const unsigned int, + const bool); +template +void ChunkSparsityPattern::create_from +(const unsigned int, + const unsigned int, + const CompressedSetSparsityPattern &, + const unsigned int, + const bool); +template +void ChunkSparsityPattern::create_from +(const unsigned int, + const unsigned int, + const CompressedSimpleSparsityPattern &, + const unsigned int, + const bool); +template void ChunkSparsityPattern::copy_from (const FullMatrix &, const unsigned int, const bool); diff --git a/deal.II/source/lac/constraint_matrix.cc b/deal.II/source/lac/constraint_matrix.cc index 711c024189..3e7c63c384 100644 --- a/deal.II/source/lac/constraint_matrix.cc +++ b/deal.II/source/lac/constraint_matrix.cc @@ -1,7 +1,7 @@ //--------------------------------------------------------------------------- // $Id$ // -// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 by the deal.II authors +// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 by the deal.II authors // // This file is subject to QPL and may not be distributed // without copyright and license information. Please refer @@ -1518,266 +1518,6 @@ void ConstraintMatrix::condense (BlockCompressedSimpleSparsityPattern &sparsity) -#ifdef DEAL_II_WITH_TRILINOS - -// this is a specialization for a parallel (non-block) Trilinos vector. The -// basic idea is to just work on the local range of the vector. But we need -// access to values that the local nodes are constrained to. - -template<> -void -ConstraintMatrix::distribute (TrilinosWrappers::MPI::Vector &vec) const -{ - Assert (sorted==true, ExcMatrixIsClosed()); - - //TODO: not implemented yet, we need to fix LocalRange() first to only - //include "owned" indices. For this we need to keep track of the owned - //indices, because Trilinos doesn't. Use same constructor interface as in - //PETSc with two IndexSets! - AssertThrow (vec.vector_partitioner().IsOneToOne(), - ExcMessage ("Distribute does not work on vectors with overlapping parallel partitioning.")); - - typedef std::vector::const_iterator constraint_iterator; - ConstraintLine index_comparison; - index_comparison.line = vec.local_range().first; - const constraint_iterator begin_my_constraints = - Utilities::lower_bound (lines.begin(),lines.end(),index_comparison); - - index_comparison.line = vec.local_range().second; - const constraint_iterator end_my_constraints - = Utilities::lower_bound(lines.begin(),lines.end(),index_comparison); - - // Here we search all the indices that we need to have read-access to - - // the local nodes and all the nodes that the constraints indicate. - IndexSet my_indices (vec.size()); - { - const std::pair - local_range = vec.local_range(); - - my_indices.add_range (local_range.first, local_range.second); - - std::set individual_indices; - for (constraint_iterator it = begin_my_constraints; - it != end_my_constraints; ++it) - for (unsigned int i=0; ientries.size(); ++i) - if ((it->entries[i].first < local_range.first) - || - (it->entries[i].first >= local_range.second)) - individual_indices.insert (it->entries[i].first); - - my_indices.add_indices (individual_indices.begin(), - individual_indices.end()); - } - -#ifdef DEAL_II_WITH_MPI - const Epetra_MpiComm *mpi_comm - = dynamic_cast(&vec.trilinos_vector().Comm()); - - Assert (mpi_comm != 0, ExcInternalError()); - - TrilinosWrappers::MPI::Vector vec_distribute - (my_indices.make_trilinos_map (mpi_comm->Comm(), true)); -#else - TrilinosWrappers::MPI::Vector vec_distribute - (my_indices.make_trilinos_map (MPI_COMM_WORLD, true)); -#endif - - // here we import the data - vec_distribute.reinit(vec,false,true); - - for (constraint_iterator it = begin_my_constraints; - it != end_my_constraints; ++it) - { - // fill entry in line next_constraint.line by adding the different - // contributions - double new_value = it->inhomogeneity; - for (unsigned int i=0; ientries.size(); ++i) - new_value += (vec_distribute(it->entries[i].first) * - it->entries[i].second); - vec(it->line) = new_value; - } - - // some processes might not apply constraints, so we need to explicitly - // state, that the others are doing an insert here: - vec.compress (::dealii::VectorOperation::insert); -} - - - -template<> -void -ConstraintMatrix::distribute (TrilinosWrappers::MPI::BlockVector &vec) const -{ - Assert (sorted==true, ExcMatrixIsClosed()); - - IndexSet my_indices (vec.size()); - for (unsigned int block=0; block::const_iterator constraint_iterator; - ConstraintLine index_comparison; - index_comparison.line = vec.block(block).local_range().first - +vec.get_block_indices().block_start(block); - const constraint_iterator begin_my_constraints = - Utilities::lower_bound (lines.begin(),lines.end(),index_comparison); - - index_comparison.line = vec.block(block).local_range().second - +vec.get_block_indices().block_start(block); - - const constraint_iterator end_my_constraints - = Utilities::lower_bound(lines.begin(),lines.end(),index_comparison); - - // Here we search all the indices that we need to have read-access to - // - the local nodes and all the nodes that the constraints indicate. - // No caching done yet. would need some more clever data structures - // for doing that. - const std::pair - local_range = vec.block(block).local_range(); - - my_indices.add_range (local_range.first, local_range.second); - - std::set individual_indices; - for (constraint_iterator it = begin_my_constraints; - it != end_my_constraints; ++it) - for (unsigned int i=0; ientries.size(); ++i) - if ((it->entries[i].first < local_range.first) - || - (it->entries[i].first >= local_range.second)) - individual_indices.insert (it->entries[i].first); - - my_indices.add_indices (individual_indices.begin(), - individual_indices.end()); - } - -#ifdef DEAL_II_WITH_MPI - const Epetra_MpiComm *mpi_comm - = dynamic_cast(&vec.block(0).trilinos_vector().Comm()); - - Assert (mpi_comm != 0, ExcInternalError()); - - TrilinosWrappers::MPI::Vector vec_distribute - (my_indices.make_trilinos_map (mpi_comm->Comm(), true)); -#else - TrilinosWrappers::MPI::Vector vec_distribute - (my_indices.make_trilinos_map (MPI_COMM_WORLD, true)); -#endif - - // here we import the data - vec_distribute.reinit(vec,true); - - for (unsigned int block=0; block::const_iterator constraint_iterator; - ConstraintLine index_comparison; - index_comparison.line = vec.block(block).local_range().first - +vec.get_block_indices().block_start(block); - const constraint_iterator begin_my_constraints = - Utilities::lower_bound (lines.begin(),lines.end(),index_comparison); - - index_comparison.line = vec.block(block).local_range().second - +vec.get_block_indices().block_start(block); - - const constraint_iterator end_my_constraints - = Utilities::lower_bound(lines.begin(),lines.end(),index_comparison); - - for (constraint_iterator it = begin_my_constraints; - it != end_my_constraints; ++it) - { - // fill entry in line next_constraint.line by adding the - // different contributions - double new_value = it->inhomogeneity; - for (unsigned int i=0; ientries.size(); ++i) - new_value += (vec_distribute(it->entries[i].first) * - it->entries[i].second); - vec(it->line) = new_value; - } - vec.block(block).compress(::dealii::VectorOperation::insert); - } -} - -#endif - -#ifdef DEAL_II_WITH_PETSC - -// this is a specialization for a parallel (non-block) PETSc vector. The -// basic idea is to just work on the local range of the vector. But we need -// access to values that the local nodes are constrained to. - -template<> -void -ConstraintMatrix::distribute (PETScWrappers::MPI::Vector &vec) const -{ - Assert (sorted==true, ExcMatrixIsClosed()); - - typedef std::vector::const_iterator constraint_iterator; - ConstraintLine index_comparison; - index_comparison.line = vec.local_range().first; - const constraint_iterator begin_my_constraints = - Utilities::lower_bound (lines.begin(),lines.end(),index_comparison); - - index_comparison.line = vec.local_range().second; - const constraint_iterator end_my_constraints - = Utilities::lower_bound(lines.begin(),lines.end(),index_comparison); - - // all indices we need to read from - IndexSet my_indices (vec.size()); - - const std::pair - local_range = vec.local_range(); - - my_indices.add_range (local_range.first, local_range.second); - - std::set individual_indices; - for (constraint_iterator it = begin_my_constraints; - it != end_my_constraints; ++it) - for (unsigned int i=0; ientries.size(); ++i) - if ((it->entries[i].first < local_range.first) - || - (it->entries[i].first >= local_range.second)) - individual_indices.insert (it->entries[i].first); - - my_indices.add_indices (individual_indices.begin(), - individual_indices.end()); - - IndexSet local_range_is (vec.size()); - local_range_is.add_range(local_range.first, local_range.second); - - - // create a vector and import those indices - PETScWrappers::MPI::Vector ghost_vec (vec.get_mpi_communicator(), - local_range_is, - my_indices); - ghost_vec = vec; - ghost_vec.update_ghost_values(); - - // finally do the distribution on own constraints - for (constraint_iterator it = begin_my_constraints; - it != end_my_constraints; ++it) - { - // fill entry in line next_constraint.line by adding the different - // contributions - PetscScalar new_value = it->inhomogeneity; - for (unsigned int i=0; ientries.size(); ++i) - new_value += (PetscScalar(ghost_vec(it->entries[i].first)) * - it->entries[i].second); - vec(it->line) = new_value; - } - - vec.compress (VectorOperation::insert); -} - - -template<> -void -ConstraintMatrix::distribute (PETScWrappers::MPI::BlockVector &/*vec*/) const -{ - Assert (sorted==true, ExcMatrixIsClosed()); - AssertThrow (false, ExcNotImplemented()); -} - -#endif - - - bool ConstraintMatrix::is_identity_constrained (const unsigned int index) const { if (is_constrained(index) == false) @@ -1919,8 +1659,7 @@ ConstraintMatrix::memory_consumption () const VectorType &, \ const FullMatrix &) const; \ template void ConstraintMatrix::distribute(const VectorType &condensed,\ - VectorType &uncondensed) const;\ - template void ConstraintMatrix::distribute(VectorType &vec) const + VectorType &uncondensed) const #define PARALLEL_VECTOR_FUNCTIONS(VectorType) \ template void ConstraintMatrix:: \ @@ -1930,11 +1669,6 @@ ConstraintMatrix::memory_consumption () const const FullMatrix &) const -// TODO: Can PETSc really do all the operations required by the above -// condense/distribute function etc also on distributed vectors? Trilinos -// can't do that - we have to rewrite those functions by hand if we want to -// use them. The key is to use local ranges etc., which still needs to be -// implemented. #ifdef DEAL_II_WITH_PETSC VECTOR_FUNCTIONS(PETScWrappers::MPI::Vector); VECTOR_FUNCTIONS(PETScWrappers::MPI::BlockVector); diff --git a/deal.II/source/lac/constraint_matrix.inst.in b/deal.II/source/lac/constraint_matrix.inst.in index 400f44cd4f..1a69fff4a2 100644 --- a/deal.II/source/lac/constraint_matrix.inst.in +++ b/deal.II/source/lac/constraint_matrix.inst.in @@ -5,7 +5,6 @@ for (S: REAL_SCALARS; T : DEAL_II_VEC_TEMPLATES) template void ConstraintMatrix::distribute_local_to_global > ( const Vector&, const std::vector &, T &, const FullMatrix&) const; template void ConstraintMatrix::distribute >(const T &, T &) const; - template void ConstraintMatrix::distribute >(T &) const; template void ConstraintMatrix::set_zero >(T &) const; } @@ -17,7 +16,6 @@ for (S: REAL_SCALARS; T : DEAL_II_VEC_TEMPLATES) template void ConstraintMatrix::distribute_local_to_global > ( const Vector&, const std::vector &, parallel::distributed::T &, const FullMatrix&) const; template void ConstraintMatrix::distribute >(const parallel::distributed::T &, parallel::distributed::T &) const; - template void ConstraintMatrix::distribute >(parallel::distributed::T &) const; template void ConstraintMatrix::set_zero >(parallel::distributed::T &) const; } @@ -29,7 +27,6 @@ for (V: EXTERNAL_SEQUENTIAL_VECTORS) template void ConstraintMatrix::distribute_local_to_global ( const Vector&, const std::vector &, V&, const FullMatrix&) const; template void ConstraintMatrix::distribute(const V&, V&) const; - template void ConstraintMatrix::distribute(V&) const; template void ConstraintMatrix::set_zero(V&) const; } @@ -57,3 +54,9 @@ for (S1 : REAL_SCALARS; S2 : REAL_SCALARS) template void ConstraintMatrix::condense >( const SparseMatrix&, const BlockVector&, SparseMatrix &, BlockVector&) const; } + + +for (Vec : SERIAL_VECTORS) + { + template void ConstraintMatrix::distribute(Vec &) const; + } diff --git a/deal.II/source/lac/slepc_solver.cc b/deal.II/source/lac/slepc_solver.cc index ded535c6bc..4640a0e50c 100644 --- a/deal.II/source/lac/slepc_solver.cc +++ b/deal.II/source/lac/slepc_solver.cc @@ -109,7 +109,7 @@ namespace SLEPcWrappers } void - SolverBase::solve (const unsigned int n_eigenvectors, + SolverBase::solve (const unsigned int n_eigenpairs, unsigned int *n_converged) { int ierr; @@ -163,7 +163,7 @@ namespace SLEPcWrappers AssertThrow (ierr == 0, ExcSLEPcError(ierr)); // set number of eigenvectors to compute - ierr = EPSSetDimensions (solver_data->eps, n_eigenvectors, + ierr = EPSSetDimensions (solver_data->eps, n_eigenpairs, PETSC_DECIDE, PETSC_DECIDE); AssertThrow (ierr == 0, ExcSLEPcError(ierr)); @@ -186,8 +186,7 @@ namespace SLEPcWrappers // get number of converged eigenstates ierr = EPSGetConverged (solver_data->eps, - reinterpret_cast(n_converged) - ); + reinterpret_cast(n_converged)); AssertThrow (ierr == 0, ExcSLEPcError(ierr)); int n_iterations = 0; @@ -220,18 +219,31 @@ namespace SLEPcWrappers void SolverBase::get_eigenpair (const unsigned int index, -#ifndef PETSC_USE_COMPLEX - double &kr, -#else - std::complex &kr, -#endif - PETScWrappers::VectorBase &vr) + double &eigenvalues, + PETScWrappers::VectorBase &eigenvectors) { AssertThrow (solver_data.get() != 0, ExcSLEPcWrappersUsageError()); // get converged eigenpair int ierr = EPSGetEigenpair (solver_data->eps, index, - &kr, PETSC_NULL, vr, PETSC_NULL); + &eigenvalues, PETSC_NULL, + eigenvectors, PETSC_NULL); + AssertThrow (ierr == 0, ExcSLEPcError(ierr)); + } + + void + SolverBase::get_eigenpair (const unsigned int index, + double &real_eigenvalues, + double &imag_eigenvalues, + PETScWrappers::VectorBase &real_eigenvectors, + PETScWrappers::VectorBase &imag_eigenvectors) + { + AssertThrow (solver_data.get() != 0, ExcSLEPcWrappersUsageError()); + + // get converged eigenpair + int ierr = EPSGetEigenpair (solver_data->eps, index, + &real_eigenvalues, &imag_eigenvalues, + real_eigenvectors, imag_eigenvectors); AssertThrow (ierr == 0, ExcSLEPcError(ierr)); } @@ -287,8 +299,8 @@ namespace SLEPcWrappers int SolverBase::convergence_test (EPS /*eps */, - PetscScalar /*kr */, - PetscScalar /*ki */, + PetscScalar /*real_eigenvalue */, + PetscScalar /*imag_eigenvalue */, PetscReal /*residual_norm */, PetscReal */*estimated_error */, void */*solver_control_x*/) diff --git a/deal.II/source/lac/trilinos_precondition.cc b/deal.II/source/lac/trilinos_precondition.cc index b57d0d403e..e812285533 100644 --- a/deal.II/source/lac/trilinos_precondition.cc +++ b/deal.II/source/lac/trilinos_precondition.cc @@ -566,6 +566,7 @@ namespace TrilinosWrappers static_cast(additional_data.smoother_overlap)); parameter_list.set("aggregation: threshold", additional_data.aggregation_threshold); + parameter_list.set("coarse: max size", 2000); if (additional_data.output_details) parameter_list.set("ML output", 10); diff --git a/deal.II/source/lac/trilinos_sparse_matrix.cc b/deal.II/source/lac/trilinos_sparse_matrix.cc index 29030f6e47..f79572e343 100644 --- a/deal.II/source/lac/trilinos_sparse_matrix.cc +++ b/deal.II/source/lac/trilinos_sparse_matrix.cc @@ -325,7 +325,6 @@ namespace TrilinosWrappers // release memory before reallocation matrix.reset (); - temp_vector.clear (); matrix.reset (new Epetra_FECrsMatrix(*m.matrix)); } @@ -398,7 +397,6 @@ namespace TrilinosWrappers const bool exchange_data) { // release memory before reallocation - temp_vector.clear(); matrix.reset(); // if we want to exchange data, build a usual Trilinos sparsity pattern @@ -496,7 +494,6 @@ namespace TrilinosWrappers void SparseMatrix::reinit (const SparsityPattern &sparsity_pattern) { - temp_vector.clear (); matrix.reset (); // reinit with a (parallel) Trilinos @@ -514,7 +511,6 @@ namespace TrilinosWrappers SparseMatrix::reinit (const SparseMatrix &sparse_matrix) { column_space_map.reset (new Epetra_Map (sparse_matrix.domain_partitioner())); - temp_vector.clear (); matrix.reset (); matrix.reset (new Epetra_FECrsMatrix (Copy, sparse_matrix.trilinos_sparsity_pattern(), false)); @@ -672,7 +668,6 @@ namespace TrilinosWrappers const Epetra_CrsGraph *graph = &input_matrix.Graph(); - temp_vector.clear (); matrix.reset (); matrix.reset (new Epetra_FECrsMatrix(Copy, *graph, false)); @@ -702,7 +697,6 @@ namespace TrilinosWrappers // empty matrix. column_space_map.reset (new Epetra_Map (0, 0, Utilities::Trilinos::comm_self())); - temp_vector.clear(); matrix.reset (new Epetra_FECrsMatrix(View, *column_space_map, 0)); matrix->FillComplete(); diff --git a/deal.II/source/multigrid/mg_tools.cc b/deal.II/source/multigrid/mg_tools.cc index 1fe5ef922d..49660106ce 100644 --- a/deal.II/source/multigrid/mg_tools.cc +++ b/deal.II/source/multigrid/mg_tools.cc @@ -554,7 +554,8 @@ namespace MGTools typename DH::cell_iterator cell = dof.begin(level), endc = dof.end(level); for (; cell!=endc; ++cell) - if (dof.get_tria().locally_owned_subdomain()==numbers::invalid_subdomain_id || cell->level_subdomain_id()==dof.get_tria().locally_owned_subdomain()) + if (dof.get_tria().locally_owned_subdomain()==numbers::invalid_subdomain_id + || cell->level_subdomain_id()==dof.get_tria().locally_owned_subdomain()) { cell->get_mg_dof_indices (dofs_on_this_cell); // make sparsity pattern for this cell @@ -1201,6 +1202,9 @@ namespace MGTools endc = dof.end(); for (; cell!=endc; ++cell) { + if (dof.get_tria().locally_owned_subdomain()!=numbers::invalid_subdomain_id + && cell->level_subdomain_id()!=dof.get_tria().locally_owned_subdomain()) + continue; const FiniteElement &fe = cell->get_fe(); const unsigned int level = cell->level(); local_dofs.resize(fe.dofs_per_face); @@ -1232,6 +1236,8 @@ namespace MGTools cell = dof.begin(), endc = dof.end(); for (; cell!=endc; ++cell) + if (dof.get_tria().locally_owned_subdomain()==numbers::invalid_subdomain_id + || cell->level_subdomain_id()==dof.get_tria().locally_owned_subdomain()) for (unsigned int face_no = 0; face_no < GeometryInfo::faces_per_cell; ++face_no) { @@ -1430,6 +1436,10 @@ namespace MGTools for (; cell!=endc; ++cell) { + if (mg_dof_handler.get_tria().locally_owned_subdomain()!=numbers::invalid_subdomain_id + && cell->level_subdomain_id()!=mg_dof_handler.get_tria().locally_owned_subdomain()) + continue; + std::fill (cell_dofs.begin(), cell_dofs.end(), false); for (unsigned int face_nr=0; face_nr::faces_per_cell; ++face_nr) @@ -1484,6 +1494,10 @@ namespace MGTools for (; cell!=endc; ++cell) { + if (mg_dof_handler.get_tria().locally_owned_subdomain()!=numbers::invalid_subdomain_id + && cell->level_subdomain_id()!=mg_dof_handler.get_tria().locally_owned_subdomain()) + continue; + std::fill (cell_dofs.begin(), cell_dofs.end(), false); std::fill (cell_dofs_interface.begin(), cell_dofs_interface.end(), false); @@ -1571,6 +1585,10 @@ namespace MGTools for (; cell!=endc; ++cell) { + if (mg_dof_handler.get_tria().locally_owned_subdomain()!=numbers::invalid_subdomain_id + && cell->level_subdomain_id()!=mg_dof_handler.get_tria().locally_owned_subdomain()) + continue; + bool has_coarser_neighbor = false; std::fill (cell_dofs.begin(), cell_dofs.end(), false); diff --git a/deal.II/source/multigrid/mg_transfer_prebuilt.cc b/deal.II/source/multigrid/mg_transfer_prebuilt.cc index 933db2ba1b..4415dc60fd 100644 --- a/deal.II/source/multigrid/mg_transfer_prebuilt.cc +++ b/deal.II/source/multigrid/mg_transfer_prebuilt.cc @@ -142,7 +142,10 @@ void MGTransferPrebuilt::build_matrices ( std::vector entries (dofs_per_cell); for (typename DoFHandler::cell_iterator cell=mg_dof.begin(level); cell != mg_dof.end(level); ++cell) - if (cell->has_children()) + if (cell->has_children() && + ( mg_dof.get_tria().locally_owned_subdomain()==numbers::invalid_subdomain_id + || cell->level_subdomain_id()==mg_dof.get_tria().locally_owned_subdomain() + )) { cell->get_mg_dof_indices (dof_indices_parent); @@ -175,15 +178,21 @@ void MGTransferPrebuilt::build_matrices ( } } } - - prolongation_sparsities[level]->copy_from (csp); + + internal::MatrixSelector::reinit(*prolongation_matrices[level], + *prolongation_sparsities[level], + level, + csp, + mg_dof); csp.reinit(0,0); - prolongation_matrices[level]->reinit (*prolongation_sparsities[level]); // now actually build the matrices for (typename DoFHandler::cell_iterator cell=mg_dof.begin(level); cell != mg_dof.end(level); ++cell) - if (cell->has_children()) + if (cell->has_children() && + (mg_dof.get_tria().locally_owned_subdomain()==numbers::invalid_subdomain_id + || cell->level_subdomain_id()==mg_dof.get_tria().locally_owned_subdomain()) + ) { cell->get_mg_dof_indices (dof_indices_parent); @@ -283,6 +292,10 @@ void MGTransferPrebuilt::build_matrices ( // by restricting from fine level. for (; level_cell!=level_end; ++level_cell) { + if (mg_dof.get_tria().locally_owned_subdomain()!=numbers::invalid_subdomain_id + && level_cell->level_subdomain_id()!=mg_dof.get_tria().locally_owned_subdomain()) + continue; + // get the dof numbers of // this cell for the global // and the level-wise @@ -323,6 +336,17 @@ void MGTransferPrebuilt::build_matrices ( } +template +void +MGTransferPrebuilt::print_matrices (std::ostream& os) const +{ + for (unsigned int level = 0;levelprint(os); + os << std::endl; + } +} // explicit instantiation diff --git a/deal.II/source/numerics/matrix_tools.cc b/deal.II/source/numerics/matrix_tools.cc index 776031f0f5..77fa2af43d 100644 --- a/deal.II/source/numerics/matrix_tools.cc +++ b/deal.II/source/numerics/matrix_tools.cc @@ -209,6 +209,7 @@ namespace MatrixCreator std::vector dof_indices; FullMatrix cell_matrix; dealii::Vector cell_rhs; + const ConstraintMatrix *constraints; }; } @@ -592,10 +593,15 @@ namespace MatrixCreator (data.cell_rhs.size() == dofs_per_cell), ExcInternalError()); - matrix->add(data.dof_indices, data.cell_matrix); if (right_hand_side != 0) - for (unsigned int i=0; idistribute_local_to_global(data.cell_matrix, + data.cell_rhs, + data.dof_indices, + *matrix, *right_hand_side); + else + data.constraints->distribute_local_to_global(data.cell_matrix, + data.dof_indices, + *matrix); } } } @@ -609,7 +615,8 @@ namespace MatrixCreator const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { Assert (matrix.m() == dof.n_dofs(), ExcDimensionMismatch (matrix.m(), dof.n_dofs())); @@ -631,6 +638,7 @@ namespace MatrixCreator assembler_data.fe_collection.max_dofs_per_cell()); copy_data.cell_rhs.reinit (assembler_data.fe_collection.max_dofs_per_cell()); copy_data.dof_indices.resize (assembler_data.fe_collection.max_dofs_per_cell()); + copy_data.constraints = &constraints; WorkStream::run (dof.begin_active(), static_cast::active_cell_iterator>(dof.end()), @@ -648,10 +656,11 @@ namespace MatrixCreator void create_mass_matrix (const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { create_mass_matrix(StaticMappingQ1::mapping, dof, - q, matrix, coefficient); + q, matrix, coefficient, constraints); } @@ -663,7 +672,8 @@ namespace MatrixCreator SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { Assert (matrix.m() == dof.n_dofs(), ExcDimensionMismatch (matrix.m(), dof.n_dofs())); @@ -684,6 +694,7 @@ namespace MatrixCreator assembler_data.fe_collection.max_dofs_per_cell()); copy_data.cell_rhs.reinit (assembler_data.fe_collection.max_dofs_per_cell()); copy_data.dof_indices.resize (assembler_data.fe_collection.max_dofs_per_cell()); + copy_data.constraints = &constraints; WorkStream::run (dof.begin_active(), static_cast::active_cell_iterator>(dof.end()), @@ -703,10 +714,12 @@ namespace MatrixCreator SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { create_mass_matrix(StaticMappingQ1::mapping, - dof, q, matrix, rhs, rhs_vector, coefficient); + dof, q, matrix, rhs, rhs_vector, coefficient, + constraints); } @@ -716,7 +729,8 @@ namespace MatrixCreator const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { Assert (matrix.m() == dof.n_dofs(), ExcDimensionMismatch (matrix.m(), dof.n_dofs())); @@ -734,6 +748,7 @@ namespace MatrixCreator assembler_data.fe_collection.max_dofs_per_cell()); copy_data.cell_rhs.reinit (assembler_data.fe_collection.max_dofs_per_cell()); copy_data.dof_indices.resize (assembler_data.fe_collection.max_dofs_per_cell()); + copy_data.constraints = &constraints; WorkStream::run (dof.begin_active(), static_cast::active_cell_iterator>(dof.end()), @@ -748,24 +763,27 @@ namespace MatrixCreator template - void create_mass_matrix (const hp::DoFHandler &dof, - const hp::QCollection &q, + void create_mass_matrix (const hp::DoFHandler &dof, + const hp::QCollection &q, SparseMatrix &matrix, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { - create_mass_matrix(hp::StaticMappingQ1::mapping_collection, dof, q, matrix, coefficient); + create_mass_matrix(hp::StaticMappingQ1::mapping_collection, + dof, q, matrix, coefficient, constraints); } template - void create_mass_matrix (const hp::MappingCollection &mapping, - const hp::DoFHandler &dof, + void create_mass_matrix (const hp::MappingCollection &mapping, + const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { Assert (matrix.m() == dof.n_dofs(), ExcDimensionMismatch (matrix.m(), dof.n_dofs())); @@ -783,6 +801,7 @@ namespace MatrixCreator assembler_data.fe_collection.max_dofs_per_cell()); copy_data.cell_rhs.reinit (assembler_data.fe_collection.max_dofs_per_cell()); copy_data.dof_indices.resize (assembler_data.fe_collection.max_dofs_per_cell()); + copy_data.constraints = &constraints; WorkStream::run (dof.begin_active(), static_cast::active_cell_iterator>(dof.end()), @@ -797,15 +816,16 @@ namespace MatrixCreator template - void create_mass_matrix (const hp::DoFHandler &dof, - const hp::QCollection &q, + void create_mass_matrix (const hp::DoFHandler &dof, + const hp::QCollection &q, SparseMatrix &matrix, - const Function &rhs, + const Function &rhs, Vector &rhs_vector, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { create_mass_matrix(hp::StaticMappingQ1::mapping_collection, dof, q, - matrix, rhs, rhs_vector, coefficient); + matrix, rhs, rhs_vector, coefficient, constraints); } @@ -1706,7 +1726,8 @@ namespace MatrixCreator const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { Assert (matrix.m() == dof.n_dofs(), ExcDimensionMismatch (matrix.m(), dof.n_dofs())); @@ -1727,6 +1748,7 @@ namespace MatrixCreator assembler_data.fe_collection.max_dofs_per_cell()); copy_data.cell_rhs.reinit (assembler_data.fe_collection.max_dofs_per_cell()); copy_data.dof_indices.resize (assembler_data.fe_collection.max_dofs_per_cell()); + copy_data.constraints = &constraints; void (*copy_local_to_global) (const MatrixCreator::internal::AssemblerData::CopyData &, SparseMatrix *, @@ -1749,7 +1771,8 @@ namespace MatrixCreator void create_laplace_matrix (const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { create_laplace_matrix(StaticMappingQ1::mapping, dof, q, matrix, coefficient); } @@ -1763,7 +1786,8 @@ namespace MatrixCreator SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { Assert (matrix.m() == dof.n_dofs(), ExcDimensionMismatch (matrix.m(), dof.n_dofs())); @@ -1784,6 +1808,7 @@ namespace MatrixCreator assembler_data.fe_collection.max_dofs_per_cell()); copy_data.cell_rhs.reinit (assembler_data.fe_collection.max_dofs_per_cell()); copy_data.dof_indices.resize (assembler_data.fe_collection.max_dofs_per_cell()); + copy_data.constraints = &constraints; void (*copy_local_to_global) (const MatrixCreator::internal::AssemblerData::CopyData &, SparseMatrix *, @@ -1808,7 +1833,8 @@ namespace MatrixCreator SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { create_laplace_matrix(StaticMappingQ1::mapping, dof, q, matrix, rhs, rhs_vector, coefficient); @@ -1821,7 +1847,8 @@ namespace MatrixCreator const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { Assert (matrix.m() == dof.n_dofs(), ExcDimensionMismatch (matrix.m(), dof.n_dofs())); @@ -1839,6 +1866,7 @@ namespace MatrixCreator assembler_data.fe_collection.max_dofs_per_cell()); copy_data.cell_rhs.reinit (assembler_data.fe_collection.max_dofs_per_cell()); copy_data.dof_indices.resize (assembler_data.fe_collection.max_dofs_per_cell()); + copy_data.constraints = &constraints; void (*copy_local_to_global) (const MatrixCreator::internal::AssemblerData::CopyData &, SparseMatrix *, @@ -1861,7 +1889,8 @@ namespace MatrixCreator void create_laplace_matrix (const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { create_laplace_matrix(hp::StaticMappingQ1::mapping_collection, dof, q, matrix, coefficient); } @@ -1875,7 +1904,8 @@ namespace MatrixCreator SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { Assert (matrix.m() == dof.n_dofs(), ExcDimensionMismatch (matrix.m(), dof.n_dofs())); @@ -1893,6 +1923,7 @@ namespace MatrixCreator assembler_data.fe_collection.max_dofs_per_cell()); copy_data.cell_rhs.reinit (assembler_data.fe_collection.max_dofs_per_cell()); copy_data.dof_indices.resize (assembler_data.fe_collection.max_dofs_per_cell()); + copy_data.constraints = &constraints; void (*copy_local_to_global) (const MatrixCreator::internal::AssemblerData::CopyData &, SparseMatrix *, @@ -1917,7 +1948,8 @@ namespace MatrixCreator SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function *const coefficient) + const Function *const coefficient, + const ConstraintMatrix &constraints) { create_laplace_matrix(hp::StaticMappingQ1::mapping_collection, dof, q, matrix, rhs, rhs_vector, coefficient); diff --git a/deal.II/source/numerics/matrix_tools.inst.in b/deal.II/source/numerics/matrix_tools.inst.in index ad686e7118..07c6301155 100644 --- a/deal.II/source/numerics/matrix_tools.inst.in +++ b/deal.II/source/numerics/matrix_tools.inst.in @@ -21,13 +21,15 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_mass_matrix (const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_mass_matrix (const Mapping &mapping, @@ -36,7 +38,8 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_mass_matrix (const DoFHandler &dof, @@ -44,7 +47,8 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template @@ -53,13 +57,15 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_mass_matrix (const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_mass_matrix (const Mapping &mapping, @@ -68,7 +74,8 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_mass_matrix (const DoFHandler &dof, @@ -76,7 +83,8 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_boundary_mass_matrix @@ -121,13 +129,15 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_mass_matrix (const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_mass_matrix (const hp::MappingCollection &mapping, @@ -136,7 +146,8 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_mass_matrix (const hp::DoFHandler &dof, @@ -144,7 +155,8 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template @@ -153,13 +165,15 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_mass_matrix (const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_mass_matrix (const hp::MappingCollection &mapping, @@ -168,7 +182,8 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_mass_matrix (const hp::DoFHandler &dof, @@ -176,7 +191,8 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); #if deal_II_dimension != 1 @@ -216,14 +232,16 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS (const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_laplace_matrix (const Mapping &mapping, const DoFHandler &dof, const Quadrature &q, SparseMatrix &matrix, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_laplace_matrix (const Mapping &mapping, @@ -232,7 +250,8 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_laplace_matrix (const DoFHandler &dof, @@ -240,7 +259,8 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); // hp versions of create_laplace_matrix template @@ -248,14 +268,16 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS (const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_laplace_matrix (const hp::MappingCollection &mapping, const hp::DoFHandler &dof, const hp::QCollection &q, SparseMatrix &matrix, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_laplace_matrix (const hp::MappingCollection &mapping, @@ -264,7 +286,8 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); template void MatrixCreator::create_laplace_matrix (const hp::DoFHandler &dof, @@ -272,7 +295,8 @@ for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS SparseMatrix &matrix, const Function &rhs, Vector &rhs_vector, - const Function * const coefficient); + const Function * const coefficient, + const ConstraintMatrix &constraints); -- 2.39.5