From: Katharina Kormann Date: Sat, 5 May 2012 15:11:20 +0000 (+0000) Subject: Merge branch with matrix-free computations. Until r25443. X-Git-Tag: v8.0.0~2625 X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5d95405f291cdee262d468d5dbab5183c3d9e6b8;p=dealii.git Merge branch with matrix-free computations. Until r25443. git-svn-id: https://svn.dealii.org/trunk@25492 0785d39b-7218-0410-832d-ea1e28bc413d --- diff --git a/deal.II/aclocal.m4 b/deal.II/aclocal.m4 index 0f115ea80f..c3a5571bd9 100644 --- a/deal.II/aclocal.m4 +++ b/deal.II/aclocal.m4 @@ -1216,6 +1216,16 @@ AC_DEFUN(DEAL_II_CHECK_CXX1X_COMPONENTS, dnl [ AC_MSG_RESULT(no); all_cxx1x_classes_available=no ] ) + AC_MSG_CHECKING(for std::type_traits) + AC_TRY_COMPILE( + [#include ], + [ const bool m0 = std::is_trivial::value; + const bool m1 = std::is_standard_layout::value; + const bool m2 = std::is_pod::value; ], + [ AC_MSG_RESULT(yes) ], + [ AC_MSG_RESULT(no); all_cxx1x_classes_available=no ] + ) + CXXFLAGS="${OLD_CXXFLAGS}" dnl If the above classes and operations are all defined then we can @@ -1999,12 +2009,12 @@ AC_DEFUN(DEAL_II_CHECK_CPU_OPTIMIZATIONS, dnl AC_MSG_RESULT(x86 derivate ($withcpu)) case "$GXX_VERSION" in gcc*) - dnl Tune for this processor, but only in optimized mode - dnl (to prevent the effects of possible compiler bugs to affect - dnl both debug as well as optimized versions) + dnl Tune for this processor + CXXFLAGSG="$CXXFLAGSG -march=$withcpu" CXXFLAGSO="$CXXFLAGSO -march=$withcpu" dnl Also set the mode for f77 compiler + F77FLAGSG="$F77FLAGSG -march=$withcpu" F77FLAGSO="$F77FLAGSO -march=$withcpu" ;; esac @@ -2014,18 +2024,19 @@ AC_DEFUN(DEAL_II_CHECK_CPU_OPTIMIZATIONS, dnl AC_MSG_RESULT(native processor variant) case "$GXX_VERSION" in gcc*) - dnl Tune for this processor, but only in optimized mode - dnl (to prevent the effects of possible compiler bugs to affect - dnl both debug as well as optimized versions) + dnl Tune for this processor + CXXFLAGSG="$CXXFLAGSG -march=native" CXXFLAGSO="$CXXFLAGSO -march=native" dnl Also set the mode for f77 compiler + F77FLAGSG="$F77FLAGSG -march=native" F77FLAGSO="$F77FLAGSO -march=native" ;; intel_icc*) dnl Same, but for the icc compiler CXXFLAGSO="$CXXFLAGSO -xhost" + CXXFLAGSG="$CXXFLAGSG -xhost" ;; esac ;; @@ -2038,6 +2049,155 @@ AC_DEFUN(DEAL_II_CHECK_CPU_OPTIMIZATIONS, dnl +dnl ------------------------------------------------------------- +dnl Check whether the compiler allows for vectorization and that +dnl vectorization actually works. For this test, we use compiler +dnl intrinsics similar to what is used in the deal.II library and +dnl check whether the arithmetic operations are correctly performed +dnl on examples where all numbers are exactly represented as +dnl floating point numbers. +dnl +dnl Usage: DEAL_II_COMPILER_VECTORIZATION_LEVEL +dnl 0 means no vectorization, 1 support for SSE2, 2 support for AVX +dnl +dnl ------------------------------------------------------------- +AC_DEFUN(DEAL_II_DETECT_VECTORIZATION_LEVEL, dnl +[ + AC_LANG(C++) + CXXFLAGS="$CXXFLAGSG" + dnl SSE2 check in debug mode + AC_MSG_CHECKING(whether CPU supports SSE2) + AC_TRY_RUN( + [ +#include +#include + int main() + { + __m128d a, b; + const unsigned int vector_bytes = sizeof(__m128d); + const int n_vectors = vector_bytes/sizeof(double); + __m128d * data = + reinterpret_cast<__m128d*>(_mm_malloc (2*vector_bytes, vector_bytes)); + double * ptr = reinterpret_cast(&a); + ptr[0] = (volatile double)(1.0); + for (int i=1; i(&data[1]); + unsigned int return_value = 0; + if (ptr[0] != 7.3125) + return_value = 1; + for (int i=1; i +#include + int main() + { + __m256d a, b; + const unsigned int vector_bytes = sizeof(__m256d); + const int n_vectors = vector_bytes/sizeof(double); + __m256d * data = + reinterpret_cast<__m256d*>(_mm_malloc (2*vector_bytes, vector_bytes)); + double * ptr = reinterpret_cast(&a); + ptr[0] = (volatile double)(1.0); + for (int i=1; i(&data[1]); + unsigned int return_value = 0; + if (ptr[0] != 7.3125) + return_value = 1; + for (int i=1; i + ], + [ + __m128d a, b; + a = _mm_set_sd (1.0); + b = _mm_set1_pd (2.1); + __m128d c = a + b; + __m128d d = b - c; + __m128d e = c * a + d; + __m128d f = e/a; + (void)f; + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS, 1, + [Defined if the compiler can use arithmetic operations on + vectorized data types]) + ], + [ + AC_MSG_RESULT(no) + ]) +]) + + + dnl ------------------------------------------------------------- dnl In some cases, -threads (or whatever else command line option) dnl switches on some preprocessor flags. If this is not the case, @@ -4953,106 +5113,6 @@ AC_DEFUN(DEAL_II_CHECK_ADVANCE_WARNING, dnl -dnl ------------------------------------------------------------- -dnl Check whether the compiler allows to use arithmetic operations -dnl +-*/ on vectorized data types or whether we need to use -dnl _mm_add_pd for addition and so on. +-*/ is preferred because -dnl it allows the compiler to choose other optimizations like -dnl fused multiply add, whereas _mm_add_pd explicitly enforces the -dnl assembler command. -dnl -dnl Usage: DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS -dnl -dnl ------------------------------------------------------------- -AC_DEFUN(DEAL_II_CHECK_VECTOR_ARITHMETICS, dnl - -[ - AC_MSG_CHECKING(whether compiler supports vector arithmetics) - AC_LANG(C++) - CXXFLAGS="$CXXFLAGSG" - AC_TRY_COMPILE( - [ -#include - ], - [ - __m128d a, b; - a = _mm_set_sd (1.0); - b = _mm_set1_pd (2.1); - __m128d c = a + b; - __m128d d = b - c; - __m128d e = c * a + d; - __m128d f = e/a; - (void)f; - ], - [ - AC_MSG_RESULT(yes) - AC_DEFINE(DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS, 1, - [Defined if the compiler can use arithmetic operations on - vectorized data types]) - ], - [ - AC_MSG_RESULT(no) - ]) -]) - - - -dnl ------------------------------------------------------------- -dnl Check for existence of a strong inline function. This can be used -dnl to force a compiler to inline some functions also at low optimization -dnl levels. We use it in vectorized data types, where we want inlining -dnl also for debug code. If we cannot find a good inlining routine, we -dnl just use 'inline'. -dnl -dnl Usage: DEAL_II_ALWAYS_INLINE -dnl -dnl ------------------------------------------------------------- -AC_DEFUN(DEAL_II_CHECK_ALWAYS_INLINE, dnl - -[ - if test "$GXX" = "yes" ; then - dnl force inline for gcc compiler - TEMP_ALWAYS_INLINE='__inline __attribute__((__always_inline__))' - else - case "$GXX_VERSION" in - clang*) - dnl force inline for clang compiler - TEMP_ALWAYS_INLINE='__inline __attribute__((__always_inline__))' - ;; - - *) - dnl for all other compilers, try with __forceinline - TEMP_ALWAYS_INLINE=__forceinline - ;; - esac - fi - AC_MSG_CHECKING(for forced inlining) - AC_LANG(C++) - CXXFLAGS="$CXXFLAGSG" - AC_TRY_COMPILE( - [ - $TEMP_ALWAYS_INLINE - void f() {}; - ], - [ - f(); - ], - [ - AC_MSG_RESULT(yes) - AC_DEFINE_UNQUOTED(DEAL_II_ALWAYS_INLINE, $TEMP_ALWAYS_INLINE, - [Forces the compiler to always inline functions, also in - debug mode]) - ], - [ - AC_MSG_RESULT(no) - AC_DEFINE(DEAL_II_ALWAYS_INLINE, inline, - [Forces the compiler to always inline functions, also in - debug mode]) - ]) -]) - - - dnl ------------------------------------------------------------- dnl dnl Usage: DEAL_II_CHECK_MIN_VECTOR_CAPACITY @@ -7425,6 +7485,7 @@ AC_DEFUN(DEAL_II_WITH_LAPACK, dnl AC_DEFINE([HAVE_LIBLAPACK], [1], [Defined if deal.II was configured with LAPACK support]) AC_SUBST(DEAL_II_USE_LAPACK, "yes") + USE_CONTRIB_LAPACK='yes' ], [AC_MSG_ERROR([LAPACK library $lapack not found])] ) @@ -7531,6 +7592,7 @@ AC_DEFUN(DEAL_II_WITH_BLAS, dnl ],,$F77LIBS) AC_SUBST(DEAL_II_USE_BLAS, "yes") AC_SUBST(NEEDS_F77LIBS, "yes") + USE_CONTRIB_BLAS='yes' else DEAL_II_CHECK_BLAS_FRAMEWORK if test "x$framework_works" != "xyes"; then @@ -7544,6 +7606,7 @@ AC_DEFUN(DEAL_II_WITH_BLAS, dnl AC_SUBST(DEAL_II_USE_BLAS, "yes") AC_SUBST(NEEDS_F77LIBS, "yes") + USE_CONTRIB_BLAS='yes' fi fi fi diff --git a/deal.II/common/Make.global_options.in b/deal.II/common/Make.global_options.in index e5bc799f72..cd3121065b 100644 --- a/deal.II/common/Make.global_options.in +++ b/deal.II/common/Make.global_options.in @@ -76,6 +76,9 @@ DEAL_II_TRILINOS_VERSION_MINOR = @DEAL_II_TRILINOS_VERSION_MINOR@ DEAL_II_TRILINOS_VERSION_SUBMINOR = @DEAL_II_TRILINOS_VERSION_SUBMINOR@ DEAL_II_TRILINOS_LIBPREFIX = @DEAL_II_TRILINOS_LIBPREFIX@ +USE_CONTRIB_BLAS = @USE_CONTRIB_BLAS@ +USE_CONTRIB_LAPACK = @USE_CONTRIB_LAPACK@ + USE_CONTRIB_MUMPS = @USE_CONTRIB_MUMPS@ DEAL_II_MUMPS_DIR = @DEAL_II_MUMPS_DIR@ DEAL_II_SCALAPACK_DIR = @DEAL_II_SCALAPACK_DIR@ diff --git a/deal.II/configure b/deal.II/configure index 407e5fbeb9..0d70c483b9 100755 --- a/deal.II/configure +++ b/deal.II/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 25395 . +# From configure.in Revision: 25397 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.68 for deal.II 7.2.pre. # @@ -625,6 +625,8 @@ USE_CONTRIB_METIS TECPLOT_INCLUDE_DIR USE_CONTRIB_HSL HSL_INCLUDE_DIR +USE_CONTRIB_LAPACK +USE_CONTRIB_BLAS NEEDS_F77LIBS DEAL_II_USE_BLAS DEAL_II_P4EST_DIR @@ -4387,6 +4389,31 @@ else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; }; all_cxx1x_classes_available=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::type_traits" >&5 +$as_echo_n "checking for std::type_traits... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + const bool m0 = std::is_trivial::value; + const bool m1 = std::is_standard_layout::value; + const bool m2 = std::is_pod::value; + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; all_cxx1x_classes_available=no + fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext @@ -5126,6 +5153,31 @@ else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; }; all_cxx1x_classes_available=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for std::type_traits" >&5 +$as_echo_n "checking for std::type_traits... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + const bool m0 = std::is_trivial::value; + const bool m1 = std::is_standard_layout::value; + const bool m2 = std::is_pod::value; + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; all_cxx1x_classes_available=no + fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext @@ -8389,116 +8441,6 @@ rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports vector arithmetics" >&5 -$as_echo_n "checking whether compiler supports vector arithmetics... " >&6; } - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - CXXFLAGS="$CXXFLAGSG" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include - -int -main () -{ - - __m128d a, b; - a = _mm_set_sd (1.0); - b = _mm_set1_pd (2.1); - __m128d c = a + b; - __m128d d = b - c; - __m128d e = c * a + d; - __m128d f = e/a; - (void)f; - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - -$as_echo "#define DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS 1" >>confdefs.h - - -else - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - - - - if test "$GXX" = "yes" ; then - TEMP_ALWAYS_INLINE='__inline __attribute__((__always_inline__))' - else - case "$GXX_VERSION" in - clang*) - TEMP_ALWAYS_INLINE='__inline __attribute__((__always_inline__))' - ;; - - *) - TEMP_ALWAYS_INLINE=__forceinline - ;; - esac - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for forced inlining" >&5 -$as_echo_n "checking for forced inlining... " >&6; } - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - CXXFLAGS="$CXXFLAGSG" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - $TEMP_ALWAYS_INLINE - void f() {}; - -int -main () -{ - - f(); - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - -cat >>confdefs.h <<_ACEOF -#define DEAL_II_ALWAYS_INLINE $TEMP_ALWAYS_INLINE -_ACEOF - - -else - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -$as_echo "#define DEAL_II_ALWAYS_INLINE inline" >>confdefs.h - - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - - ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -11964,6 +11906,7 @@ fi NEEDS_F77LIBS="yes" + USE_CONTRIB_BLAS='yes' else if (echo $target | grep apple-darwin > /dev/null) ; then @@ -12092,6 +12035,7 @@ fi NEEDS_F77LIBS="yes" + USE_CONTRIB_BLAS='yes' fi fi fi @@ -12100,6 +12044,8 @@ fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for HSL subroutines" >&5 $as_echo_n "checking for HSL subroutines... " >&6; } hsl_subroutines="" @@ -13456,6 +13402,7 @@ $as_echo "#define HAVE_LIBLAPACK 1" >>confdefs.h DEAL_II_USE_LAPACK="yes" + USE_CONTRIB_LAPACK='yes' else as_fn_error $? "LAPACK library $lapack not found" "$LINENO" 5 @@ -13614,9 +13561,11 @@ $as_echo "PowerPC64" >&6; } $as_echo "x86 derivate ($withcpu)" >&6; } case "$GXX_VERSION" in gcc*) - CXXFLAGSO="$CXXFLAGSO -march=$withcpu" + CXXFLAGSG="$CXXFLAGSG -march=$withcpu" + CXXFLAGSO="$CXXFLAGSO -march=$withcpu" - F77FLAGSO="$F77FLAGSO -march=$withcpu" + F77FLAGSG="$F77FLAGSG -march=$withcpu" + F77FLAGSO="$F77FLAGSO -march=$withcpu" ;; esac ;; @@ -13626,13 +13575,16 @@ $as_echo "x86 derivate ($withcpu)" >&6; } $as_echo "native processor variant" >&6; } case "$GXX_VERSION" in gcc*) - CXXFLAGSO="$CXXFLAGSO -march=native" + CXXFLAGSG="$CXXFLAGSG -march=native" + CXXFLAGSO="$CXXFLAGSO -march=native" - F77FLAGSO="$F77FLAGSO -march=native" + F77FLAGSG="$F77FLAGSG -march=native" + F77FLAGSO="$F77FLAGSO -march=native" ;; intel_icc*) CXXFLAGSO="$CXXFLAGSO -xhost" + CXXFLAGSG="$CXXFLAGSG -xhost" ;; esac ;; @@ -13709,6 +13661,181 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + CXXFLAGS="$CXXFLAGSG" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether CPU supports SSE2" >&5 +$as_echo_n "checking whether CPU supports SSE2... " >&6; } + if test "$cross_compiling" = yes; then : + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run test program while cross compiling +See \`config.log' for more details" "$LINENO" 5; } +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include + int main() + { + __m128d a, b; + const unsigned int vector_bytes = sizeof(__m128d); + const int n_vectors = vector_bytes/sizeof(double); + __m128d * data = + reinterpret_cast<__m128d*>(_mm_malloc (2*vector_bytes, vector_bytes)); + double * ptr = reinterpret_cast(&a); + ptr[0] = (volatile double)(1.0); + for (int i=1; i(&data[1]); + unsigned int return_value = 0; + if (ptr[0] != 7.3125) + return_value = 1; + for (int i=1; i&5 +$as_echo "yes" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether CPU supports AVX" >&5 +$as_echo_n "checking whether CPU supports AVX... " >&6; } + if test "$cross_compiling" = yes; then : + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run test program while cross compiling +See \`config.log' for more details" "$LINENO" 5; } +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include + int main() + { + __m256d a, b; + const unsigned int vector_bytes = sizeof(__m256d); + const int n_vectors = vector_bytes/sizeof(double); + __m256d * data = + reinterpret_cast<__m256d*>(_mm_malloc (2*vector_bytes, vector_bytes)); + double * ptr = reinterpret_cast(&a); + ptr[0] = (volatile double)(1.0); + for (int i=1; i(&data[1]); + unsigned int return_value = 0; + if (ptr[0] != 7.3125) + return_value = 1; + for (int i=1; i&5 +$as_echo "yes" >&6; } + +$as_echo "#define DEAL_II_COMPILER_VECTORIZATION_LEVEL 2" >>confdefs.h + + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +$as_echo "#define DEAL_II_COMPILER_VECTORIZATION_LEVEL 1" >>confdefs.h + + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + +else + + +$as_echo "#define DEAL_II_COMPILER_VECTORIZATION_LEVEL 0" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports vector arithmetics" >&5 +$as_echo_n "checking whether compiler supports vector arithmetics... " >&6; } + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + CXXFLAGS="$CXXFLAGSG" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +int +main () +{ + + __m128d a, b; + a = _mm_set_sd (1.0); + b = _mm_set1_pd (2.1); + __m128d c = a + b; + __m128d d = b - c; + __m128d e = c * a + d; + __m128d f = e/a; + (void)f; + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS 1" >>confdefs.h + + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 $as_echo "" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: ---------------- configuring other programs -----------------" >&5 diff --git a/deal.II/configure.in b/deal.II/configure.in index 0402549871..f2e3b1c4dd 100644 --- a/deal.II/configure.in +++ b/deal.II/configure.in @@ -245,8 +245,6 @@ DEAL_II_HAVE_BUILTIN_EXPECT DEAL_II_HAVE_VERBOSE_TERMINATE DEAL_II_HAVE_GLIBC_STACKTRACE DEAL_II_HAVE_DEMANGLER -DEAL_II_CHECK_VECTOR_ARITHMETICS -DEAL_II_CHECK_ALWAYS_INLINE DEAL_II_CHECK_MIN_VECTOR_CAPACITY DEAL_II_CHECK_ABORT DEAL_II_CHECK_GETRUSAGE @@ -590,6 +588,8 @@ AC_ARG_WITH(blas, is given, use -lblas. Default is to use -lblas only if required by other libraries.], DEAL_II_WITH_BLAS($withval)) +AC_SUBST(USE_CONTRIB_BLAS) +AC_SUBST(USE_CONTRIB_LAPACK) DEAL_II_CONFIGURE_HSL AC_SUBST(USE_CONTRIB_HSL) @@ -696,10 +696,15 @@ AC_MSG_RESULT(---------------- checking compiler flags --------------------) dnl Possibly add some flags if optimizations are requested DEAL_II_CHECK_CPU_OPTIMIZATIONS -dnl Last check: test whether CXXFLAGS and F77FLAGS are ok +dnl Test whether CXXFLAGS and F77FLAGS are ok DEAL_II_CHECK_CXXFLAGS_CONSISTENCY DEAL_II_CHECK_F77FLAGS_CONSISTENCY +dnl Last check: check whether the compiler and host system allows for +dnl vectorization +DEAL_II_DETECT_VECTORIZATION_LEVEL +DEAL_II_CHECK_VECTOR_ARITHMETICS + dnl ------------------------------------------------------------- dnl Third party programs dnl ------------------------------------------------------------- diff --git a/deal.II/doc/doxygen/tutorial/navbar.html b/deal.II/doc/doxygen/tutorial/navbar.html index daf4ff0109..7608b3ca95 100644 --- a/deal.II/doc/doxygen/tutorial/navbar.html +++ b/deal.II/doc/doxygen/tutorial/navbar.html @@ -79,17 +79,19 @@ 35  36  - 38
+ 37
+ 38  39  - 40  - 41
+ 40
+ 41  43  - 44  - 45
+ 44
+ 45  46  + 48


diff --git a/deal.II/doc/doxygen/tutorial/steps.cmapx b/deal.II/doc/doxygen/tutorial/steps.cmapx index f000285fac..66bc3f06b4 100644 --- a/deal.II/doc/doxygen/tutorial/steps.cmapx +++ b/deal.II/doc/doxygen/tutorial/steps.cmapx @@ -1,45 +1,47 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/deal.II/doc/doxygen/tutorial/steps.pl b/deal.II/doc/doxygen/tutorial/steps.pl index b18b68ebb0..191cdb62e6 100644 --- a/deal.II/doc/doxygen/tutorial/steps.pl +++ b/deal.II/doc/doxygen/tutorial/steps.pl @@ -11,8 +11,8 @@ use strict; my @steps = (1,2,3,4,5,6,7,8,9, 10,11,12,13,14,15,16,17,18,19, 20,21,22,23,24,25, 27,28,29, - 30,31,32,33,34,35,36, 38,39, - 40,41, 43,44,45,46); + 30,31,32,33,34,35,36,37,38,39, + 40,41, 43,44,45,46, 48); # List of additional node attributes to highlight purpose and state of the example diff --git a/deal.II/doc/doxygen/tutorial/steps.png b/deal.II/doc/doxygen/tutorial/steps.png index 81836339f0..583cbaa611 100644 Binary files a/deal.II/doc/doxygen/tutorial/steps.png and b/deal.II/doc/doxygen/tutorial/steps.png differ diff --git a/deal.II/doc/doxygen/tutorial/toc-list.html b/deal.II/doc/doxygen/tutorial/toc-list.html index a3a8fc2b81..f00cd561a7 100644 --- a/deal.II/doc/doxygen/tutorial/toc-list.html +++ b/deal.II/doc/doxygen/tutorial/toc-list.html @@ -244,6 +244,12 @@ problem. The Schrödinger wave equation. + + Step-37 + Solving a Poisson problem with a multilevel preconditioner without + explicitly storing the matrix (a matrix-free method). + + Step-38 Solving the Laplace-Beltrami equation on curved manifolds embedded @@ -289,6 +295,13 @@ Step-46 Coupling different kinds of equations in different parts of the domain. + + + Step-48 + Explicit time stepping for the Sine–Gordon equation based on + a diagonal mass matrix. Efficient implementation of (nonlinear) finite + element operators. + diff --git a/deal.II/doc/doxygen/tutorial/toc-topics.html b/deal.II/doc/doxygen/tutorial/toc-topics.html index f54f2a6cee..d0b0662b02 100644 --- a/deal.II/doc/doxygen/tutorial/toc-topics.html +++ b/deal.II/doc/doxygen/tutorial/toc-topics.html @@ -75,7 +75,8 @@ Step-9, Step-28, Step-32, - Step-44 + Step-44, + Step-48 Multithreading @@ -265,6 +266,16 @@ + + + + Step-37, + Step-48 + + Matrix-free methods + + +
@@ -294,6 +305,7 @@ Step-16, Step-31, Step-32, + Step-37, Step-39, Step-41, Step-43 @@ -433,7 +445,8 @@ Step-23, Step-24, - Step-25 + Step-25, + Step-48 The wave equation, in linear and nonlinear variants @@ -552,7 +565,9 @@ Step-23, Step-24, - Step-25 + Step-25, + Step-48 + The wave equation, in linear and nonlinear variants diff --git a/deal.II/examples/step-25/doc/results.dox b/deal.II/examples/step-25/doc/results.dox index 4abd99cca9..e7e332e453 100644 --- a/deal.II/examples/step-25/doc/results.dox +++ b/deal.II/examples/step-25/doc/results.dox @@ -128,10 +128,16 @@ is defined as contributions to the energy are positive, and so that decaying solutions have finite energy on unbounded domains.) -Beyond this, clearly, adaptivity (i.e. time-adaptive grids) would be of -interest to problems like these. Their complexity leads us to leave this out -of this program again, though the general comments in the introduction of @ref -step_23 "step-23" remain true. - - - +Beyond this, there are two obvious areas: + +- Clearly, adaptivity (i.e. time-adaptive grids) would be of interest + to problems like these. Their complexity leads us to leave this out + of this program again, though the general comments in the + introduction of @ref step_23 "step-23" remain true. + +- Faster schemes to solve this problem. While computers today are + plenty fast enough to solve 2d and, frequently, even 3d stationary + problems within not too much time, time dependent problems present + an entirely different class of problems. We address this topic in + step-48 where we show how to solve this problem in parallel and + without assembling or inverting any matrix at all. diff --git a/deal.II/examples/step-37/Makefile b/deal.II/examples/step-37/Makefile index 0b41eb0b8d..15898a61c7 100644 --- a/deal.II/examples/step-37/Makefile +++ b/deal.II/examples/step-37/Makefile @@ -45,6 +45,21 @@ clean-up-files = *gmv *gnuplot *gpl *eps *pov *vtk *ucd *.d2 # settings include $D/common/Make.global_options +################################################################ +# This example program will only work if Trilinos is installed. If this +# # is not the case, then simply redefine the main targets to do nothing +ifneq ($(USE_CONTRIB_LAPACK),yes) +default run clean: + @echo + @echo "===========================================================" + @echo "= This program can only be run with LAPACK enabled. =" + @echo "===========================================================" + @echo +else +# +################################################################# + + # Since the whole project consists of only one file, we need not # consider difficult dependencies. We only have to declare the @@ -141,4 +156,5 @@ Makefile.dep: $(target).cc Makefile \ # them: include Makefile.dep +endif # USE_CONTRIB_LAPACK diff --git a/deal.II/examples/step-37/doc/intro.dox b/deal.II/examples/step-37/doc/intro.dox index 9d488ece7f..54c3686564 100644 --- a/deal.II/examples/step-37/doc/intro.dox +++ b/deal.II/examples/step-37/doc/intro.dox @@ -4,62 +4,94 @@ This program was contributed by Katharina Kormann and Martin Kronbichler. -This program is currently under construction. - -The algorithm for the matrix-vector product is built upon the report "MPI -parallelization of a cell-based matrix-vector product for finite elements. An -application from quantum dynamics" by Katharina Kormann, Uppsala -University, June 2009. +The algorithm for the matrix-vector product is built upon the preprint "A generic interface for parallel cell-based finite element operator application" by Martin Kronbichler and Katharina Kormann, Uppsala +University, October 2011, and the paper "Parallel finite element operator application: Graph partitioning and coloring" by Katharina Kormann and Martin Kronbichler in: Proceedings of the 7th IEEE International Conference on e-Science, 2011.

Introduction

-This example shows how to implement a matrix-free method, that is, a method -that does not explicitly store the matrix elements, for a -second-order Poisson equation with variable coefficients on a fairly -unstructured mesh representing a circle. +This example shows how to implement a matrix-free method, that is, a +method that does not explicitly store the matrix elements, for a +second-order Poisson equation with variable coefficients on a +hypercube. The eliptic equation will be solved with a multigrid +method. + +The major motivation for matrix-free methods is the fact that today +access to main memory (i.e., for objects that don't fit in the cache) +has become the bottleneck in scientific computing: To perform a +matrix-vector product, modern CPUs spend far more time waiting for +data to arrive from memory than on actually doing the floating point +multiplications and additions. Thus, if we could substitute looking up +matrix elements in memory by re-computing them — or rather, the +operator represented by these entries —, we may win in terms of +overall run-time (even if this requires a significant number of +additional floating point operations). That said, to realize this with +a trivial implementation is not enough and one needs to really look at +what it takes to make this happen. This tutorial program (and the +papers referenced above) show how one can implement such a scheme and +demonstrates the speedup that can be obtained. + + +

The test case

+ +In this example, we consider the Poisson problem @f{eqnarray*} - +\nabla \cdot a(\mathbf x) \nabla u &=& 1, \\ u &=& 0 \quad \text{on}\ +\partial \Omega @f} where $a(\mathbf x)$ is a variable coefficient. +Below, we explain how to implement a matrix-vector product for this +problem without explicitly forming the matrix. The construction can, +of course, be done in a similar way for other equations as well. + +We choose as domain $\Omega=[0,1]^3$ and $a(\mathbf x)=\frac{1}{0.05 + +2\|\mathbf x\|^2}$. Since the coefficient is symmetric around the +origin but the domain is not, we will end up with a non-symmetric +solution. +

Matrix-vector product implementation

In order to find out how we can write a code that performs a matrix-vector product, but does not need to store the matrix elements, let us start at -looking how some finite-element related matrix A is assembled: +looking how a finite element matrix A is assembled: @f{eqnarray*} -A = \sum_{\mathrm{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T A_\mathrm{cell} -P_\mathrm{cell,{loc-glob}}. +A = \sum_{\mathrm{cell}=1}^{\mathrm{n\_cells}} +P_{\mathrm{cell,{loc-glob}}}^T A_{\mathrm{cell}} P_{\mathrm{cell,{loc-glob}}}. @f} In this formula, the matrix Pcell,loc-glob is a rectangular matrix that defines the index mapping from local degrees of freedom in the current cell to the global degrees of freedom. The information from which this operator can be built is usually encoded in the local_dof_indices -variable we have always used in the assembly of matrices. +variable we have always used in the assembly of matrices. Moreover, +Acell denotes the cell-operation associated with A. If we are to perform a matrix-vector product, we can hence use that @f{eqnarray*} -y &=& A\cdot x = \left(\sum_{\text{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T -A_\mathrm{cell} P_\mathrm{cell,{loc-glob}}\right) \cdot x +y &=& A\cdot u = \left(\sum_{\text{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T +A_\mathrm{cell} P_\mathrm{cell,{loc-glob}}\right) \cdot u \\ &=& \sum_{\mathrm{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T -A_\mathrm{cell} x_\mathrm{cell} +A_\mathrm{cell} u_\mathrm{cell} \\ &=& \sum_{\mathrm{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T -y_\mathrm{cell}, +v_\mathrm{cell}, @f} -where xcell are the values of x at the degrees of freedom -of the respective cell, and xcell correspondingly for the result. +where ucell are the values of u at the degrees of freedom +of the respective cell, and +vcell=Acellucell +correspondingly for the result. A naive attempt to implement the local action of the Laplacian would hence be to use the following code: @code -MatrixFree::vmult (Vector &dst, - const Vector &src) const +MatrixFree::vmult (Vector &dst, + const Vector &src) const { dst = 0; QGauss quadrature_formula(fe.degree+1); FEValues fe_values (fe, quadrature_formula, - update_gradients | update_JxW_values); + update_gradients | update_JxW_values| + update_quadrature_points); const unsigned int dofs_per_cell = fe.dofs_per_cell; const unsigned int n_q_points = quadrature_formula.size(); @@ -67,6 +99,8 @@ MatrixFree::vmult (Vector &dst, FullMatrix cell_matrix (dofs_per_cell, dofs_per_cell); Vector cell_src (dofs_per_cell), cell_dst (dofs_per_cell); + const Coefficient coefficient; + std::vector coefficient_values(n_q_points); std::vector local_dof_indices (dofs_per_cell); @@ -77,13 +111,16 @@ MatrixFree::vmult (Vector &dst, { cell_matrix = 0; fe_values.reinit (cell); + coefficient.value_list(fe_values.get_quadrature_points(), + coefficient_values); - for (unsigned int q_point=0; q_pointget_dof_indices (local_dof_indices); @@ -101,8 +138,9 @@ MatrixFree::vmult (Vector &dst, Here we neglected boundary conditions as well as any hanging nodes we may have, though neither would be very difficult to include using the ConstraintMatrix class. Note how we first generate the local matrix in the -usual way. To form the actual product as expressed in the above formula, we -read in the values of src of the cell-related degrees of freedom +usual way as a sum over all quadrature points for each local matrix entry. +To form the actual product as expressed in the above formula, we +extract the values of src of the cell-related degrees of freedom (the action of Pcell,loc-glob), multiply by the local matrix (the action of Acell), and finally add the result to the destination vector dst (the action of @@ -110,8 +148,8 @@ destination vector dst (the action of is not more difficult than that, in principle. While this code is completely correct, it is very slow. For every cell, we -generate a local matrix, which takes three nested loops with as many -elements as there are degrees of freedom on the actual cell to compute. The +generate a local matrix, which takes three nested loops with loop length equal +to the number of local degrees of freedom to compute. The multiplication itself is then done by two nested loops, which means that it is much cheaper. @@ -125,110 +163,85 @@ element of Bcell is given by fe_values.shape_grad(i,q)[d]. The matrix consists of dim*n_q_points rows and @p dofs_per_cell columns). The matrix Dcell is diagonal and contains the values -fe_values.JxW(q) (or, rather, @p dim copies of it). - -Every numerical analyst learns in one of her first classes that for -forming a product of the form -@f{eqnarray*} -A_\mathrm{cell}\cdot x_\mathrm{cell} = B_\mathrm{cell} D_\mathrm{cell} - B_\mathrm{cell}^T \cdot x_\mathrm{cell}, -@f} -one should never form the matrix-matrix products, but rather multiply with the -vector from right to left so that only three successive matrix-vector products -are formed. To put this into code, we can write: -@code -... - for (; cell!=endc; ++cell) - { - fe_values.reinit (cell); - - cell->get_dof_indices (local_dof_indices); - - for (unsigned int i=0; id is a not really a loop, rather two or three -operations). What happens is as follows: We first transform the vector of -values on the local dofs to a vector of gradients on the quadrature -points. In the second loop, we multiply these gradients by the integration -weight. The third loop applies the second gradient (in transposed form), so -that we get back to a vector of (Laplacian) values on the cell dofs. - -This improves the situation a lot and reduced the complexity of the product -from something like $\mathcal {O}(\mathrm{dofs\_per\_cell}^3)$ to $\mathcal -{O}(\mathrm{dofs\_per\_cell}^2)$. In fact, all the remainder is just to make -a slightly more clever use of data in order to gain some extra speed. It does -not change the code structure, though. - -The bottleneck in the above code is the operations done by the call -fe_values.reinit(cell), which take about as much time as the +fe_values.JxW(q) * coefficient_values[q] (or, rather, @p +dim copies of each of these values). This kind of representation of +finite element matrices can often be found in the engineering literature. + +When the cell-based matrix is applied to a vector @f{eqnarray*} +A_\mathrm{cell}\cdot u_\mathrm{cell} = B_\mathrm{cell}^T +D_\mathrm{cell} B_\mathrm{cell} \cdot u_\mathrm{cell}, @f} one would +then never form the matrix-matrix products, but rather multiply with +the vector from right to left so that only three successive +matrix-vector products are formed. This removed the three nested +loops in the calculation of the local matrix. What happens is as +follows: We first transform the vector of values on the local dofs to +a vector of gradients on the quadrature points. In the second loop, we +multiply these gradients by the integration weight. The third loop +applies the second gradient (in transposed form), so that we get back +to a vector of (Laplacian) values on the cell dofs. This reduces the +complexity of the work on one cell from something like $\mathcal +{O}(\mathrm{dofs\_per\_cell}^3)$ to $\mathcal +{O}(\mathrm{dofs\_per\_cell}^2)$. + +The bottleneck in the above code is the operations done by the call to +FEValues::reinit for every cell, which take about as much time as the other steps together (at least if the mesh is unstructured; deal.II can recognize that the gradients are often unchanged on structured meshes). That is certainly not ideal and we would like to do better than this. What the reinit function does is to calculate the gradient in real space by transforming the gradient on the reference cell using the Jacobian of the transformation from real to reference cell. This is done for each basis -function on the cell, for each quadrature point. The Jacobian does not depend on -the basis function, but it is different on different quadrature points in -general. The trick is now to factor out the Jacobian transformation and first -apply the operation that leads us to temp_vector only with the -gradient on the reference cell. That transforms the vector of values on the -local dofs to a vector of gradients on the quadrature points. There, we first -apply the Jacobian that we factored out from the gradient, then we apply the -weights of the quadrature, and we apply with the transposed Jacobian for -preparing the third loop which again uses the gradients on the unit cell. +function on the cell, for each quadrature point. The Jacobian does not depend +on the basis function, but it is different on different quadrature points in +general. If you only build the matrix once as we've done in all +previous tutorial programs, there is nothing one can do about the need +to call FEValues::reinit on every cell since this transformation has +to be done when we want to compute the local matrix elements. + +However, in a matrix-free implementation, we are not interested in +applying the matrix only once. Rather, in iterative solvers, we need +to expect that we have to apply the matrix many times, and so we can +think about whether we may be able to cache something between +different applications. On the other hand, we realize that we must not +cache too much data since otherwise we get back to the situation where +memory access becomes the dominating factor. + +The trick is now to factor out the Jacobian transformation and first +apply the gradient on the reference cell only. That transforms the vector of +values on the local dofs to a vector of gradients on the quadrature +points. There, we first apply the Jacobian that we factored out from the +gradient, then we apply the weights of the quadrature, and we apply the +transposed Jacobian for preparing the third loop which again uses the +gradients on the unit cell. Let us again write this in terms of matrices. Let the matrix Bcell denote the cell-related gradient matrix, with each row -containing the values of the quadrature points. It is constructed by a +containing the values on the quadrature points. It is constructed by a matrix-matrix product as @f{eqnarray*} B_\mathrm{cell} = J_\mathrm{cell} B_\mathrm{ref\_cell}, @f} where Bref_cell denotes the gradient on the reference cell -and Jcell denotes the Jacobian -transformation. Jcell is block-diagonal, and the blocks size -is equal to the dimension of the problem. Each diagonal block is the Jacobian -transformation that goes from the reference cell to the real cell. +and Jcell denotes the Jacobian transformation from unit to +real cell (in the language of transformations, the operation represented by +Jcell represents a covariant +transformation). Jcell is block-diagonal, and the blocks +size is equal to the dimension of the problem. Each diagonal block is the +Jacobian transformation that goes from the reference cell to the real cell. Putting things together, we find that @f{eqnarray*} -A_\mathrm{cell} = B_\mathrm{cell}^T D B_\mathrm{cell} - = B_\mathrm{ref\_cell}^T J_\mathrm{cell}^T - D_\mathrm{cell} +A_\mathrm{cell} = B_\mathrm{cell}^T D B_\mathrm{cell} + = B_\mathrm{ref\_cell}^T J_\mathrm{cell}^T + D_\mathrm{cell} J_\mathrm{cell} B_\mathrm{ref\_cell}, @f} so we calculate the product (starting the local product from the right) @f{eqnarray*} -y_\mathrm{cell} = B_\mathrm{ref\_cell}^T J_\mathrm{cell}^T D J_\mathrm{cell} -B_\mathrm{ref\_cell} x_\mathrm{cell}, \quad -y = \sum_{\mathrm{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T -y_\mathrm{cell}. +v_\mathrm{cell} = B_\mathrm{ref\_cell}^T J_\mathrm{cell}^T D J_\mathrm{cell} +B_\mathrm{ref\_cell} u_\mathrm{cell}, \quad +v = \sum_{\mathrm{cell}=1}^{\mathrm{n\_cells}} P_\mathrm{cell,{loc-glob}}^T +v_\mathrm{cell}. @f} @code ... @@ -239,11 +252,14 @@ y_\mathrm{cell}. fe_values_reference.reinit (reference_cell.begin()); FEValues fe_values (fe, quadrature_formula, - update_inverse_jacobians | update_JxW_values); + update_inverse_jacobians | update_JxW_values | + update_quadrature_points); for (; cell!=endc; ++cell) { fe_values.reinit (cell); + coefficient.value_list(fe_values.get_quadrature_points(), + coefficient_values); cell->get_dof_indices (local_dof_indices); @@ -251,51 +267,50 @@ y_\mathrm{cell}. cell_src(i) = src(local_dof_indices(i)); temp_vector = 0; - for (unsigned int q_point=0; q_point temp; for (unsigned int d=0; ddim as many -columns than before (here we assume that the number of quadrature points is -the same as the number of degrees of freedom per cell, which is usual for -scalar problems). Then, we also need to keep in mind that we touch some -degrees of freedom several times because they belong to several cells. This -also increases computational costs. A realistic value compared to a sparse -matrix is that we now have to perform about 10 times as many operations (a bit -less in 2D, a bit more in 3D). - -The above is, in essence, what happens in the code below and if you have -difficulties in understanding the implementation, you should try to first -understand what happens in the code above. In the actual implementation -there are a few more points done to be even more efficient, namely: -
    -
  • We pre-compute the inverse of the Jacobian of the transformation and - store it in an extra array. This allows us to fuse the three - operations JcellT Dcell - Jcell (apply Jacobian, multiply by - weights, apply transposed - Jacobian) into one second-rank tensor that is also symmetric (so we - only need to store half the tensor). -
  • We work on several cells at once when we apply the gradients of the - unit cell (it is always the same matrix with the reference cell - data). This allows us to replace the matrix-vector product by a - matrix-matrix product (several vectors of cell-data form a matrix), - which enables a faster implementation. Obviously, we need some adapted - data structures for that, but it isn't too hard to provide that. What - is nice is that dense matrix-matrix products are close to today's - processors' peak performance if the matrices are neither too small nor - too large — and these operations are the most expensive part in - the implementation shown here. -
+derivative data is then applied by the inverse, transposed Jacobians (deal.II +calls the Jacobian matrix from unit to real cell inverse_jacobian, because the +transformation direction in deal.II is from real to unit cell). + +Finally, we are using tensor product basis functions and now that we have +separated out the gradient on the reference cell Bref_cell, +we can exploit the tensor-product structure to further reduce the +complexity. We illustrate this in two space dimensions, but the same technique +can be used in higher dimensions. On the reference cell, the basis functions +are of the tensor product form $\phi(x,y,z) = \varphi_i(x) \varphi_j(y)$. The +part of the matrix Bref_cell that computes the first +component has the form $B_\mathrm{sub\_cell}^x = B_\mathrm{grad,x} \otimes +B_\mathrm{val,y}$, where Bgrad,x and +Bval,y contain the evaluation of all the 1D basis functions +on all the 1D quadrature points. Forming a matrix U with U(j,i) +containing the coefficient belonging to basis function $\varphi_i(x) +\varphi_j(y)$, we get $(B_\mathrm{grad,x} \otimes +B_\mathrm{val,y})u_\mathrm{cell} = B_\mathrm{val,y} U B_\mathrm{grad,x}$. This +reduces the complexity for computing this product from $p^4$ to $2 p^3$, where +p-1 is the degree of the finite element (i.e., equivalently, +p is the number of shape functions in each coordinate +direction), or $p^{2d}$ to $d p^{d+1}$ in general. + The implementation of the matrix-free matrix-vector product shown in this tutorial is slower than a matrix-vector product using a sparse matrix for -linear and quadratic elements, but on par with third order elements and faster -for even higher order elements. An additional gain with this implementation is -that we do not have to build the sparse matrix itself, which can also be quite -expensive depending on the underlying differential equation. +linear elements, but faster for all higher order elements thanks to the +reduced complexity due to the tensor product structure and due to less memory +transfer during computations. The impact of reduced memory transfer is +particularly beneficial when working on a multi-core processor where several +processing units share access to memory. In that case, an algorithm which is +computation bound will show almost perfect parallel speedup, whereas an +algorithm that is bound by memory transfer might not achieve similar speedup +(even when the work is perfectly parallel and one could expect perfect scaling +like in sparse matrix-vector products). An additional gain with this +implementation is that we do not have to build the sparse matrix itself, which +can also be quite expensive depending on the underlying differential +equation. Moreover, the above framework is simple to generalize to nonlinear +operations, as we demonstrate in step-48.

Combination with multigrid

Above, we have gone to significant lengths to implement a matrix-vector product that does not actually store the matrix elements. In many user codes, -however, one wants more than just performing some uncertain number of +however, one wants more than just performing some number of matrix-vector products — one wants to do as little of these operations as possible when solving linear equation systems. In theory, we could use the CG method without preconditioning; however, that would not be very efficient. Rather, one uses preconditioners for improving speed. On the other hand, most of the more frequently used preconditioners such as SSOR, ILU or -algebraic multigrid (AMG) can now no longer be used here because their +algebraic multigrid (AMG) cannot be used here because their implementation requires knowledge of the elements of the system matrix. -One solution is to use multigrid methods as shown in -step-16. They are known to be very fast, and they are suitable for our -purpose since they can be designed based purely on matrix-vector products. All -one needs to do is to find a smoother that works with matrix-vector products -only (our choice requires knowledge of the diagonal entries of the matrix, -though). One such candidate would be a damped Jacobi iteration, but that is -often not sufficiently good in damping high-frequency errors. -A Chebyshev preconditioner, eventually, is what we use here. It can be -seen as an extension of the Jacobi method by using Chebyshev polynomials. With -degree zero, the Jacobi method with optimal damping parameter is retrieved, -whereas higher order corrections improve the smoothing properties if some -parameters are suitably chosen. The effectiveness of Chebyshev smoothing in -multigrid has been demonstrated, e.g., in the article M. Adams, M. Brezina, -J. Hu, R. Tuminaro. Parallel multigrid smoothers: polynomial versus -Gauss–Seidel, J. Comput. Phys. 188:593–610, 2003. This -publication also identifies one more advantage of Chebyshev smoothers that we -exploit here, namely that they are easy to parallelize, whereas -SOR/Gauss–Seidel smoothing relies on substitutions, which can often only -be parallelized by working on diagonal sub-blocks of the matrix, which -decreases efficiency. - -The implementation into the multigrid framework is then straightforward. This +One solution is to use multigrid methods as shown in step-16. They are known +to be very fast, and they are suitable for our purpose since they can be +designed based purely on matrix-vector products. All one needs to do is to +find a smoother that works with matrix-vector products only (our choice +requires knowledge of the diagonal entries of the matrix, though). One such +candidate would be a damped Jacobi iteration, but that is often not +sufficiently good in damping high-frequency errors. A Chebyshev +preconditioner, eventually, is what we use here. It can be seen as an +extension of the Jacobi method by using Chebyshev polynomials. With degree +zero, the Jacobi method with optimal damping parameter is retrieved, whereas +higher order corrections improve the smoothing properties if some parameters +are suitably chosen. The effectiveness of Chebyshev smoothing in multigrid has +been demonstrated, e.g., in the article +M. Adams, M. Brezina, J. Hu, R. Tuminaro. Parallel multigrid smoothers: +polynomial versus Gauss–Seidel, J. Comput. Phys. 188:593–610, +2003 . This publication also identifies one more advantage of +Chebyshev smoothers that we exploit here, namely that they are easy to +parallelize, whereas SOR/Gauss–Seidel smoothing relies on substitutions, +for which a naive parallelization works on diagonal sub-blocks of the matrix, +thereby decreases efficiency (for more detail see e.g. Y. Saad, +Iterative Methods for Sparse Linear Systems, SIAM, 2nd edition, 2003, chapters +11 & 12). + +The implementation into the multigrid framework is then straightforward. The multigrid implementation in this program is based on an earlier version of step-16 that demonstrated multigrid -on uniformly refined grids. However, the present matrix-free techniques would -obviously also apply to the adaptive meshes the current step-16 uses. +on uniformly refined grids. -

The test case

- -In order to demonstrate the capabilities of the method, we work on a rather -general Poisson problem, based on a more or less unstructured mesh (where -the Jacobians are different from cell to cell), higher order mappings to a -curved boundary, and a non-constant coefficient in the equation. If we -worked on a constant-coefficient case with structured mesh, we could -decrease the operation count by a factor of 4 in 2D and 6 in 3D by building -a local matrix (which is then the same for all cells), and doing the -products as in the first developing step of the above code pieces. diff --git a/deal.II/examples/step-37/doc/results.dox b/deal.II/examples/step-37/doc/results.dox index 6e00f0b1a1..8c269d575b 100644 --- a/deal.II/examples/step-37/doc/results.dox +++ b/deal.II/examples/step-37/doc/results.dox @@ -3,309 +3,236 @@

Program output

Since this example solves the same problem as step-5 (except for -a different coefficient), we refer to the graphical output there. Here, we -evaluate some aspects of the multigrid solver. +a different coefficient), there is little to say about the +solution. We show a picture anyway, illustrating the size of the +solution through both isocontours and volume rendering: +@image html "step-37.solution.png" + +Of more interest is to evaluate some aspects of the multigrid solver. When we run this program in 2D for quadratic ($Q_2$) elements, we get the following output: @code Cycle 0 -Number of degrees of freedom: 337 -System matrix memory consumption: 0.02573 MiB. -Multigrid objects memory consumption: 0.05083 MiB. -Convergence in 10 CG iterations. +Number of degrees of freedom: 81 +System matrix memory consumption: 0.008982 MB. +Multigrid objects memory consumption: 0.02617 MB. +Total setup time (wall) 0.001811s +Time solve (5 iterations) (CPU/wall) 0s/0.0002651s Cycle 1 -Number of degrees of freedom: 1313 -System matrix memory consumption: 0.09257 MiB. -Multigrid objects memory consumption: 0.1794 MiB. -Convergence in 10 CG iterations. +Number of degrees of freedom: 289 +System matrix memory consumption: 0.01817 MB. +Multigrid objects memory consumption: 0.05779 MB. +Total setup time (wall) 0.001223s +Time solve (5 iterations) (CPU/wall) 0s/0.000926s Cycle 2 -Number of degrees of freedom: 5185 -System matrix memory consumption: 0.3553 MiB. -Multigrid objects memory consumption: 0.6779 MiB. -Convergence in 10 CG iterations. +Number of degrees of freedom: 1089 +System matrix memory consumption: 0.05286 MB. +Multigrid objects memory consumption: 0.1581 MB. +Total setup time (wall) 0.003045s +Time solve (6 iterations) (CPU/wall) 0.012s/0.003393s Cycle 3 -Number of degrees of freedom: 20609 -System matrix memory consumption: 1.397 MiB. -Multigrid objects memory consumption: 2.645 MiB. -Convergence in 10 CG iterations. +Number of degrees of freedom: 4225 +System matrix memory consumption: 0.1957 MB. +Multigrid objects memory consumption: 0.5228 MB. +Total setup time (wall) 0.008561s +Time solve (6 iterations) (CPU/wall) 0.02s/0.01133s Cycle 4 -Number of degrees of freedom: 82177 -System matrix memory consumption: 5.546 MiB. -Multigrid objects memory consumption: 10.46 MiB. -Convergence in 10 CG iterations. +Number of degrees of freedom: 16641 +System matrix memory consumption: 0.7343 MB. +Multigrid objects memory consumption: 1.925 MB. +Total setup time (wall) 0.02938s +Time solve (6 iterations) (CPU/wall) 0.068s/0.03312s Cycle 5 -Number of degrees of freedom: 328193 -System matrix memory consumption: 22.11 MiB. -Multigrid objects memory consumption: 41.65 MiB. -Convergence in 10 CG iterations. +Number of degrees of freedom: 66049 +System matrix memory consumption: 2.856 MB. +Multigrid objects memory consumption: 7.435 MB. +Total setup time (wall) 0.1128s +Time solve (6 iterations) (CPU/wall) 0.228s/0.09577s + +Cycle 6 +Number of degrees of freedom: 263169 +System matrix memory consumption: 11.28 MB. +Multigrid objects memory consumption: 29.3 MB. +Total setup time (wall) 0.4553s +Time solve (6 iterations) (CPU/wall) 1.272s/0.3955s @endcode As in step-16, we see that the number of CG iterations remains constant with increasing number of degrees of freedom. We can also see that the various objects we have to store for the multigrid method on the individual levels of -our mesh together make up about twice as much as the matrix on the finest -level. +our mesh together make up more than twice as much as the matrix on the finest +level. For the present example, about half the memory consumption of the +multigrid objects are the level transfer matrices, and the other half is +consumed by the matrix-free objects (and there, mainly the indices and the +variable coefficient). -Not much changes if we run the -program in three spatial dimensions, with the exception that the multilevel -objects now take up comparatively less space (because in 3d, each level has -only one eighth the number of cells of the next finer one, whereas in 2d this -factor if one quarter): +Not much changes if we run the program in three spatial dimensions, with the +exception that the multilevel objects now take up some more memory (because +the level transfer matrices are denser) and the computing times are somewhat +larger: @code Cycle 0 -Number of degrees of freedom: 517 -System matrix memory consumption: 0.1001 MiB. -Multigrid objects memory consumption: 0.1463 MiB. -Convergence in 9 CG iterations. +Number of degrees of freedom: 125 +System matrix memory consumption: 0.01093 MB. +Multigrid objects memory consumption: 0.03094 MB. +Total setup time (wall) 0.002481s +Time solve (5 iterations) (CPU/wall) 0s/0.000334s Cycle 1 -Number of degrees of freedom: 3817 -System matrix memory consumption: 0.6613 MiB. -Multigrid objects memory consumption: 0.8896 MiB. -Convergence in 10 CG iterations. +Number of degrees of freedom: 729 +System matrix memory consumption: 0.04105 MB. +Multigrid objects memory consumption: 0.1274 MB. +Total setup time (wall) 0.004471s +Time solve (5 iterations) (CPU/wall) 0.004s/0.001979s Cycle 2 -Number of degrees of freedom: 29521 -System matrix memory consumption: 5.1 MiB. -Multigrid objects memory consumption: 6.653 MiB. -Convergence in 10 CG iterations. +Number of degrees of freedom: 4913 +System matrix memory consumption: 0.2821 MB. +Multigrid objects memory consumption: 0.8048 MB. +Total setup time (wall) 0.01651s +Time solve (4 iterations) (CPU/wall) 0.036s/0.01295s Cycle 3 -Number of degrees of freedom: 232609 -System matrix memory consumption: 40.4 MiB. -Multigrid objects memory consumption: 52.24 MiB. -Convergence in 11 CG iterations. +Number of degrees of freedom: 35937 +System matrix memory consumption: 1.948 MB. +Multigrid objects memory consumption: 5.734 MB. +Total setup time (wall) 0.1072s +Time solve (5 iterations) (CPU/wall) 0.16s/0.0709s Cycle 4 -Number of degrees of freedom: 1847617 -System matrix memory consumption: 322 MiB. -Multigrid objects memory consumption: 415.1 MiB. -Convergence in 11 CG iterations. +Number of degrees of freedom: 274625 +System matrix memory consumption: 14.49 MB. +Multigrid objects memory consumption: 44.41 MB. +Total setup time (wall) 0.8173s +Time solve (5 iterations) (CPU/wall) 1.52s/0.5093s + +Cycle 5 +Number of degrees of freedom: 2146689 +System matrix memory consumption: 115.9 MB. +Multigrid objects memory consumption: 342.6 MB. +Total setup time (wall) 6.387s +Time solve (5 iterations) (CPU/wall) 12.45s/3.767s @endcode

Comparison with a sparse matrix

-In order to understand the capabilities of this class, we compare the memory -consumption and execution (wallclock) time for assembly and 50 matrix-vector -products (MV) on a 3D problem with one million unknowns the classical -sparse matrix implementation (SpM) and the MatrixFree implementation shown -here (M-F). Both matrices are based on @p double %numbers. The program is run -on a 2.8 GHz Opteron processor with the ACML -BLAS. We present results running on one core core and four cores, -respectively. Moreover, we measure the time it takes to construct the -individual matrices and filling them with data (@p setup and @p assemble -functions). The sparse matrix is initialized using a -CompressedSimpleSparsityPattern for calling the -DoFTools::make_sparsity_pattern function, and then copied to a SparsityPattern -object. The boundary nodes are eliminated using the ConstraintMatrix class, so -that only elements that are actually nonzero are stored in the matrix. +In order to understand the capabilities of the matrix-free implementation, we +compare the performance on the 3d example above with a SparseMatrix +implementation and we measure the computation times for both initialization of +the problem (distribute DoFs, setup and assemble matrices, setup multigrid +structures) and the actual solution for the matrix-free variant and the +variant based on sparse matrices. We base the preconditioner on float +numbers and the actual matrix and vectors on double numbers, as shown +above. Tests are run on an Intel Core i7-2620M notebook processor (two cores +and AVX +support, i.e., four operations on doubles can be done with one CPU +instruction, which is heavily used in FEEvaluation) and optimized mode. The +example makes use of multithreading, so both cores are actually used. - - - - + + - - - - - - - - - + + + + + - - - - - - - - - + + + + + - - - - - - - - - + + + + + - - - - - - - - - + + + + + - - - - - - - - - + + + + + - - - - - - - - - + + + + + - - - - - - - - - + + + + +
 Memory consumptionTime assemblyTime 50 MV, 1 CPUTime 50 MV, 4 CPUsSparse matrixMatrix-free implementation
element orderSpMM-FSpMM-FSpMM-FSpMM-Fn_dofsSetup + assemble Solve Setup + assemble Solve 
1299 MiB394 MiB8.09 s3.43 s5.50 s22.4 s4.30 s11.0 s1250.0048s0.00075s0.0025s0.00033s
2698 MiB177 MiB12.43 s1.32 s12.0 s18.6 s9.10 s6.31 s7290.014s0.0022s0.0026s0.0018s
31295 MiB124 MiB41.1 s1.31 s21.2 s23.7 s16.0 s7.43 s4,9130.10s0.012s0.017s0.013s
42282 MiB107 MiB117 s1.97 s40.8 s36.3 s19.7 s10.9 s35,9370.80s0.14s0.11s0.071s
53597 MiB96.4 MiB510 s5.52 s75.7 s53.9 s29.3 s15.7 s274,6255.93s1.05s0.82s0.51s
65679 MiB96.3 MiB2389 s26.1 s135 s79.1 s45.8 s24.3 s2,146,68946.7s8.44s6.39s3.77s
-There are a few interesting things with the %numbers in this table. - -Firstly, we see the disappointing fact that for linear elements the -MatrixFree class does actually consume more memory than a SparseMatrix with -its SparsityPattern, despite the efforts made in this program. As mentioned -earlier, this is mostly because the Transformation data is stored for every -quadrature point. For each quadrature point, the transformation consists of -six doubles, and there are about eight times as many quadrature points as -there are degrees of freedom. In first approximation, this means that the -matrix consumes 384 (= @p sizeof(double) * 6 * 8) bytes for each degree of -freedom. On the other hand, the sparse matrix has a bandwidth of 27 or less, -so each dof gives rise to at most 324 (= 27 * 12) bytes. A more clever -implementation would try to compress the Jacobian transformation data, by -exploiting similarities between the mappings within the cells, as well as from -one cell to the next. This could dramatically reduce the memory requirements, -and hence, increase the speed for lower-order implementations. - -Secondly, we observe that the memory requirements for a SparseMatrix grow -quickly as the order of the elements increases. This is because there are -increasingly many entries in each row, which exist due to more degrees of -freedom that couple to each other. The matrix-free implementation does not -suffer from this drawback. Here, the memory consumption decreases instead, -since there are less DoFs that are shared among elements, which decreases the -relative amount of quadrature points. Regarding the execution speed, we see -that the matrix-free variant gets more competitive with higher order, and it -does scale better when run on multiple processors (3.5 speedup with four -processors compared to the serial case, compared to 2-2.5 speedup for the -SparseMatrix). The advantage in %parallel scaling was expected, because the -matrix-free variant is less memory-bound for higher order implementations, so -that the additional computing power from many cores can better be exploited. - -A third thing, which is unrelated to this tutorial program, is the fact that -standard matrix assembly gets really slow for high order elements. The %numbers -shown here are based on the usual routines that many other tutorial programs -make use of. A closer analysis of this shows that the cell data does not fit -into cache anymore. One could circumvent this problem by writing the assembly -as a matrix-matrix product, and using (cache-aware) BLAS implementations. - -For completeness, here comes a similar table for a 2D problem with 5.7 -million unknowns. Since the excess in work for the matrix-free -implementation is less compared to 3D, the implementation is more competitive -for lower-order elements. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Memory consumptionTime assemblyTime 50 MV, 4 CPUs
element orderSpMM-FSpMM-FSpMM-F
1659 MiB661 MiB18.8 s6.45 s11.0 s28.8 s
21119 MiB391 MiB15.6 s2.46 s17.1 s16.2 s
31711 MiB318 MiB17.4 s1.82 s23.1 s13.7 s
42434 MiB285 MiB24.2 s1.34 s31.1 s14.6 s
53289 MiB266 MiB35.9 s1.26 s29.6 s16.7 s
64274 MiB254 MiB58.0 s1.12 s35.9 s19.4 s
+The table clearly shows that the matrix-free implementation is twice as fast +for the solver, and more than six times as fast when it comes to +initialization costs. As the problem size is made a factor 8 larger, we note +that the times usually go up by a factor eight, too (as the solver iterations +are constant at 5). There are two deviations. The first is in the sparse +matrix between 5k and 36k degrees of freedom, where the time increases by a +factor 12. This is the threshold when the cache in the processor can no longer +hold all data necessary for the matrix-vector products and all matrix elements +must be fetched from main memory. The second deviation is the times for the +matrix-free solve which increase by less than a factor 8. This is because of +more parallelism from more cells, exploited by the (involved) dynamic tasks +scheduling approach CellFEOperator. Note that about 30% of the time in the +matrix-free solver is spent on restriction and prolongation, which use sparse +matrices. So the speedup could be even better if all parts where done +efficiently. + +Of course, this picture does not necessarily translate to all cases, as there +are problems where knowledge of matrix entries enables much better solvers (as +happens when the coefficient is varying more strongly than in the above +example). Moreover, it also depends on the computer system. The present system +has good memory performance, so sparse matrices perform comparably +well. Nonetheless, the matrix-free implementation gives a nice speedup already +for the Q2 elements used in this example. This becomes +particularly apparent for time-dependent or nonlinear problems where sparse +matrices would need to be reassembled over and over again, which becomes much +easier with this class. And of course, thanks to the better complexity of the +products, the method gains increasingly larger advantages when the order of the +elements increases (the matrix-free implementation has costs +4d2p per degree of freedom, compared to +2pd for the sparse matrix, so it will win anyway for order 4 +and higher in 3d). + +

Possibilities for extensions

+ +Above, we have shown figures for second-order finite elements. Our +implementation gains more compared to sparse matrices if higher order elements +are used. However, FE_Q elements with equidistant nodes are badly conditioned +if the order increases. In this case, the smoother and the multigrid solver +break down. Node clustering close to the element boundaries resolves this +problem (and the multigrid solver converges in 5 or 6 iterations also for very +high order). Elements with this properties are the Gauss-Lobatto FE_Q +elements, which are presented in step-48. diff --git a/deal.II/examples/step-37/doc/tooltip b/deal.II/examples/step-37/doc/tooltip index d469776e90..65cc3a07fc 100644 --- a/deal.II/examples/step-37/doc/tooltip +++ b/deal.II/examples/step-37/doc/tooltip @@ -1 +1 @@ -Matrix-free methods. Multigrid. +Matrix-free methods. Multigrid. Cell-based finite element operator. diff --git a/deal.II/examples/step-37/step-37.cc b/deal.II/examples/step-37/step-37.cc index eb8224b20b..8586e70297 100644 --- a/deal.II/examples/step-37/step-37.cc +++ b/deal.II/examples/step-37/step-37.cc @@ -1,21 +1,20 @@ -/* Authors: Katharina Kormann, Martin Kronbichler, Uppsala University, 2009 */ +/* Author: Katharina Kormann, Martin Kronbichler, Uppsala University, 2009-2011 */ /* $Id$ */ /* */ -/* Copyright (C) 2009, 2010, 2012 by the deal.II authors */ +/* Copyright (C) 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. */ - - // To start with the include files are more - // or less the same as in step-16: + // First include the necessary files + // from the deal.II library. #include #include #include -#include +#include #include #include @@ -43,1571 +42,1482 @@ #include #include + // This includes the data structures for the + // efficient implementation of matrix-free + // methods or more generic finite element + // operators with the class MatrixFree. +#include +#include + #include #include -using namespace dealii; - - - - // @sect3{Equation data} - - // We define a variable coefficient function - // for the Poisson problem. It is similar to - // the function in step-5 but we use the form - // $a(\mathbf x)=\frac{1}{0.1 + \|\bf x\|^2}$ - // instead of a discontinuous one. It is - // merely to demonstrate the possibilities of - // this implementation, rather than making - // much sense physically. -template -class Coefficient : public Function -{ - public: - Coefficient () : Function() {} - - virtual double value (const Point &p, - const unsigned int component = 0) const; - - virtual void value_list (const std::vector > &points, - std::vector &values, - const unsigned int component = 0) const; -}; - - - -template -double Coefficient::value (const Point &p, - const unsigned int /*component*/) const -{ - return 1./(0.1+p.square()); -} - - -template -void Coefficient::value_list (const std::vector > &points, - std::vector &values, - const unsigned int component) const +namespace Step37 { - Assert (values.size() == points.size(), - ExcDimensionMismatch (values.size(), points.size())); - Assert (component == 0, - ExcIndexRange (component, 0, 1)); - - const unsigned int n_points = points.size(); - - for (unsigned int i=0; i + class Coefficient : public Function + { + public: + Coefficient () : Function() {} + virtual double value (const Point &p, + const unsigned int component = 0) const; + template + number value (const Point &p, + const unsigned int component = 0) const; - // @sect3{Matrix-free implementation} - - // In this program, we want to make - // use of the ability of deal.II to - // runs things in %parallel if compute - // resources are available. We will - // follow the general framework laid - // out in the @ref threads module and - // use the WorkStream class to do - // operations on the range of all - // cells. - // - // To this end, we first have to have - // a few declarations that we use for - // defining the %parallel layout of - // the vector multiplication function - // with the WorkStream concept in the - // Matrix-free class. These comprise - // so-called scratch data that we use - // for calculating cell-related - // information, and copy data that is - // eventually used in a separate - // function for writing local data - // into the global vector. The reason - // for this split-up definition is - // that many threads at a time can - // execute the local multiplications - // (and filling up the copy data), - // but than that copy data needs to - // be worked on by one process at a - // time. -namespace WorkStreamData -{ - template - struct ScratchData - { - ScratchData (); - ScratchData (const ScratchData &scratch); - FullMatrix solutions; + virtual void value_list (const std::vector > &points, + std::vector &values, + const unsigned int component = 0) const; }; - template - ScratchData::ScratchData () - : - solutions () - {} - template - ScratchData::ScratchData (const ScratchData &) - : - solutions () - {} + // This is the new function mentioned + // above: Evaluate the coefficient for + // abstract type @p number: It might be + // just a usual double, but it can also be + // a somewhat more complicated type that we + // call VectorizedArray. This data type is + // essentially a short array of doubles + // whose length depends on the particular + // computer system in use. For example, + // systems based on x86-64 support the + // streaming SIMD extensions (SSE), where + // the processor's vector units can process + // two doubles (or four single-precision + // floats) by one CPU instruction. Newer + // processors with support for the + // so-called advanced vector extensions + // (AVX) with 256 bit operands can use four + // doubles and eight floats, + // respectively. Vectorization is a + // single-instruct/multiple-data (SIMD) + // concept, that is, one CPU instruction is + // used to process multiple data values at + // once. Often, finite element programs do + // not use vectorization explicitly as the + // benefits of this concept are only in + // arithmetic intensive operations. The + // bulk of typical finite element workloads + // are memory bandwidth limited (operations + // on sparse matrices and vectors) where + // the additional computational power is + // useless. + // + // Behind the scenes, optimized BLAS + // packages might heavily rely on + // vectorization, though. Also, optimizing + // compilers might automatically transform + // loops involving standard code into more + // efficient vectorized form. However, the + // data flow must be very regular in order + // for compilers to produce efficient + // code. For example, already the automatic + // vectorization of the prototype operation + // that benefits from vectorization, + // matrix-matrix products, fails on most + // compilers (as of writing this tutorial + // in early 2012, neither gcc-4.6 nor the + // Intel compiler v. 12 manage to produce + // useful vectorized code for the + // FullMatrix::mmult function, and not even + // on the more simpler case where the + // matrix bounds are compile-time constants + // instead of run-time constants as in + // FullMatrix::mmult). The main reason for + // this is that the information to be + // processed at the innermost loop (that is + // where vectorization is applied) is not + // necessarily a multiple of the vector + // length, leaving parts of the resources + // unused. Moreover, the data that can + // potentially be processed together might + // not be laid out in a contiguous way in + // memory or not with the necessary + // alignment to address boundaries that are + // needed by the processor. Or the compiler + // might not be able to prove that. + // + // In the matrix-free implementation in + // deal.II, we have therefore chosen to + // apply vectorization at the level which + // is most appropriate for finite element + // computations: The cell-wise computations + // are typically exactly the same for all + // cells (except for reading from and + // writing to vectors), and hence SIMD can + // be used to process several cells at + // once. In all what follows, you can think + // of an AlignedVector to hold data from + // several cells. For example, we evaluate + // the coefficient shown here not on a + // simple point as usually done, but we + // hand it a + // Point > + // point, which is actually a collection of + // two points in the case of SSE2. Do not + // confuse the entries in + // VectorizedArray with the + // different coordinates of the + // point. Indeed, the data is laid out such + // that p[0] returns a + // VectorizedArray, which in turn + // contains the x-coordinate for the first + // point and the second point. You may + // access the coordinates individually + // using e.g. p[0][j], j=1,2, + // but it is recommended to define + // operations on a VectorizedArray as much + // as possible in order to make use of + // vectorized operations. + // + // In the function implementation, we + // assume that the number type overloads + // basic arithmetic operations, so we just + // write the code as usual. The standard + // functions @p value and value_list that + // are virtual functions contained in the + // base class are then computed from the + // templated function with double type, in + // order to avoid duplicating code. + template template - struct CopyData : public ScratchData + number Coefficient::value (const Point &p, + const unsigned int /*component*/) const { - CopyData (); - CopyData (const CopyData &scratch); - unsigned int first_cell; - unsigned int n_dofs; - }; + return 1. / (0.05 + 2.*p.square()); + } - template - CopyData::CopyData () - : - ScratchData () - {} - - template - CopyData::CopyData (const CopyData &) - : - ScratchData () - {} - -} - - - - // Next comes the implementation of the - // matrix-free class. It provides some - // standard information we expect for - // matrices (like returning the dimensions - // of the matrix), it implements - // matrix-vector multiplications in several - // forms, and it provides functions for - // filling the matrix with data. - // - // We choose to make this class generic, - // i.e., we do not implement the actual - // differential operator (here: Laplace - // operator) directly in this class. We - // instead let the actual transformation - // (which happens on the level of quadrature - // points, see the discussion in the - // introduction) be a template parameter that - // is implemented by another class. We then - // only have to store a list of these objects - // for each quadrature point on each cell in - // a big list – we choose a - // Table<2,Transformation> data - // format) – and call a transform - // command of the @p Transformation - // class. This template magic makes it easy - // to reuse this MatrixFree class for other - // problems that are based on a symmetric - // operation without the need for substantial - // changes. -template -class MatrixFree : public Subscriptor -{ - public: - MatrixFree (); - - void reinit (const unsigned int n_dofs, - const unsigned int n_cells, - const FullMatrix &cell_matrix, - const unsigned int n_points_per_cell); - void clear(); - - unsigned int m () const; - unsigned int n () const; - ConstraintMatrix & get_constraints (); - - void set_local_dof_indices (const unsigned int cell_no, - const std::vector &local_dof_indices); - void set_derivative_data (const unsigned int cell_no, - const unsigned int quad_point, - const Transformation &trans_in); - - template - void vmult (Vector &dst, - const Vector &src) const; - template - void Tvmult (Vector &dst, - const Vector &src) const; - template - void vmult_add (Vector &dst, - const Vector &src) const; - template - void Tvmult_add (Vector &dst, - const Vector &src) const; - - number el (const unsigned int row, - const unsigned int col) const; - void calculate_diagonal () const; - - std::size_t memory_consumption () const; - - // The private member variables of the - // @p MatrixFree class are a - // small matrix that does the - // transformation from solution values to - // quadrature points, a list with the - // mapping between local degrees of freedom - // and global degrees of freedom for each - // cell (stored as a two-dimensional array, - // where each row corresponds to one - // cell, and the columns within individual - // cells are the local degrees of freedom), - // the transformation variable for - // implementing derivatives, a constraint - // matrix for handling boundary conditions - // as well as a few other variables that - // store matrix properties. - private: - typedef std::vector >::const_iterator - CellChunkIterator; - template - void local_vmult (CellChunkIterator cell_range, - WorkStreamData::ScratchData &scratch, - WorkStreamData::CopyData ©, - const Vector &src) const; - - template - void - copy_local_to_global (const WorkStreamData::CopyData ©, - Vector &dst) const; - - FullMatrix B_ref_cell; - Table<2,unsigned int> indices_local_to_global; - Table<2,Transformation> derivatives; - - ConstraintMatrix constraints; - - mutable Vector diagonal_values; - mutable bool diagonal_is_calculated; - - struct MatrixSizes - { - unsigned int n_dofs, n_cells; - unsigned int m, n; - unsigned int n_points, n_comp; - std::vector > chunks; - } matrix_sizes; -}; - - - - // This is the constructor of the @p - // MatrixFree class. All it does is to - // subscribe to the general deal.II @p - // Subscriptor scheme that makes sure that we - // do not delete an object of this class as - // long as it used somewhere else, e.g. in a - // preconditioner. -template -MatrixFree::MatrixFree () - : - Subscriptor() -{} - - - - // The next functions return the - // number of rows and columns of the - // global matrix (i.e. the dimensions - // of the operator this class - // represents, the point of this - // tutorial program was, after all, - // that we don't actually store the - // elements of the rows and columns - // of this operator). Since the - // matrix is square, the returned - // numbers are the same. -template -unsigned int -MatrixFree::m () const -{ - return matrix_sizes.n_dofs; -} - - - -template -unsigned int -MatrixFree::n () const -{ - return matrix_sizes.n_dofs; -} - - - - // One more function that just returns an - // %internal variable. Note that the user - // will need to change this variable, so it - // returns a non-constant reference to the - // ConstraintMatrix. -template -ConstraintMatrix & -MatrixFree::get_constraints () -{ - return constraints; -} - - - - // The following function takes a vector of - // local dof indices on cell level and writes - // the data into the - // @p indices_local_to_global field - // in order to have fast access to it. It - // performs a few sanity checks like whether - // the sizes in the matrix are set - // correctly. One tiny thing: Whenever we - // enter this function, we probably make some - // modification to the matrix. This means - // that the diagonal of the matrix, which we - // might have computed to have fast access to - // those elements, is invalidated. We set the - // respective flag to @p false. -template -void MatrixFree:: -set_local_dof_indices (const unsigned int cell_no, - const std::vector &local_dof_indices) -{ - Assert (local_dof_indices.size() == matrix_sizes.m, - ExcDimensionMismatch(local_dof_indices.size(), - matrix_sizes.m)); - for (unsigned int i=0; i -void MatrixFree:: -set_derivative_data (const unsigned int cell_no, - const unsigned int quad_point, - const Transformation &trans_in) -{ - Assert (quad_point < matrix_sizes.n_points, ExcInternalError()); - derivatives(cell_no,quad_point) = trans_in; - diagonal_is_calculated = false; -} - - - - // Now finally to the central function of the - // matrix-free class, implementing the - // multiplication of the matrix with a - // vector. This function does not actually - // work on all cells of a mesh, but only the - // subset of cells specified by the first - // argument @p cell_range. Since this - // function operates similarly irrespective - // on which cell chunk we are sitting, we can - // call it simultaneously on many processors, - // but with different cell range data. - // - // The goal of this function is to provide - // the multiplication of a vector with the - // local contributions of a set of cells. As - // mentioned in the introduction, if we were - // to deal with a single cell, this would - // amount to performing the product - // @f{eqnarray*} - // P^T_\mathrm{cell,local-global} A_\mathrm{cell} - // P_\mathrm{cell,local-global} x - // @f} - // where - // @f{eqnarray*} - // A_\mathrm{cell} = - // B_\mathrm{ref\_cell}^T J_\mathrm{cell}^T - // D_\mathrm{cell} - // J_\mathrm{cell} B_\mathrm{ref\_cell} - // @f} - // and Pcell,local-global - // is the transformation from local to global - // indices. - // - // To do this, we would have to do the - // following steps: - //
    - //
  1. Form $x_\mathrm{cell} = - // P_\mathrm{cell,local-global} x$. This is - // done by using the command - // ConstraintMatrix::get_dof_values. - //
  2. Form $x_1 = B_\mathrm{ref\_cell} - // x_\mathrm{cell}$. The vector - // x1 contains the - // reference cell gradient to the local - // cell vector. - //
  3. Form $x_2 = J_\mathrm{cell}^T - // D_\mathrm{cell} J_\mathrm{cell} - // x_1$. This is a block-diagonal - // operation, with the block size equal to - // @p dim. The blocks just - // correspond to the individual quadrature - // points. The operation on each quadrature - // point is implemented by the - // Transformation class object that this - // class is equipped with. Compared to the - // introduction, the matrix - // Dcell now contains the - // @p JxW values and the - // inhomogeneous coefficient. - //
  4. Form $y_\mathrm{cell} = - // B_\mathrm{ref\_cell}^T x_2$. This gives - // the local result of the matrix-vector - // product. - //
  5. Form $y \leftarrow y + - // P_\mathrm{cell,local-global}^T - // y_\mathrm{cell}$. This adds the local - // result to the global vector, which is - // realized using the method - // ConstraintMatrix::distribute_local_to_global. - // Note that we do this in an extra - // function called - // @p copy_local_to_global - // because that operation must not be done - // in %parallel, in order to avoid two or - // more processes trying to add to the same - // positions in the result vector y. - //
- // The steps 1 to 4 can be done in %parallel - // by multiple processes. - - // Now, it turns out that the most expensive - // part of the above is the multiplication - // Bref_cell - // xcell in the second step - // and the transpose operation in step - // 4. Note that the matrix - // JT D J is - // block-diagonal, and hence, its application - // is cheaper. Since the matrix - // Bref_cell is the same - // for all cells, all that changes is the - // vector xcell. Hence, - // nothing prevents us from collecting - // several cell vectors to a (rectangular) - // matrix, and then perform a matrix-matrix - // product. These matrices are both full, but - // not very large, having of the order @p - // dofs_per_cell rows and columns. This is an - // operation that can be much better - // optimized than matrix-vector products. The - // functions @p FullMatrix::mmult and - // @p FullMatrix::mTmult use the BLAS - // dgemm function (as long as BLAS has been - // detected in deal.II configuration), which - // provides optimized kernels for doing this - // product. In our case, a matrix-matrix - // product is between three and five times - // faster than doing the matrix-vector - // product on one cell after the other. The - // variables that hold the solution on the - // respective cell's support points and the - // quadrature points are thus full matrices, - // which we set to the correct size as a - // first action in this function. The number - // of rows in the two matrices @p - // scratch.solutions and @p copy.solutions is - // given by the number of cells they work on, - // and the number of columns is the number of - // degrees of freedom per cell for the first - // and the number of quadrature points times - // the number of components per point for the - // latter. -template -template -void -MatrixFree:: -local_vmult (CellChunkIterator cell_range, - WorkStreamData::ScratchData &scratch, - WorkStreamData::CopyData ©, - const Vector &src) const -{ - const unsigned int chunk_size = cell_range->second - cell_range->first; - - scratch.solutions.reinit (chunk_size, matrix_sizes.n, true); - copy.solutions.reinit (chunk_size, matrix_sizes.m, true); - copy.first_cell = cell_range->first; - copy.n_dofs = chunk_size*matrix_sizes.m; - - constraints.get_dof_values(src, &indices_local_to_global(copy.first_cell,0), - ©.solutions(0,0), - ©.solutions(0,0)+copy.n_dofs); - - copy.solutions.mmult (scratch.solutions, B_ref_cell); - - for (unsigned int i=0, k = copy.first_cell; i + double Coefficient::value (const Point &p, + const unsigned int component) const + { + return value(p,component); + } -template -template -void -MatrixFree:: -copy_local_to_global (const WorkStreamData::CopyData ©, - Vector &dst) const -{ - constraints.distribute_local_to_global (©.solutions(0,0), - ©.solutions(0,0)+copy.n_dofs, - &indices_local_to_global(copy.first_cell,0), - dst); -} + template + void Coefficient::value_list (const std::vector > &points, + std::vector &values, + const unsigned int component) const + { + Assert (values.size() == points.size(), + ExcDimensionMismatch (values.size(), points.size())); + Assert (component == 0, + ExcIndexRange (component, 0, 1)); + + const unsigned int n_points = points.size(); + for (unsigned int i=0; i(points[i],component); + } + + + + // @sect3{Matrix-free implementation} + + // The following class, called + // LaplaceOperator, + // implements the differential + // operator. For all practical + // purposes, it is a matrix, i.e., + // you can ask it for its size + // (member functions m(), + // n()) and you can apply it + // to a vector (the various + // variants of the + // vmult() + // function). The difference to a + // real matrix of course lies in + // the fact that this class doesn't + // actually store the + // elements of the matrix, + // but only knows how to compute + // the action of the operator when + // applied to a vector. + + // In this program, we want to make use of + // the data cache for finite element operator + // application that is integrated in + // deal.II. The main class that collects all + // data is called MatrixFree. It contains + // mapping information (Jacobians) and index + // relations between local and global degrees + // of freedom. It also contains constraints + // like the ones from Dirichlet boundary + // conditions (or hanging nodes, if we had + // any). Moreover, it can issue a loop over + // all cells in %parallel, where it makes + // sure that only cells are worked on that do + // not share any degree of freedom (this + // makes the loop thread-safe when writing + // into destination vectors). This is a more + // advanced strategy compared to the + // WorkStream class described in the @ref + // threads module that serializes operations + // that might not be thread-safe. Of course, + // to not destroy thread-safety, we have to + // be careful when writing into class-global + // structures. + // + // First comes the implementation of the + // matrix-free class. It provides some + // standard information we expect for + // matrices (like returning the dimensions of + // the matrix), it implements matrix-vector + // multiplications in several forms + // (transposed and untransposed), and it + // provides functions for initializing the + // structure with data. The class has three + // template arguments, one for the dimension + // (as many deal.II classes carry), one for the + // degree of the finite element (which we + // need to enable efficient computations + // through the FEEvaluation class), and one + // for the underlying scalar type. We want to use + // double numbers + // (i.e., double precision, 64-bit + // floating point) for the final + // matrix, but floats (single + // precision, 32-bit floating point + // numbers) for the multigrid level + // matrices (as that is only a + // preconditioner, and floats can + // be worked with twice as fast). + // + // In this class, we store the actual MatrixFree + // object, the variable + // coefficient that is evaluated at all + // quadrature points (so that we don't have + // to recompute it during matrix-vector + // products), and a vector that contains the + // diagonal of the matrix that we need for + // the multigrid smoother. We choose to let + // the user provide the diagonal in this + // program, but we could also integrate a + // function in this class to evaluate the + // diagonal. Unfortunately, this forces us to + // define matrix entries at two places, + // once when we evaluate the product and once + // for the diagonal, but the work is still + // much less than when we compute sparse + // matrices. + // + // As a sidenote, if we implemented + // several different operations on + // the same grid and degrees of + // freedom (like a mass matrix and + // a Laplace matrix), we would have + // to have two classes like the + // current one for each of the + // operators (maybe with a common + // base class). However, in that + // case, we would not store a + // MatrixFree object in this + // class to avoid doing the + // expensive work of pre-computing + // everything MatrixFree stores + // twice. Rather, we would keep + // this object in the main class + // and simply store a reference. + // + // @note Observe how we store the values + // for the coefficient: We use a vector + // type + // AlignedVector + // > structure. One would think that + // one can use + // std::vector + // > as well, but there are some + // technicalities with vectorization: A + // certain alignment of the data with the + // memory address boundaries is required + // (essentially, a VectorizedArray of 16 + // bytes length as in SSE needs to start at + // a memory address that is divisible by + // 16). The chosen class makes sure that + // this alignment is respected, whereas + // std::vector can in general not, which + // may lead to segmentation faults at + // strange places for some systems or + // suboptimal performance for other + // systems. + template + class LaplaceOperator : public Subscriptor + { + public: + LaplaceOperator (); - // Now to the @p vmult function that is - // called externally: In addition to what we - // do in a @p vmult_add function, we set the - // destination to zero first. -template -template -void -MatrixFree::vmult (Vector &dst, - const Vector &src) const -{ - dst = 0; - vmult_add (dst, src); -} + void clear(); + void reinit (const MGDoFHandler &dof_handler, + const ConstraintMatrix &constraints, + const unsigned int level = numbers::invalid_unsigned_int); + unsigned int m () const; + unsigned int n () const; - // Transposed matrix-vector products (needed - // for the multigrid operations to be - // well-defined): do the same. Since we - // implement a symmetric operation, we can - // refer to the @p vmult_add operation. -template -template -void -MatrixFree::Tvmult (Vector &dst, - const Vector &src) const -{ - dst = 0; - Tvmult_add (dst,src); -} + void vmult (Vector &dst, + const Vector &src) const; + void Tvmult (Vector &dst, + const Vector &src) const; + void vmult_add (Vector &dst, + const Vector &src) const; + void Tvmult_add (Vector &dst, + const Vector &src) const; + number el (const unsigned int row, + const unsigned int col) const; + void set_diagonal (const Vector &diagonal); + std::size_t memory_consumption () const; -template -template -void -MatrixFree::Tvmult_add (Vector &dst, - const Vector &src) const -{ - vmult_add (dst,src); -} + private: + void local_apply (const MatrixFree &data, + Vector &dst, + const Vector &src, + const std::pair &cell_range) const; + void evaluate_coefficient(const Coefficient &function); + MatrixFree data; + AlignedVector > coefficient; - // This is the @p vmult_add function that - // multiplies the matrix with vector @p src - // and adds the result to vector @p dst. We - // include a few sanity checks to make sure - // that the size of the vectors is the same - // as the dimension of the matrix. We call a - // %parallel function that applies the - // multiplication on a chunk of cells at once - // using the WorkStream module (cf. also the - // @ref threads module). The subdivision into - // chunks will be performed in the reinit - // function and is stored in the field @p - // matrix_sizes.chunks. What the rather - // cryptic command to @p std_cxx1x::bind does - // is to transform a function that has - // several arguments (source vector, chunk - // information) into a function which has - // three arguments (in the first case) or one - // argument (in the second), which is what - // the WorkStream::run function expects. The - // placeholders _1, std_cxx1x::_2, _3 in - // the local vmult specify variable input - // values, given by the chunk information, - // scratch data and copy data that the - // WorkStream::run function will provide, - // whereas the other arguments to the @p - // local_vmult function are bound: to @p this - // and a constant reference to the @p src in - // the first case, and @p this and a - // reference to the output vector in the - // second. Similarly, the placeholder - // @p _1 argument in the - // @p copy_local_to_global function - // sets the first explicit argument of that - // function, which is of class - // @p CopyData. We need to - // abstractly specify these arguments because - // the tasks defined by different cell chunks - // will be scheduled by the WorkStream class, - // and we will reuse available scratch and - // copy data. -template -template -void -MatrixFree::vmult_add (Vector &dst, - const Vector &src) const -{ - Assert (src.size() == n(), ExcDimensionMismatch(src.size(), n())); - Assert (dst.size() == m(), ExcDimensionMismatch(dst.size(), m())); - - WorkStream::run (matrix_sizes.chunks.begin(), matrix_sizes.chunks.end(), - std_cxx1x::bind(&MatrixFree:: - template local_vmult, - this, std_cxx1x::_1, std_cxx1x::_2, std_cxx1x::_3, boost::cref(src)), - std_cxx1x::bind(&MatrixFree:: - template copy_local_to_global, - this, std_cxx1x::_1, boost::ref(dst)), - WorkStreamData::ScratchData(), - WorkStreamData::CopyData(), - 2*multithread_info.n_default_threads,1); - - // One thing to be cautious about: - // The deal.II classes expect that - // the matrix still contains a - // diagonal entry for constrained - // dofs (otherwise, the matrix - // would be singular, which is not - // what we want). Since the - // distribute_local_to_global - // command of the constraint matrix - // which we used for adding the - // local elements into the global - // vector does not do anything with - // constrained elements, we have to - // circumvent that problem by - // artificially setting the - // diagonal to some non-zero value - // and adding the source values. We - // simply set it to one, which - // corresponds to copying the - // respective elements of the - // source vector into the matching - // entry of the destination vector. - for (unsigned int i=0; i -void MatrixFree:: -reinit (const unsigned int n_dofs_in, - const unsigned int n_cells_in, - const FullMatrix &B_ref_cell_in, - const unsigned int n_points_per_cell) -{ - B_ref_cell = B_ref_cell_in; - - derivatives.reinit (n_cells_in, n_points_per_cell); - indices_local_to_global.reinit (n_cells_in, B_ref_cell.m()); - - diagonal_is_calculated = false; - - matrix_sizes.n_dofs = n_dofs_in; - matrix_sizes.n_cells = n_cells_in; - matrix_sizes.m = B_ref_cell.m(); - matrix_sizes.n = B_ref_cell.n(); - matrix_sizes.n_points = n_points_per_cell; - matrix_sizes.n_comp = B_ref_cell.n()/matrix_sizes.n_points; - Assert(matrix_sizes.n_comp * n_points_per_cell == B_ref_cell.n(), - ExcInternalError()); - - // One thing to make the matrix-vector - // product with this class efficient is to - // decide how many cells should be combined - // to one chunk, which will determine the - // size of the full matrix that we work - // on. If we choose too few cells, then the - // gains from using the matrix-matrix - // product will not be fully utilized - // (dgemm tends to provide more efficiency - // the larger the matrix dimensions get), - // so we choose at least 60 cells for one - // chunk (except when there are very few - // cells, like on the coarse levels of the - // multigrid scheme). If we choose too - // many, we will degrade parallelization - // (we need to have sufficiently - // independent tasks). We need to also - // think about the fact that most high - // performance BLAS implementations - // internally work with square - // sub-matrices. Choosing as many cells in - // a chunk as there are degrees of freedom - // on each cell (coded in @p - // matrix_sizes.m) respects the BLAS GEMM - // design, whenever we exceed 60. Clearly, - // the chunk size is an - // architecture-dependent value and the - // interested user can squeeze out some - // extra performance by hand-tuning this - // parameter. Once we have chosen the - // number of cells we collect in one chunk, - // we determine how many chunks we have on - // the given cell range and recalculate the - // actual chunk size in order to evenly - // distribute the chunks. - const unsigned int divisor = std::max(60U, matrix_sizes.m); - const unsigned int n_chunks = std::max (matrix_sizes.n_cells/divisor + 1, - 2*multithread_info.n_default_threads); - - const unsigned int chunk_size = (matrix_sizes.n_cells/n_chunks + - (matrix_sizes.n_cells%n_chunks>0)); - - std::pair chunk; - for (unsigned int i=0; i matrix_sizes.n_cells) - chunk.second = matrix_sizes.n_cells; - else - chunk.second = (i+1)*chunk_size; - - if (chunk.second > chunk.first) - matrix_sizes.chunks.push_back(chunk); - else - break; - } -} + Vector diagonal_values; + bool diagonal_is_available; + }; - // Then we need a function if we want to - // delete the content of the matrix, - // e.g. when we are finished with one grid - // level and continue to the next one. Just - // set all the field sizes to 0. -template -void -MatrixFree::clear () -{ - B_ref_cell.reinit(0,0); - derivatives.reinit (0,0); - indices_local_to_global.reinit(0,0); + // This is the constructor of the @p + // LaplaceOperator class. All it does is to + // subscribe to the general deal.II @p + // Subscriptor scheme that makes sure that we + // do not delete an object of this class as + // long as it used somewhere else, e.g. in a + // preconditioner. + template + LaplaceOperator::LaplaceOperator () + : + Subscriptor() + {} - constraints.clear(); - diagonal_values.reinit (0); - diagonal_is_calculated = false; - - matrix_sizes.n_dofs = 0; - matrix_sizes.n_cells = 0; - matrix_sizes.chunks.clear(); -} + // The next functions return the + // number of rows and columns of + // the global matrix (i.e. the + // dimensions of the operator this + // class represents, the point of + // this tutorial program was, after + // all, that we don't actually + // store the elements of the rows + // and columns of this + // operator). Since the matrix is + // square, the returned numbers are + // the same. We get the number from + // the vector partitioner stored in + // the data field (a partitioner + // distributes elements of a vector + // onto a number of different + // machines if programs are run in + // %parallel; since this program is + // written to run on only a single + // machine, the partitioner will + // simply say that all elements of + // the vector -- or, in the current + // case, all rows and columns of a + // matrix -- are stored on the + // current machine). + template + unsigned int + LaplaceOperator::m () const + { + return data.get_vector_partitioner()->size(); + } - // The next function returns the entries of the - // matrix. Since this class is intended not - // to store the matrix entries, it would make - // no sense to provide all those - // elements. However, diagonal entries are - // explicitly needed for the implementation - // of the Chebyshev smoother that we intend - // to use in the multigrid - // preconditioner. This matrix is equipped - // with a vector that stores the diagonal, - // and we compute it when this function is - // called for the first time. -template -number -MatrixFree::el (const unsigned int row, - const unsigned int col) const -{ - Assert (row == col, ExcNotImplemented()); - if (diagonal_is_calculated == false) - calculate_diagonal(); - return diagonal_values(row); -} + template + unsigned int + LaplaceOperator::n () const + { + return data.get_vector_partitioner()->size(); + } - // Regarding the calculation of the diagonal, - // remember that this is as simple (or - // complicated) as assembling a right hand - // side in deal.II. Well, it is a bit easier - // to do this within this class since we have - // all the derivative information - // available. What we do is to go through all - // the cells (now in serial, since this - // function should not be called very often - // anyway), then all the degrees of - // freedom. At this place, we first copy the - // first basis functions in all the - // quadrature points to a temporary array, - // apply the derivatives from the Jacobian - // matrix, and finally multiply with the - // second basis function. This is exactly the - // value that would be written into the - // diagonal of a sparse matrix. Note that we - // need to condense hanging node constraints - // and set the constrained diagonals to one. -template -void -MatrixFree::calculate_diagonal() const -{ - diagonal_values.reinit (matrix_sizes.n_dofs); - std::vector calculation (matrix_sizes.n); - for (unsigned int cell=0; cell + void + LaplaceOperator::clear () + { + data.clear(); + diagonal_is_available = false; + diagonal_values.reinit(0); + } + + + // @sect4{Initialization} + + // Once we have created the multi-grid + // dof_handler and the constraints, we can + // call the reinit function for each level + // of the multi-grid routine (and the + // active cells). The main purpose of the + // reinit function is to setup the + // MatrixFree instance for the + // problem. Also, the coefficient is + // evaluated. For this, we need to activate + // the update flag in the AdditionalData + // field of MatrixFree that enables the + // storage of quadrature point coordinates + // in real space (by default, it only + // caches data for gradients (inverse + // transposed Jacobians) and JxW + // values). Note that if we call the reinit + // function without specifying the level + // (i.e., giving level = + // numbers::invalid_unsigned_int), + // we have told the class to loop over the + // active cells. + // + // We also set one option regarding + // task parallelism. We choose to + // use the @p partition_color + // strategy, which is based on + // subdivision of cells into + // partitions where cells in + // partition $k$ (or, more + // precisely, the degrees of + // freedom on these cells) only + // interact with cells in + // partitions $k-1$, $k$, and + // $k+1$. Within each partition, + // cells are colored in such a way + // that cells with the same color + // do not share degrees of freedom + // and can, therefore, be worked on + // at the same time without + // interference. This determines a + // task dependency graph that is + // scheduled by the Intel Threading + // Building Blocks library. Another + // option would be the strategy @p + // partition_partition, which + // performs better when the grid is + // more unstructured. We could also + // manually set the size of chunks + // that form one task in the + // scheduling process by setting @p + // tasks_block_size, but the + // default strategy to let the + // function decide works well + // already. + // + // To initialize the coefficient, + // we directly give it the + // Coefficient class defined above + // and then select the method + // coefficient_function.value + // with vectorized number (which + // the compiler can deduce from the + // point data type). The use of the + // FEEvaluation class (and its + // template arguments) will be + // explained below. + template + void + LaplaceOperator::reinit (const MGDoFHandler &dof_handler, + const ConstraintMatrix &constraints, + const unsigned int level) + { + typename MatrixFree::AdditionalData additional_data; + additional_data.tasks_parallel_scheme = + MatrixFree::AdditionalData::partition_color; + additional_data.level_mg_handler = level; + additional_data.mapping_update_flags = (update_gradients | update_JxW_values | + update_quadrature_points); + data.reinit (dof_handler, constraints, QGauss<1>(fe_degree+1), + additional_data); + evaluate_coefficient(Coefficient()); + } + + + + template + void + LaplaceOperator:: + evaluate_coefficient (const Coefficient &coefficient_function) + { + const unsigned int n_cells = data.get_size_info().n_macro_cells; + FEEvaluation phi (data); + coefficient.resize (n_cells * phi.n_q_points); + for (unsigned int cell=0; cell -std::size_t MatrixFree::memory_consumption () const -{ - std::size_t glob_size = derivatives.memory_consumption() + - indices_local_to_global.memory_consumption() + - constraints.memory_consumption() + - B_ref_cell.memory_consumption() + - diagonal_values.memory_consumption() + - matrix_sizes.chunks.size()*2*sizeof(unsigned int) + - sizeof(*this); - return glob_size; -} - - - - // @sect3{Laplace operator implementation} - - // This class implements the local action of - // a Laplace operator on a quadrature - // point. This is a very basic class - // implementation, providing functions for - // initialization with a Tensor of rank 2 and - // implementing the @p transform operation - // needed by the @p MatrixFree class. There - // is one point worth noting: The - // quadrature-point related action of the - // Laplace operator is a tensor of rank - // two. It is symmetric since it is the - // product of the inverse Jacobian - // transformation between unit and real cell - // with its transpose (times quadrature - // weights and a coefficient, which are - // scalar), so we can just save the diagonal - // and upper diagonal part. We could use the - // SymmetricTensor<2,dim> class for doing - // this, however, that class is only based on - // @p double %numbers. Since we also want to - // use @p float %numbers for the multigrid - // preconditioner (in order to save memory - // and computing time), we manually implement - // this operator. Note that @p dim is a - // template argument and hence known at - // compile-time, so the compiler knows that - // this symmetric rank-2 tensor has 3 entries - // if used in 2D and 6 entries if used in 3D. -template -class LaplaceOperator -{ - public: - LaplaceOperator (); - - LaplaceOperator (const Tensor<2,dim> &tensor); - - void transform (number * result) const; - - LaplaceOperator& - operator = (const Tensor<2,dim> &tensor); - - unsigned int memory_consumption () const; - - private: - number transformation[dim*(dim+1)/2]; -}; - -template -LaplaceOperator::LaplaceOperator() -{} - - -template -LaplaceOperator::LaplaceOperator(const Tensor<2,dim> &tensor) -{ - *this = tensor; -} - - // Now implement the transformation, which is - // just a so-called contraction - // operation between a tensor of rank two and a - // tensor of rank one. Unfortunately, we - // need to implement this by hand, since we - // chose not to use the - // SymmetricTensor<2,dim> class (note that - // the resulting values are entries in a full - // matrix that consists of doubles or - // floats). It feels a bit unsafe to operate - // on a pointer to the data, but that is the - // only possibility if we do not want to copy - // data back and forth, which is expensive - // since this is the innermost position of - // the loop in the @p vmult - // operation of the MatrixFree class. We need - // to pay attention to the fact that we only - // saved half of the (symmetric) rank-two - // tensor. - // - // At first sight, it seems inefficient that - // we have an @p if clause at this position - // in the code at the innermost loop, but - // note once again that @p dim is known when - // this piece of code is compiled, so the - // compiler can optimize away the @p if - // statement (and actually even inline these - // few lines of code into the @p MatrixFree - // class). -template -void LaplaceOperator::transform (number* result) const -{ - if (dim == 2) - { - const number temp = result[0]; - result[0] = transformation[0] * temp + transformation[1] * result[1]; - result[1] = transformation[1] * temp + transformation[2] * result[1]; - } - else if (dim == 3) - { - const number temp1 = result[0]; - const number temp2 = result[1]; - result[0] = transformation[0] * temp1 + transformation[1] * temp2 + - transformation[2] * result[2]; - result[1] = transformation[1] * temp1 + transformation[3] * temp2 + - transformation[4] * result[2]; - result[2] = transformation[2] * temp1 + transformation[4] * temp2 + - transformation[5] * result[2]; - } - else - AssertThrow(false, ExcNotImplemented()); -} + } + + + + // @sect4{Local evaluation of Laplace operator} + + // Here comes the main function of this + // class, the evaluation of the + // matrix-vector product (or, in general, a + // finite element operator + // evaluation). This is done in a function + // that takes exactly four arguments, the + // MatrixFree object, the destination and + // source vectors, and a range of cells + // that are to be worked on. The method + // cell_loop in the MatrixFree + // class will internally call this function + // with some range of cells that is + // obtained by checking which cells are + // possible to work on simultaneously so + // that write operations do not cause any + // race condition. Note that the total + // range of cells as visible in this class + // is usually not equal to the number of + // (active) cells in the triangulation. In + // fact, "cell" may be the wrong term to + // begin with, since it is rather a + // collection of quadrature points from + // several cells, and the MatrixFree class + // groups the quadrature points of several + // cells into one block to enable a higher + // degree of vectorization. The number of + // such "cells" is stored in MatrixFree and + // can be queried through + // MatrixFree::get_size_info().n_macro_cells. Compared + // to the deal.II cell iterators, in this + // class all cells are laid out in a plain + // array with no direct knowledge of level + // or neighborship relations, which makes + // it possible to index the cells by + // unsigned integers. + // + // The implementation of the Laplace + // operator is quite simple: First, we need + // to create an object FEEvaluation that + // contains the computational kernels and + // has data fields to store temporary + // results (e.g. gradients evaluated on all + // quadrature points on a collection of a + // few cells). Note that temporary results + // do not use a lot of memory, and since we + // specify template arguments with the + // element order, the data is stored on the + // stack (without expensive memory + // allocation). Usually, one only needs to + // set two template arguments, the + // dimension as first argument and the + // degree of the finite element as + // the second argument (this is equal to + // the number of degrees of freedom per + // dimension minus one for FE_Q + // elements). However, here we also want to + // be able to use float numbers for the + // multigrid preconditioner, which is the + // last (fifth) template + // argument. Therefore, we cannot rely on + // the default template arguments and must + // also fill the third and fourth field, + // consequently. The third argument + // specifies the number of quadrature + // points per direction and has a default + // value equal to the degree of the element + // plus one. The fourth argument sets + // the number of components (one can also + // evaluate vector-valued functions in + // systems of PDEs, but the default is a + // scalar element), and finally the last + // argument sets the number type. + // + // Next, we loop over the given cell range and + // then we continue with the actual + // implementation: + //
    + //
  1. Tell the FEEvaluation object the + // (macro) cell we want to work on. + //
  2. Read in the values of the + // source vectors (@p read_dof_values), + // including the resolution of + // constraints. This stores + // $u_\mathrm{cell}$ as described in the + // introduction. + //
  3. Compute the unit-cell gradient + // (the evaluation of finite element + // functions). Since FEEvaluation can + // combine value computations with + // gradient computations, it uses a + // unified interface to all kinds of + // derivatives of order between zero and + // two. We only want gradients, no values + // and no second derivatives, so we set + // the function arguments to true in the + // gradient slot (second slot), and to + // false in the values slot (first slot) + // and Hessian slot (third slot). Note + // that the FEEvaluation class internally + // evaluates shape functions in an + // efficient way where one dimension is + // worked on at a time (using the tensor + // product form of shape functions and + // quadrature points as mentioned in the + // introduction). This gives complexity + // equal to $\mathcal O(d^2 (p+1)^{d+1})$ + // for polynomial degree $p$ in $d$ + // dimensions, compared to the naive + // approach with loops over all local + // degrees of freedom and quadrature + // points that is used in FEValues that + // costs $\mathcal O(d (p+1)^{2d})$. + //
  4. Next comes the application of the + // Jacobian transformation, the + // multiplication by the variable + // coefficient and the quadrature + // weight. FEEvaluation has an access + // function @p get_gradient that applies + // the Jacobian and returns the gradient + // in real space. Then, we just need to + // multiply by the (scalar) coefficient, + // and let the function @p + // submit_gradient apply the second + // Jacobian (for the test function) and + // the quadrature weight and Jacobian + // determinant (JxW). Note that the + // submitted gradient is stored in the + // same data field as where it is read + // from in @p get_gradient. Therefore, + // you need to make sure to not read from + // the same quadrature point again after + // having called @p submit_gradient on + // that particular quadrature point. In + // general, it is a good idea to copy the + // result of @p get_gradient when it is + // used more often than once. + //
  5. Next follows the summation over + // quadrature points for all test + // functions that corresponds to the + // actual integration step. For the + // Laplace operator, we just multiply by + // the gradient, so we call the integrate + // function with the respective argument + // set. If you have an equation where you + // test by both the values of the test + // functions and the gradients, both + // template arguments need to be set to + // true. Calling first the integrate + // function for values and then gradients + // in a separate call leads to wrong + // results, since the second call will + // internally overwrite the results from + // the first call. Note that there is no + // function argument for the second + // derivative for integrate step. + //
  6. Eventually, the local + // contributions in the vector + // $v_\mathrm{cell}$ as mentioned in the + // introduction need to be added into the + // result vector (and constraints are + // applied). This is done with a call to + // @p distribute_local_to_global, the + // same name as the corresponding + // function in the ConstraintMatrix (only + // that we now store the local vector in + // the FEEvaluation object, as are the + // indices between local and global + // degrees of freedom).
+ template + void + LaplaceOperator:: + local_apply (const MatrixFree &data, + Vector &dst, + const Vector &src, + const std::pair &cell_range) const + { + FEEvaluation phi (data); + AssertDimension (coefficient.size(), + data.get_size_info().n_macro_cells * phi.n_q_points); - // The final function in this group - // takes the content of a rank-2 - // tensor and writes it to the field - // @p transformation of - // this class. We save the upper part - // of the symmetric tensor row-wise: - // we first take the (0,0)-entry, - // then the (0,1)-entry, and so - // on. We only implement this for - // dimensions two and three, which - // for the moment should do just - // fine: -template -LaplaceOperator& -LaplaceOperator::operator=(const Tensor<2,dim> &tensor) -{ - if (dim == 2) - { - transformation[0] = tensor[0][0]; - transformation[1] = tensor[0][1]; - transformation[2] = tensor[1][1]; - Assert (std::fabs(tensor[1][0]-tensor[0][1])<1e-15, - ExcInternalError()); - } - else if (dim == 3) - { - transformation[0] = tensor[0][0]; - transformation[1] = tensor[0][1]; - transformation[2] = tensor[0][2]; - transformation[3] = tensor[1][1]; - transformation[4] = tensor[1][2]; - transformation[5] = tensor[2][2]; - Assert (std::fabs(tensor[1][0]-tensor[0][1])<1e-15, - ExcInternalError()); - Assert (std::fabs(tensor[2][0]-tensor[0][2])<1e-15, - ExcInternalError()); - Assert (std::fabs(tensor[2][1]-tensor[1][2])<1e-15, - ExcInternalError()); - } - else - AssertThrow(false, ExcNotImplemented()); - return *this; -} + for (unsigned int cell=cell_range.first; cell + void + LaplaceOperator::vmult (Vector &dst, + const Vector &src) const + { + dst = 0; + vmult_add (dst, src); + } -template -unsigned int -LaplaceOperator::memory_consumption () const -{ - return sizeof(*this); -} + template + void + LaplaceOperator::Tvmult (Vector &dst, + const Vector &src) const + { + dst = 0; + vmult_add (dst,src); + } - // @sect3{LaplaceProblem class} - // This class is based on the same - // class in step-16. However, we - // replaced the SparseMatrix - // class by our matrix-free - // implementation, which means that - // we can also skip the sparsity - // patterns. -template -class LaplaceProblem -{ - public: - LaplaceProblem (const unsigned int degree); - void run (); - - private: - void setup_system (); - void assemble_system (); - void assemble_multigrid (); - void solve (); - void output_results (const unsigned int cycle) const; - - Triangulation triangulation; - FE_Q fe; - MGDoFHandler mg_dof_handler; - - MatrixFree > system_matrix; - typedef MatrixFree > MatrixFreeType; - MGLevelObject mg_matrices; - FullMatrix coarse_matrix; - - Vector solution; - Vector system_rhs; -}; - - - -template -LaplaceProblem::LaplaceProblem (const unsigned int degree) - : - fe (degree), - mg_dof_handler (triangulation) -{} - - - - // @sect4{LaplaceProblem::setup_system} - - // This is the function of step-16 with - // relevant changes due to the MatrixFree - // class. What we need to do is to somehow - // create a local gradient matrix that does - // not contain any cell-related data - // (gradient on the reference cell). The - // way to get to this matrix is to create - // an FEValues object with gradient - // information on a cell that corresponds - // to the reference cell, which is a cube - // with side length 1. So we create a - // pseudo triangulation, initialize the - // FEValues to the only cell of that - // triangulation, and read off the - // gradients (which we put in a - // FullMatrix). That full matrix is then - // passed to the reinit function of the - // MatrixFree class used as a system matrix - // and, further down, as multigrid matrices - // on the individual levels. We need to - // implement Dirichlet boundary conditions - // here, which is done with the - // ConstraintMatrix function as shown, - // e.g., in step-22. -template -void LaplaceProblem::setup_system () -{ - system_matrix.clear(); - mg_matrices.clear(); - - mg_dof_handler.distribute_dofs (fe); - - std::cout << "Number of degrees of freedom: " - << mg_dof_handler.n_dofs() - << std::endl; - - const unsigned int nlevels = triangulation.n_levels(); - mg_matrices.resize(0, nlevels-1); - - QGauss quadrature_formula(fe.degree+1); - FEValues fe_values_reference (fe, quadrature_formula, - update_gradients); - Triangulation reference_cell; - GridGenerator::hyper_cube (reference_cell, 0, 1); - fe_values_reference.reinit (reference_cell.begin()); - FullMatrix ref_cell_gradients (fe.dofs_per_cell, - quadrature_formula.size()*dim); - for (unsigned int i=0; i(), - system_matrix.get_constraints()); - system_matrix.get_constraints().close(); - std::cout.precision(4); - std::cout << "System matrix memory consumption: " - << system_matrix.memory_consumption()/double(1<<20) - << " MiB." - << std::endl; - - solution.reinit (mg_dof_handler.n_dofs()); - system_rhs.reinit (mg_dof_handler.n_dofs()); - - // Next, initialize the matrices for the - // multigrid method on all the - // levels. Unfortunately, the function - // MGTools::make_boundary_list cannot write - // Dirichlet boundary conditions into a - // ConstraintMatrix object directly, so we - // first have to make the boundary list and - // then manually fill the boundary - // conditions using the command - // ConstraintMatrix::add_line. Once this is - // done, we close the ConstraintMatrix so - // it can be used for matrix-vector - // products. - typename FunctionMap::type dirichlet_boundary; - ZeroFunction homogeneous_dirichlet_bc (1); - dirichlet_boundary[0] = &homogeneous_dirichlet_bc; - std::vector > boundary_indices(triangulation.n_levels()); - MGTools::make_boundary_list (mg_dof_handler, - dirichlet_boundary, - boundary_indices); - for (unsigned int level=0;level::iterator bc_it = boundary_indices[level].begin(); - for ( ; bc_it != boundary_indices[level].end(); ++bc_it) - mg_matrices[level].get_constraints().add_line(*bc_it); - mg_matrices[level].get_constraints().close(); - } - coarse_matrix.reinit (mg_dof_handler.n_dofs(0), - mg_dof_handler.n_dofs(0)); -} + template + void + LaplaceOperator::Tvmult_add (Vector &dst, + const Vector &src) const + { + vmult_add (dst,src); + } + + + + // This function implements the loop over all + // cells. This is done with the @p cell_loop + // of the MatrixFree class, which takes + // the operator() of this class with arguments + // MatrixFree, OutVector, InVector, + // cell_range. Note that we could also use a + // simple function as local operation in case + // we had constant coefficients (all we need + // then is the MatrixFree, the vectors and + // the cell range), but since the coefficient + // is stored in a variable of this class, we + // cannot use that variant here. The cell loop + // is automatically performed on several threads + // if multithreading is enabled (this class + // uses a quite elaborate algorithm to work on + // cells that do not share any degrees of + // freedom that could possibly give rise to + // race conditions, using the dynamic task + // scheduler of the Intel Threading Building + // Blocks). + // + // After the cell loop, we need to touch + // the constrained degrees of freedom: + // Since the assembly loop automatically + // resolves constraints (just as the + // ConstraintMatrix::distribute_local_to_global + // call does), it does not compute any + // contribution for constrained degrees of + // freedom. In other words, the entries for + // constrained DoFs remain zero after the + // first part of this function, as if the + // matrix had empty rows and columns for + // constrained degrees of freedom. On the + // other hand, iterative solvers like CG + // only work for non-singular matrices, so + // we have to modify the operation on + // constrained DoFs. The easiest way to do + // that is to pretend that the sub-block of + // the matrix that corresponds to + // constrained DoFs is the identity matrix, + // in which case application of the matrix + // would simply copy the elements of the + // right hand side vector into the left + // hand side. In general, however, one + // needs to make sure that the diagonal + // entries of this sub-block are of the + // same order of magnitude as the diagonal + // elements of the rest of the matrix. + // Here, the domain extent is of unit size, + // so we can simply choose unit size. If we + // had domains that are far away from unit + // size, we would need to choose a number + // that is close to the size of other + // diagonal matrix entries, so that these + // artificial eigenvalues do not change the + // eigenvalue spectrum (and make + // convergence with CG more difficult). + template + void + LaplaceOperator::vmult_add (Vector &dst, + const Vector &src) const + { + data.cell_loop (&LaplaceOperator::local_apply, this, dst, src); + + const std::vector & + constrained_dofs = data.get_constrained_dofs(); + for (unsigned int i=0; i + number + LaplaceOperator::el (const unsigned int row, + const unsigned int col) const + { + Assert (row == col, ExcNotImplemented()); + Assert (diagonal_is_available == true, ExcNotInitialized()); + return diagonal_values(row); + } + + + + // Regarding the calculation of the + // diagonal, we expect the user to + // provide a vector with the + // diagonal entries (and we will + // compute them in the code + // below). We only need it for the + // level matrices of multigrid, not + // the system matrix (since we only + // need these diagonals for the + // multigrid smoother). Since we + // fill only elements into + // unconstrained entries, we have + // to set constrained entries to + // one in order to avoid the same + // problems as discussed above. + template + void + LaplaceOperator::set_diagonal(const Vector&diagonal) + { + AssertDimension (m(), diagonal.size()); + + diagonal_values = diagonal; + + const std::vector & + constrained_dofs = data.get_constrained_dofs(); + for (unsigned int i=0; i + std::size_t + LaplaceOperator::memory_consumption () const + { + return (data.memory_consumption () + + coefficient.memory_consumption() + + diagonal_values.memory_consumption() + + MemoryConsumption::memory_consumption(diagonal_is_available)); + } + + + + // @sect3{LaplaceProblem class} + + // This class is based on the one in + // step-16. However, we replaced the + // SparseMatrix class by our + // matrix-free implementation, which means + // that we can also skip the sparsity + // patterns. Notice that we define the + // LaplaceOperator class with the degree of finite + // element as template argument (the value is + // defined at the top of the file), and that + // we use float numbers for the multigrid + // level matrices. + // + // The class also has a member variable to + // keep track of all the time we spend on + // setting up the entire chain of data + // before we actually go about solving the + // problem. In addition, there is an output + // stream (that is disabled by default) + // that can be used to output details for + // the individual setup operations instead + // of the summary only that is printed out + // by default. + template + class LaplaceProblem + { + public: + LaplaceProblem (); + void run (); + + private: + void setup_system (); + void assemble_system (); + void assemble_multigrid (); + void solve (); + void output_results (const unsigned int cycle) const; + + typedef LaplaceOperator SystemMatrixType; + typedef LaplaceOperator LevelMatrixType; + + Triangulation triangulation; + FE_Q fe; + MGDoFHandler mg_dof_handler; + ConstraintMatrix constraints; + + SystemMatrixType system_matrix; + MGLevelObject mg_matrices; + FullMatrix coarse_matrix; + MGLevelObject mg_constraints; + + Vector solution; + Vector system_rhs; + + double setup_time; + ConditionalOStream time_details; + }; - // @sect4{LaplaceProblem::assemble_system} - - // The assemble function is significantly - // reduced compared to step-16. All we need - // to do is to assemble the right hand side - // and to calculate the cell-dependent part - // of the Laplace operator. The first task is - // standard. The second is also not too hard - // given the discussion in the introduction: - // We need to take the inverse of the - // Jacobian of the transformation from unit - // to real cell, multiply it with its - // transpose and multiply the resulting - // rank-2 tensor with the quadrature weights - // and the coefficient values at the - // quadrature points. To make this work, we - // add the update flag @p - // update_inverse_jacobians to the FEValues - // constructor, and query the inverse of the - // Jacobian in a loop over the quadrature - // points (note that the Jacobian is not - // related to any kind of degrees of freedom - // directly). In the end, we condense the - // constraints from Dirichlet boundary - // conditions away from the right hand side. -template -void LaplaceProblem::assemble_system () -{ - QGauss quadrature_formula(fe.degree+1); - MappingQ mapping (fe.degree); - FEValues fe_values (mapping, fe, quadrature_formula, - update_values | update_inverse_jacobians | - update_quadrature_points | update_JxW_values); + // When we initialize the finite element, we + // of course have to use the degree specified + // at the top of the file as well (otherwise, + // an exception will be thrown at some point, + // since the computational kernel defined in + // the templated LaplaceOperator class and the + // information from the finite element read + // out by MatrixFree will not match). + template + LaplaceProblem::LaplaceProblem () + : + fe (degree_finite_element), + mg_dof_handler (triangulation), + time_details (std::cout, false) + {} - const unsigned int dofs_per_cell = fe.dofs_per_cell; - const unsigned int n_q_points = quadrature_formula.size(); - std::vector local_dof_indices (dofs_per_cell); - const Coefficient coefficient; - std::vector coefficient_values (n_q_points); - unsigned int cell_no = 0; + // @sect4{LaplaceProblem::setup_system} + + // This is the function of step-16 with + // relevant changes due to the LaplaceOperator + // class. We do not use adaptive grids, so we + // do not have to compute edge matrices. Thus, + // all we do is to implement Dirichlet + // boundary conditions through the + // ConstraintMatrix, set up the + // (one-dimensional) quadrature that should + // be used by the matrix-free class, and call + // the initialization functions. + // + // In the process, we output data on both + // the run time of the program as well as + // on memory consumption, where we output + // memory data in megabytes (1 million + // bytes). + template + void LaplaceProblem::setup_system () + { + Timer time; + time.start (); + setup_time = 0; + + system_matrix.clear(); + mg_matrices.clear(); + mg_constraints.clear(); + + mg_dof_handler.distribute_dofs (fe); + + std::cout << "Number of degrees of freedom: " + << mg_dof_handler.n_dofs() + << std::endl; + + constraints.clear(); + VectorTools::interpolate_boundary_values (mg_dof_handler, + 0, + ZeroFunction(), + constraints); + constraints.close(); + setup_time += time.wall_time(); + time_details << "Distribute DoFs & B.C. (CPU/wall) " + << time() << "s/" << time.wall_time() << "s" << std::endl; + time.restart(); + + system_matrix.reinit (mg_dof_handler, constraints); + std::cout.precision(4); + std::cout << "System matrix memory consumption: " + << system_matrix.memory_consumption()*1e-6 + << " MB." + << std::endl; + + solution.reinit (mg_dof_handler.n_dofs()); + system_rhs.reinit (mg_dof_handler.n_dofs()); + + setup_time += time.wall_time(); + time_details << "Setup matrix-free system (CPU/wall) " + << time() << "s/" << time.wall_time() << "s" << std::endl; + time.restart(); + + // Next, initialize the matrices + // for the multigrid method on + // all the levels. The function + // MGTools::make_boundary_list + // returns for each multigrid + // level which degrees of freedom + // are located on a Dirichlet + // boundary; we force these DoFs + // to have value zero by adding + // to the ConstraintMatrix object + // a zero condition by using the + // command + // ConstraintMatrix::add_line. Once + // this is done, we close the + // ConstraintMatrix on each level + // so it can be used to read out + // indices internally in the + // MatrixFree. + const unsigned int nlevels = triangulation.n_levels(); + mg_matrices.resize(0, nlevels-1); + mg_constraints.resize (0, nlevels-1); + + typename FunctionMap::type dirichlet_boundary; + ZeroFunction homogeneous_dirichlet_bc (1); + dirichlet_boundary[0] = &homogeneous_dirichlet_bc; + std::vector > boundary_indices(triangulation.n_levels()); + MGTools::make_boundary_list (mg_dof_handler, + dirichlet_boundary, + boundary_indices); + for (unsigned int level=0;level::iterator bc_it = boundary_indices[level].begin(); + for ( ; bc_it != boundary_indices[level].end(); ++bc_it) + mg_constraints[level].add_line(*bc_it); + + mg_constraints[level].close(); + mg_matrices[level].reinit(mg_dof_handler, + mg_constraints[level], + level); + } + coarse_matrix.reinit (mg_dof_handler.n_dofs(0), + mg_dof_handler.n_dofs(0)); + setup_time += time.wall_time(); + time_details << "Setup matrix-free levels (CPU/wall) " + << time() << "s/" << time.wall_time() << "s" << std::endl; + } + + + + // @sect4{LaplaceProblem::assemble_system} + + // The assemble function is significantly + // reduced compared to step-16. All we need + // to do is to assemble the right hand + // side. That is the same as in many other + // tutorial programs. In the end, we condense + // the constraints from Dirichlet boundary + // conditions away from the right hand side. + template + void LaplaceProblem::assemble_system () + { + Timer time; + QGauss quadrature_formula(fe.degree+1); + FEValues fe_values (fe, quadrature_formula, + update_values | update_JxW_values); - typename DoFHandler::active_cell_iterator cell = mg_dof_handler.begin_active(), - endc = mg_dof_handler.end(); - for (; cell!=endc; ++cell, ++cell_no) - { - cell->get_dof_indices (local_dof_indices); - fe_values.reinit (cell); - coefficient.value_list (fe_values.get_quadrature_points(), - coefficient_values); - - for (unsigned int i=0; i local_dof_indices (dofs_per_cell); + const Coefficient coefficient; + std::vector coefficient_values (n_q_points); - // @sect4{LaplaceProblem::assemble_multigrid} - - // Here is another assemble - // function. The integration core is - // the same as above. Only the loop - // goes over all existing cells now - // and the results must be entered - // into the correct matrix. - - // Since we only do multilevel - // preconditioning, no right-hand side is - // assembled here. Compared to step-16, there - // is one new thing here: we manually - // calculate the matrix on the coarsest - // level. In step-16, we could simply copy - // the entries from the respective sparse - // matrix, but this is obviously not possible - // here. We could have integrated this into the - // MatrixFree class as well, but it is simple - // enough, so calculate it here instead. -template -void LaplaceProblem::assemble_multigrid () -{ - coarse_matrix = 0; - QGauss quadrature_formula(fe.degree+1); - MappingQ mapping (fe.degree); - FEValues fe_values (mapping, fe, quadrature_formula, - update_gradients | update_inverse_jacobians | - update_quadrature_points | update_JxW_values); - - const unsigned int dofs_per_cell = fe.dofs_per_cell; - const unsigned int n_q_points = quadrature_formula.size(); - - std::vector local_dof_indices (dofs_per_cell); - const Coefficient coefficient; - std::vector coefficient_values (n_q_points); - - std::vector cell_no(triangulation.n_levels()); - typename MGDoFHandler::cell_iterator cell = mg_dof_handler.begin(), - endc = mg_dof_handler.end(); - for (; cell!=endc; ++cell) - { - const unsigned int level = cell->level(); - cell->get_mg_dof_indices (local_dof_indices); - fe_values.reinit (cell); - coefficient.value_list (fe_values.get_quadrature_points(), - coefficient_values); - - mg_matrices[level].set_local_dof_indices (cell_no[level], - local_dof_indices); - for (unsigned int q=0; q::active_cell_iterator cell = mg_dof_handler.begin_active(), + endc = mg_dof_handler.end(); + for (; cell!=endc; ++cell) + { + cell->get_dof_indices (local_dof_indices); + fe_values.reinit (cell); + for (unsigned int i=0; i + void LaplaceProblem::assemble_multigrid () + { + Timer time; + coarse_matrix = 0; + QGauss quadrature_formula(fe.degree+1); + FEValues fe_values (fe, quadrature_formula, + update_gradients | update_inverse_jacobians | + update_quadrature_points | update_JxW_values); + + const unsigned int dofs_per_cell = fe.dofs_per_cell; + const unsigned int n_q_points = quadrature_formula.size(); + + std::vector local_dof_indices (dofs_per_cell); + const Coefficient coefficient; + std::vector coefficient_values (n_q_points); + FullMatrix local_matrix (dofs_per_cell, dofs_per_cell); + Vector local_diagonal (dofs_per_cell); + + const unsigned int n_levels = triangulation.n_levels(); + std::vector > diagonals (n_levels); + for (unsigned int level=0; level cell_no(triangulation.n_levels()); + typename MGDoFHandler::cell_iterator cell = mg_dof_handler.begin(), + endc = mg_dof_handler.end(); + for (; cell!=endc; ++cell) + { + const unsigned int level = cell->level(); + cell->get_mg_dof_indices (local_dof_indices); + fe_values.reinit (cell); + coefficient.value_list (fe_values.get_quadrature_points(), + coefficient_values); - // In a final step, we need to - // condense the boundary conditions - // on the coarse matrix. There is - // no built-in function for doing - // this on a full matrix, so - // manually delete the rows and - // columns of the matrix that are - // constrained. - for (unsigned int i=0; i -void LaplaceProblem::solve () -{ - GrowingVectorMemory<> vector_memory; - - MGTransferPrebuilt > mg_transfer; - mg_transfer.build_matrices(mg_dof_handler); - - MGCoarseGridHouseholder > mg_coarse; - mg_coarse.initialize(coarse_matrix); - - typedef PreconditionChebyshev > SMOOTHER; - MGSmootherPrecondition > - mg_smoother(vector_memory); - - // Then, we initialize the smoother - // with our level matrices and the - // required, additional data for - // the Chebyshev smoother. In - // particular, we use a higher - // polynomial degree for higher - // order elements, since smoothing - // gets more difficult for - // these. Smooth out a range of - // $[\lambda_{\max}/10,\lambda_{\max}]$. In - // order to compute the maximum - // eigenvalue of the corresponding - // matrix, the Chebyshev - // initializations performs a few - // steps of a CG algorithm. Since - // all we need is a rough estimate, - // we choose some eight iterations - // (more if the finite element - // polynomial degree is larger, - // less if it is smaller than - // quadratic). - typename SMOOTHER::AdditionalData smoother_data; - smoother_data.smoothing_range = 10.; - smoother_data.degree = fe.degree; - smoother_data.eig_cg_n_iterations = 4+2*fe.degree; - mg_smoother.initialize(mg_matrices, smoother_data); - - MGMatrix > - mg_matrix(&mg_matrices); - - Multigrid > mg(mg_dof_handler, - mg_matrix, - mg_coarse, - mg_transfer, - mg_smoother, - mg_smoother); - PreconditionMG, - MGTransferPrebuilt > > - preconditioner(mg_dof_handler, mg, mg_transfer); - - // Finally, write out the memory - // consumption of the Multigrid object - // (or rather, of its most significant - // components, since there is no built-in - // function for the total multigrid - // object), then create the solver object - // and solve the system. This is very - // easy, and we didn't even see any - // difference in the solve process - // compared to step-16. The magic is all - // hidden behind the implementation of - // the MatrixFree::vmult operation. - const unsigned int multigrid_memory - = (mg_matrices.memory_consumption() + - mg_transfer.memory_consumption() + - coarse_matrix.memory_consumption()); - std::cout << "Multigrid objects memory consumption: " - << multigrid_memory/double(1<<20) - << " MiB." - << std::endl; - - SolverControl solver_control (1000, 1e-12); - SolverCG<> cg (solver_control); - - cg.solve (system_matrix, solution, system_rhs, - preconditioner); - - std::cout << "Convergence in " << solver_control.last_step() - << " CG iterations." << std::endl; -} + // @sect4{LaplaceProblem::solve} - // @sect4{LaplaceProblem::output_results} + // The solution process again looks like + // step-16. We now use a Chebyshev smoother + // instead of SOR (SOR would be very + // difficult to implement because we do not + // have the matrix elements available + // explicitly, and it is difficult to make it + // work efficiently in %parallel). The + // multigrid classes provide a simple + // interface for using the Chebyshev smoother + // which is defined in a preconditioner + // class: MGSmootherPrecondition. + template + void LaplaceProblem::solve () + { + Timer time; + GrowingVectorMemory<> vector_memory; + + MGTransferPrebuilt > mg_transfer; + mg_transfer.build_matrices(mg_dof_handler); + setup_time += time.wall_time(); + time_details << "MG build transfer time (CPU/wall) " << time() + << "s/" << time.wall_time() << "s\n"; + time.restart(); + + MGCoarseGridHouseholder > mg_coarse; + mg_coarse.initialize(coarse_matrix); + setup_time += time.wall_time(); + time_details << "MG coarse time (CPU/wall) " << time() + << "s/" << time.wall_time() << "s\n"; + time.restart(); + + typedef PreconditionChebyshev > SMOOTHER; + MGSmootherPrecondition > + mg_smoother(vector_memory); + + // Then, we initialize the smoother with + // our level matrices and the mandatory + // additional data for the Chebyshev + // smoother. We use quite a high degree + // here (6), since matrix-vector products + // are comparably cheap and more parallel + // than the level-transfer operations. We + // choose to smooth out a range of $[1.2 + // \hat{\lambda}_{\max}/10,1.2 + // \hat{\lambda}_{\max}]$ in the smoother + // where $\hat{\lambda}_{\max}$ is an + // estimate of the largest eigenvalue. In + // order to compute that eigenvalue, the + // Chebyshev initializations performs a + // few steps of a CG algorithm without + // preconditioner. Since the highest + // eigenvalue is usually the easiest one + // to find and a rough estimate is enough, + // we choose 10 iterations. + typename SMOOTHER::AdditionalData smoother_data; + smoother_data.smoothing_range = 10.; + smoother_data.degree = 6; + smoother_data.eig_cg_n_iterations = 10; + mg_smoother.initialize(mg_matrices, smoother_data); + + MGMatrix > + mg_matrix(&mg_matrices); + + Multigrid > mg(mg_dof_handler, + mg_matrix, + mg_coarse, + mg_transfer, + mg_smoother, + mg_smoother); + PreconditionMG, + MGTransferPrebuilt > > + preconditioner(mg_dof_handler, mg, mg_transfer); + + // Finally, write out the memory + // consumption of the Multigrid object + // (or rather, of its most significant + // components, since there is no built-in + // function for the total multigrid + // object), then create the solver object + // and solve the system. This is very + // easy, and we didn't even see any + // difference in the solve process + // compared to step-16. The magic is all + // hidden behind the implementation of + // the LaplaceOperator::vmult + // operation. Note that we print out the + // solve time and the accumulated setup + // time through standard out, i.e., in + // any case, whereas detailed times for + // the setup operations are only printed + // in case the flag for detail_times in + // the constructor is changed. + const std::size_t multigrid_memory + = (mg_matrices.memory_consumption() + + mg_transfer.memory_consumption() + + coarse_matrix.memory_consumption()); + std::cout << "Multigrid objects memory consumption: " + << multigrid_memory * 1e-6 + << " MB." + << std::endl; + + SolverControl solver_control (1000, 1e-12*system_rhs.l2_norm()); + SolverCG<> cg (solver_control); + setup_time += time.wall_time(); + time_details << "MG build smoother time (CPU/wall) " << time() + << "s/" << time.wall_time() << "s\n"; + std::cout << "Total setup time (wall) " << setup_time + << "s\n"; + + time.reset(); + time.start(); + cg.solve (system_matrix, solution, system_rhs, + preconditioner); + + + std::cout << "Time solve (" + << solver_control.last_step() + << " iterations) (CPU/wall) " << time() << "s/" + << time.wall_time() << "s\n"; + } + + + + // @sect4{LaplaceProblem::output_results} + + // Here is the data output, which is a + // simplified version of step-5. We use the + // standard VTU (= compressed VTK) output for + // each grid produced in the refinement + // process. + template + void LaplaceProblem::output_results (const unsigned int cycle) const + { + DataOut data_out; - // Here is the data output, which is a - // simplified version of step-5. We use the - // standard VTK output for each grid - // produced in the refinement process. -template -void LaplaceProblem::output_results (const unsigned int cycle) const -{ - DataOut data_out; + data_out.attach_dof_handler (mg_dof_handler); + data_out.add_data_vector (solution, "solution"); + data_out.build_patches (); - data_out.attach_dof_handler (mg_dof_handler); - data_out.add_data_vector (solution, "solution"); - data_out.build_patches (); + std::ostringstream filename; + filename << "solution-" + << cycle + << ".vtu"; - std::ostringstream filename; - filename << "solution-" - << cycle - << ".vtk"; + std::ofstream output (filename.str().c_str()); + data_out.write_vtu (output); + } - std::ofstream output (filename.str().c_str()); - data_out.write_vtk (output); -} + // @sect4{LaplaceProblem::run} - // @sect4{LaplaceProblem::run} + // The function that runs the program is + // very similar to the one in step-16. We + // make less refinement steps in 3D + // compared to 2D, but that's it. + template + void LaplaceProblem::run () + { + for (unsigned int cycle=0; cycle<9-dim; ++cycle) + { + std::cout << "Cycle " << cycle << std::endl; - // The function that runs the program is - // very similar to the one in step-16. We - // make less refinement steps in 3D - // compared to 2D, but that's it. -template -void LaplaceProblem::run () -{ - for (unsigned int cycle=0; cycle<8-dim; ++cycle) - { - std::cout << "Cycle " << cycle << std::endl; - - if (cycle == 0) - { - GridGenerator::hyper_ball(triangulation); - static const HyperBallBoundary boundary; - triangulation.set_boundary (0, boundary); - triangulation.refine_global (3-dim); - } - triangulation.refine_global (1); - setup_system (); - assemble_system (); - assemble_multigrid (); - solve (); - output_results (cycle); - std::cout << std::endl; - }; + if (cycle == 0) + { + GridGenerator::hyper_cube (triangulation, 0., 1.); + triangulation.refine_global (3-dim); + } + triangulation.refine_global (1); + setup_system (); + assemble_system (); + assemble_multigrid (); + solve (); + output_results (cycle); + std::cout << std::endl; + }; + } } // @sect3{The main function} - // This is as in all other programs: -int main () + // This is as in most other programs. +int main (int argc, char** argv) { try { - deallog.depth_console (0); - LaplaceProblem<2> laplace_problem (2); + using namespace Step37; + + deallog.depth_console(0); + LaplaceProblem laplace_problem; laplace_problem.run (); } catch (std::exception &exc) diff --git a/deal.II/examples/step-48/Makefile b/deal.II/examples/step-48/Makefile new file mode 100644 index 0000000000..f7cde30d6d --- /dev/null +++ b/deal.II/examples/step-48/Makefile @@ -0,0 +1,143 @@ +# $Id$ + + +# For the small projects Makefile, you basically need to fill in only +# four fields. +# +# The first is the name of the application. It is assumed that the +# application name is the same as the base file name of the single C++ +# file from which the application is generated. +target = $(basename $(shell echo step-*.cc)) + +# The second field determines whether you want to run your program in +# debug or optimized mode. The latter is significantly faster, but no +# run-time checking of parameters and internal states is performed, so +# you should set this value to `on' while you develop your program, +# and to `off' when running production computations. +debug-mode = on + + +# As third field, we need to give the path to the top-level deal.II +# directory. You need to adjust this to your needs. Since this path is +# probably the most often needed one in the Makefile internals, it is +# designated by a single-character variable, since that can be +# reference using $D only, i.e. without the parentheses that are +# required for most other parameters, as e.g. in $(target). +D = ../../ + + +# The last field specifies the names of data and other files that +# shall be deleted when calling `make clean'. Object and backup files, +# executables and the like are removed anyway. Here, we give a list of +# files in the various output formats that deal.II supports. +clean-up-files = *gmv *gnuplot *gpl *eps *pov *vtk *vtu *ucd *.d2 + + + + +# +# +# Usually, you will not need to change anything beyond this point. +# +# +# The next statement tells the `make' program where to find the +# deal.II top level directory and to include the file with the global +# settings +include $D/common/Make.global_options + + +# Since the whole project consists of only one file, we need not +# consider difficult dependencies. We only have to declare the +# libraries which we want to link to the object file. deal.II has two +# libraries: one for the debug mode version of the +# application and one for optimized mode. +libs.g := $(lib-deal2.g) +libs.o := $(lib-deal2.o) + + +# We now use the variable defined above to switch between debug and +# optimized mode to select the set of libraries to link with. Included +# in the list of libraries is the name of the object file which we +# will produce from the single C++ file. Note that by default we use +# the extension .g.o for object files compiled in debug mode and .o for +# object files in optimized mode (or whatever local default on your +# system is instead of .o) +ifeq ($(debug-mode),on) + libraries = $(target).g.$(OBJEXT) $(libs.g) +else + libraries = $(target).$(OBJEXT) $(libs.o) +endif + + +# Now comes the first production rule: how to link the single object +# file produced from the single C++ file into the executable. Since +# this is the first rule in the Makefile, it is the one `make' selects +# if you call it without arguments. +$(target)$(EXEEXT) : $(libraries) + @echo ============================ Linking $@ + @$(CXX) -o $@ $^ $(LIBS) $(LDFLAGS) + + +# To make running the application somewhat independent of the actual +# program name, we usually declare a rule `run' which simply runs the +# program. You can then run it by typing `make run'. This is also +# useful if you want to call the executable with arguments which do +# not change frequently. You may then want to add them to the +# following rule: +run: $(target)$(EXEEXT) + @echo ============================ Running $< + @./$(target)$(EXEEXT) + + +# As a last rule to the `make' program, we define what to do when +# cleaning up a directory. This usually involves deleting object files +# and other automatically created files such as the executable itself, +# backup files, and data files. Since the latter are not usually quite +# diverse, you needed to declare them at the top of this file. +clean: + -rm -f *.$(OBJEXT) *~ Makefile.dep $(target)$(EXEEXT) $(clean-up-files) + + +# Since we have not yet stated how to make an object file from a C++ +# file, we should do so now. Since the many flags passed to the +# compiler are usually not of much interest, we suppress the actual +# command line using the `at' sign in the first column of the rules +# and write the string indicating what we do instead. +./%.g.$(OBJEXT) : + @echo "==============debug========= $( $@" + @$(CXX) -w $(CXXFLAGS.g) -c $< -o $@ +./%.$(OBJEXT) : + @echo "==============optimized===== $( $@" + @$(CXX) $(CXXFLAGS.o) -c $< -o $@ + + +# The following statement tells make that the rules `run' and `clean' +# are not expected to produce files of the same name as Makefile rules +# usually do. +.PHONY: run clean + + +# Finally there is a rule which you normally need not care much about: +# since the executable depends on some include files from the library, +# besides the C++ application file of course, it is necessary to +# re-generate the executable when one of the files it depends on has +# changed. The following rule creates a dependency file +# `Makefile.dep', which `make' uses to determine when to regenerate +# the executable. This file is automagically remade whenever needed, +# i.e. whenever one of the cc-/h-files changed. Make detects whether +# to remake this file upon inclusion at the bottom of this file. +# +# If the creation of Makefile.dep fails, blow it away and fail +Makefile.dep: $(target).cc Makefile \ + $(shell echo $D/include/deal.II/*/*.h) + @echo ============================ Remaking $@ + @$D/common/scripts/make_dependencies $(INCLUDE) -B. $(target).cc \ + > $@ \ + || (rm -f $@ ; false) + @if test -s $@ ; then : else rm $@ ; fi + +# To make the dependencies known to `make', we finally have to include +# them: +include Makefile.dep + + diff --git a/deal.II/examples/step-48/doc/builds-on b/deal.II/examples/step-48/doc/builds-on new file mode 100644 index 0000000000..017d95465b --- /dev/null +++ b/deal.II/examples/step-48/doc/builds-on @@ -0,0 +1 @@ +step-25 step-37 step-40 diff --git a/deal.II/examples/step-48/doc/intro.dox b/deal.II/examples/step-48/doc/intro.dox new file mode 100644 index 0000000000..3c582a5c5b --- /dev/null +++ b/deal.II/examples/step-48/doc/intro.dox @@ -0,0 +1,171 @@ + + +This program was contributed by Katharina Kormann and Martin +Kronbichler. + +The algorithm for the matrix-vector product is built upon the preprint +"A generic interface for parallel cell-based finite element +operator application" by Martin Kronbichler and Katharina +Kormann, Uppsala University, October 2011, and the paper +"Parallel finite element operator application: Graph partitioning +and coloring" by Katharina Kormann and Martin Kronbichler in: +Proceedings of the 7th IEEE International Conference on e-Science, +2011. + + +

Introduction

+ +This program demonstrates how to use the cell-based implementation of +finite element operators with the MatrixFree class, +first introduced in step-37, to solve nonlinear partial +differential equations. Moreover, we demonstrate how the +MatrixFree class handles constraints and how it can be +parallelized over distributed nodes. Finally, we will use an explicit +time-stepping method to solve the problem and introduce Gauss-Lobatto +finite elements that are very convenient in this case since they have +a diagonally, and thus trivially invertable, mass matrix. Moreover, +this type of elements clusters the nodes towards the element +boundaries which is why it has good properties for high-order +discretization methods. + +

Problem statement and discretization

+ +As an example, we choose to solve the sine-Gordon soliton equation +\f{eqnarray*} +u_{tt} &=& \Delta u -\sin(u) \quad\mbox{for}\quad (x,t) \in +\Omega \times (t_0,t_f],\\ +{\mathbf n} \cdot \nabla u &=& 0 +\quad\mbox{for}\quad (x,t) \in \partial\Omega \times (t_0,t_f],\\ +u(x,t_0) &=& u_0(x). +\f} + +that was already introduced in step-25. As a simple explicit time +integration method, we choose leap frog scheme using the second-order +formulation of the equation. Then, the scheme reads in weak form + +\f{eqnarray*} +(v,u^{n+1}) = (v,2 u^n-u^{n-1} - +(\Delta t)^2 \sin(u^n)) - (\nabla v, (\Delta t)^2 \nabla u^n), +\f} +where v denotes a test function and the index n stands for +the time step number. + +For the spatial discretization, we choose FE_Q elements +with basis functions defined to interpolate the support points of the +Gauss-Lobatto quadrature rule. Moreover, when we compute the integrals +over the basis functions to form the mass matrix and the operator on +the right hand side of the equation above, we use the +Gauss-Lobatto quadrature rule with the same support points as the +node points of the finite element to evaluate the integrals. Since the +finite element is Lagrangian, this will yield a diagonal mass matrix +on the left hand side of the equation, making the solution of the +linear system in each time step trivial. + +Using this quadrature rule, for a pth order finite element, we +use a (2p-1)th order accurate formula to evaluate the +integrals. Since the product of two pth order basis functions +when computing a mass matrix gives a function with polynomial degree +2p in each direction, the integrals are not exactly +evaluated. However, considering the fact that the interpolation order +of finite elements of degree p is p+1, the overall +convergence properties are not disturbed by the quadrature error, in +particular not when we use high orders. + +Apart from the fact, that we avoid solving linear systems with this +type of elements when using explicit time-stepping, they come with two +other advantages. When we are using the sum-factorization approach to +evaluate the finite element operator (cf. step-37), we have to +evaluate the function at the quadrature points. In the case of +Gauss-Lobatto elements, where quadrature points and node points of the +finite element coincide, this operation is trivial since the value +of the function at the quadrature points is given by its one-dimensional +coefficients. In this way, the complexity of a finite element operator +evaluation is further reduced compared to equidistant elements. + +The third advantage is the fact that these elements are better conditioned as +equidistant Lagrange polynomials for increasing order so that we can use +higher order elements for an accurate solution of the equation. Lagrange +elements FE_Q with equidistant points should not be used for polynomial +degrees four and higher. + +To sum up the discussion, by using the right finite element and +quadrature rule combination, we end up with a scheme where we in each +time step need to compute the right hand side vector corresponding +to the formulation above and then multiply it by the inverse of the +diagonal mass matrix. In practice, of course, we extract the diagonal +elements and invert them only once at the beginning of the program. + +

Implementation of constraints

+ +The usual way to handle constraints in deal.II is to use +the ConstraintMatrix class that builds a sparse matrix storing +information about which degrees of freedom (DoF) are constrained and +how they are constrained. This format uses an unnecessarily large +amount of memory since there are not so many different types of +constraints: for example, in the case of hanging nodes when using +linear finite element on every cell, constraints most have the form +$x_k = \frac 12 x_i + \frac 12 x_j$ where the coefficients $\frac 12$ +are always the same and only $i,j,k$ are different. While storing this +redundant information is not a problem in general because it is only +needed once during matrix and right hand side assembly, it becomes a +problem when we want to use the matrix-free approach since there this +information has to be accessed every time we apply the operator. Thus, +instead of a ConstraintMatrix, we use a variable that we call +constraint_pool that collects the weights of the +different constraints. Then, we only have to store an identifier of +each constraint in the mesh instead of all the weights. Moreover, we +do not want to apply the constraints in a pre- and postprocessing step +but want to take care of constraints as we evaluate the finite element +operator. Therefore, we embed the constraint information into the +variable indices_local_to_global that is used to extract +the cell information from the global vector. If a DoF is constrained, +the indices_local_to_global variable contains the global +indices of the DoFs that it is constrained to. Then, we have another +variable constraint_indicator at hand that holds, for +each cell, the local indices of DoFs that are constrained as well as +the identifer of the type of constraint. Actually, you will not see +these data structures in the example program since the class +FEEvaluationGL takes care of the constraints without user +interaction. + + +

Parallelization

+ +The MatrixFree class comes with the option to be +parallelized on three levels: MPI parallelization on clusters of +distributed nodes, thread parallelization scheduled by the Threading +Building Blocks library, and finally with a vectorization by clustering of two +(or more) cells into a SIMD data type for the operator application. We +have already seen how shared-memory parallelism can be exploited in +step-37. Here, we demonstrate MPI parallelization. + +To facilitate parallelism with distributed memory (MPI), we use a special +vector type parallel::distributed::Vector that holds the +processor-local part of the solution as well as information on and data +fields for the ghost DoFs, i.e. DoFs that are owned by a remote +processor but needed on cells that are treated by the present +processor. Moreover, it holds the MPI-send information for DoFs that +are owned locally but needed by other processors. This is similar to +the PETScWrappers::MPI::Vector and TrilinosWrappers::MPI::Vector data +types we have used in step-40 and step-32 before, but since we do not +need any other parallel functionality of these libraries, we use the +parallel::distributed::Vector class of deal.II instead of linking in +another large library. + +Note that this program is designed to be run with a distributed triangulation +(parallel::distributed::Triangulation), which requires deal.II to be +configured with p4est as described +in the deal.II ReadMe file. However, a +non-distributed triangulation is also supported, in which case the +computation will be run in serial. + +

The test case

+ +In our example, we choose the initial value to be \f{eqnarray*} u(x,t) = +\prod_{i=1}^{d} -4 \arctan \left( +\frac{m}{\sqrt{1-m^2}}\frac{\sin\left(\sqrt{1-m^2} t +c_2\right)}{\cosh(mx_i+c_1)}\right) +\f} and solve the equation over the time interval [-10,10]. The +constants are chosen to be $c_1=c_1=0$ and m=0.5. As mentioned +in step-25, in one dimension u as a function of t is the exact +solution of the sine-Gordon equation. For higher dimension, this is however +not the case. diff --git a/deal.II/examples/step-48/doc/kind b/deal.II/examples/step-48/doc/kind new file mode 100644 index 0000000000..86a44aa1ef --- /dev/null +++ b/deal.II/examples/step-48/doc/kind @@ -0,0 +1 @@ +time dependent diff --git a/deal.II/examples/step-48/doc/results.dox b/deal.II/examples/step-48/doc/results.dox new file mode 100644 index 0000000000..34e7cf6dbd --- /dev/null +++ b/deal.II/examples/step-48/doc/results.dox @@ -0,0 +1,174 @@ +

Results

+ +

Comparison with a sparse matrix

+ +In order to demonstrate the gain in using the CellFEOperator class instead of +the standard deal.II assembly routines for evaluating the +information from old time steps, we study a simple serial run of the code on a +nonadaptive mesh. Since much time is spent on evaluating the sine function, we +do not only show the numbers of the full sine-Gordon equation but also for the +wave equation (the sine-term skipped from the sine-Gordon equation). We use +both second and fourth order elements. The results are summarized in the +following table. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 wave equationsine-Gordon
 MFSpMVdealiiMFdealii
2D, $\mathcal{Q}_2$ 0.0106 0.00971 0.109 0.0243 0.124
2D, $\mathcal{Q}_4$ 0.0328 0.0706 0.528 0.0714 0.502
3D, $\mathcal{Q}_2$ 0.0151 0.0320 0.331 0.0376 0.364
3D, $\mathcal{Q}_4$ 0.0918 0.844 6.83 0.194 6.95
+ +It is apparent that the CellFEOperator outperforms the standard assembly +routines in deal.II by far. In 3D and for fourth order elements, one operator +application is also almost ten times as fast as a sparse matrix-vector +product. + +

Parallel run in 3D

+ +To demonstrate how the example scales for a parallel run and to demsonstrate +that hanging node constraints can be handled in an efficient way, we run the +example in 3D with $\mathcal{Q}_4$ elements. First, we run it on a notebook +with 2 cores (Sandy Bridge CPU) at 2.7 GHz. +@code +\$ make debug-mode=off run + Number of global active cells: 17592 + Number of degrees of freedom: 1193881 + Time step size: 0.0117233, finest cell: 0.46875 + + Time: -10, solution norm: 29.558 + Time: -7.66, solution norm: 129.13 + Time: -5.31, solution norm: 67.753 + Time: -2.97, solution norm: 79.245 + Time: -0.621, solution norm: 123.52 + Time: 1.72, solution norm: 43.525 + Time: 4.07, solution norm: 93.285 + Time: 6.41, solution norm: 97.722 + Time: 8.76, solution norm: 36.734 + Time: 10, solution norm: 94.115 + + Performed 1706 time steps. + Average wallclock time per time step: 0.038261s + Spent 11.977s on output and 65.273s on computations. +@endcode + +It takes 0.04 seconds for one time step on a notebook with more than a million +degrees of freedom (note that we would need many processors to reach such +numbers when solving linear systems). If we run the same 3D code on a +cluster with 2 nodes and each node runs 8 threads, we get the following times: + +@code +\$ mpirun --bynode -n 2 ./\step-48 +... + Performed 1706 time steps. + Average wallclock time per time step: 0.0123188s + Spent 6.74378s on output and 21.0158s on computations. +@endcode + +We observe a considerable speedup over the notebook (16 cores versus 2 cores; +nonethess, one notebook core is considerably faster than one core of the +cluster because of a newer processor architecture). If we run the same program +on 4 nodes with 8 threads on each node, we get: +@code +\$ mpirun --bynode -n 4 ./\step-48 +... + Performed 1706 time steps. + Average wallclock time per time step: 0.00689865s + Spent 3.54145s on output and 11.7691s on computations. +@endcode + +By comparing the times for two nodes and four nodes, we observe the nice +scaling behavior of the implementation. Of course, the code can also be run in +MPI-mode only by disabling the multithreading flag in the code. If we use the +same 32 cores as for the hybrid parallelization above, we observe the +following run-time: + +@code +\$ mpirun -n 32 ./\step-48 +... + Performed 1706 time steps. + Average wallclock time per time step: 0.0189041s + Spent 0.968967s on output and 32.2504s on computations. +@endcode + +We observe slower speed for computations, but faster output (which makes +sense, as output is only parallelized by MPI and not threads), whereas the +computations are faster if we use hybrid parallelism in the given case. + +

Possibilities for extensions

+ +There are several things in this program that could be improved to make it +even more efficient (besides improved boundary conditions and physical +stuff as discussed in step-25): + +
  • Faster evaluation of sine terms: As becomes obvious + from the comparison of the plain wave equation and the sine-Gordon + equation above, the evaluation of the sine terms dominates the total + time for the finite element operator application. There are a few + reasons for this: Firstly, the deal.II sine computation of a + VectorizedArray field is not vectorized (as opposed to the rest of + the operator application). This could be cured by handing the sine + computation to a library with vectorized sine computations like + Intel's math kernel library (MKL). By using the function + vdSin in MKL, the program uses half the computing time + in 2D and 40 percent less time in 3D. On the other hand, the sine + computation is structurally much more complicated than the simple + arithmetic operations like additions and multiplications in the rest + of the local operation. + +
  • Higher order time stepping: While the implementation allows for + arbitrary order in the spatial part (by adjusting the degree of the finite + element), the time stepping scheme is a standard second-order leap-frog + scheme. Since solutions in wave propagation problems are usually very + smooth, the error is likely dominated by the time stepping part. Of course, + this could be cured by using smaller time steps (at a fixed spatial + resolution), but it would be more efficient to use higher order time + stepping as well. While it would be straight-forward to do so for a + first-order system (use some Runge–Kutta scheme of higher order, + probably combined with adaptive time step selection like the Dormand–Prince + method), it is more challenging for the second-order formulation. At + least in the finite difference community, people usually use the PDE to find + spatial correction terms that improve the temporal error. + +
diff --git a/deal.II/examples/step-48/doc/tooltip b/deal.II/examples/step-48/doc/tooltip new file mode 100644 index 0000000000..b7f08a32cc --- /dev/null +++ b/deal.II/examples/step-48/doc/tooltip @@ -0,0 +1 @@ +Parallelization via MPI. The wave equation, in linear and nonlinear variants. Mass lumping. Cell-based finite element operator. diff --git a/deal.II/examples/step-48/step-48.cc b/deal.II/examples/step-48/step-48.cc new file mode 100644 index 0000000000..ddd3174156 --- /dev/null +++ b/deal.II/examples/step-48/step-48.cc @@ -0,0 +1,768 @@ +/* $Id$ */ +/* Author: Katharina Kormann, Martin Kronbichler, Uppsala University, 2011 */ + +/* $Id$ */ +/* */ +/* Copyright (C) 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. */ + + + // The necessary files from the deal.II + // library. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + // This includes the data structures for the + // efficient implementation of matrix-free + // methods. +#include +#include +#include + +#include +#include +#include + + +namespace Step48 +{ + using namespace dealii; + + // We start by defining two global + // variables to collect all parameters + // subject to changes at one place: + // One for the dimension and one for + // the finite element degree. The + // dimension is used in the main + // function as a template argument for + // the actual classes (like in all + // other deal.II programs), whereas + // the degree of the finite element is + // more crucial, as it is passed as a + // template argument to the + // implementation of the Sine-Gordon + // operator. Therefore, it needs to be + // a compile-time constant. + const unsigned int dimension = 2; + const unsigned int fe_degree = 4; + + + // @sect3{SineGordonOperation} + + // The SineGordonOperation class + // implements the cell-based operation that is + // needed in each time step. This nonlinear + // operation can be implemented + // straight-forwardly based on the + // MatrixFree class, in the + // same way as a linear operation would be + // treated by this implementation of the + // finite element operator application. We + // apply two template arguments to the class, + // one for the dimension and one for the + // degree of the finite element. This is a + // difference to other functions in deal.II + // where only the dimension is a template + // argument. This is necessary to provide the + // inner loops in @p FEEvaluation with + // information about loop lengths etc., which + // is essential for efficiency. On the other + // hand, it makes it more challenging to + // implement the degree as a run-time + // parameter. + template + class SineGordonOperation + { + public: + typedef VectorizedArray vector_t; + + SineGordonOperation(const MatrixFree &data_in, + const double time_step); + + void apply (parallel::distributed::Vector &dst, + const std::vector*> &src) const; + + private: + const MatrixFree &data; + const vector_t delta_t_sqr; + parallel::distributed::Vector inv_mass_matrix; + + void local_apply (const MatrixFree &data, + parallel::distributed::Vector &dst, + const std::vector*>&src, + const std::pair &cell_range) const; + }; + + + + // @sect4{SineGordonOperation::SineGordonOperation} + + // This is the constructor of the + // SineGordonOperation class. It receives a + // reference to the MatrixFree holding the + // problem information and the time step size + // as input parameters. The initialization + // routine sets up the mass matrix. Since we + // use Gauss-Lobatto elements, the mass matrix + // is a diagonal matrix and can be stored as a + // vector. The computation of the mass matrix + // diagonal is simple to achieve with the data + // structures provided by FEEvaluation: Just + // loop over all (macro-) cells and integrate + // over the function that is constant one on + // all quadrature points by using the + // integrate function with @p + // true argument at the slot for + // values. Finally, we invert the diagonal + // entries since we have to multiply by the + // inverse mass matrix in each time step. + template + SineGordonOperation:: + SineGordonOperation(const MatrixFree &data_in, + const double time_step) + : + data(data_in), + delta_t_sqr(make_vectorized_array(time_step*time_step)) + { + vector_t one = make_vectorized_array (1.); + + data.initialize_dof_vector (inv_mass_matrix); + + FEEvaluationGL fe_eval(data); + const unsigned int n_q_points = fe_eval.n_q_points; + + for (unsigned int cell=0; cell1e-15) + inv_mass_matrix.local_element(k) = 1./inv_mass_matrix.local_element(k); + else + inv_mass_matrix.local_element(k) = 0; + } + + + + // @sect4{SineGordonOperation::local_apply} + + // This operator implements the core operation + // of the program, the integration over a + // range of cells for the nonlinear operator + // of the Sine-Gordon problem. The + // implementation is based on the + // FEEvaluationGL class since we are using + // the cell-based implementation for + // Gauss-Lobatto elements. + + // The nonlinear function that we have to + // evaluate for the time stepping routine + // includes the value of the function at + // the present time @p current as well as + // the value at the previous time step @p + // old. Both values are passed to the + // operator in the collection of source + // vectors @p src, which is simply an STL + // vector of pointers to the actual + // solution vectors. This construct of + // collecting several source vectors into + // one is necessary as the cell loop in @p + // MatrixFree takes exactly one source + // and one destination vector, even if we + // happen to use many vectors like the two + // in this case. Note that the cell loop + // accepts any valid class for input and + // output, which does not only include + // vectors but general data types. However, + // only in case it encounters a + // parallel::distributed::Vector or + // an STL vector collecting these vectors, + // it calls functions that exchange data at + // the beginning and the end of the + // loop. In the loop over the cells, we + // first have to read in the values in the + // vectors related to the local + // values. Then, we evaluate the value and + // the gradient of the current solution + // vector and the values of the old vector + // at the quadrature points. Then, we + // combine the terms in the scheme in the + // loop over the quadrature + // points. Finally, we integrate the result + // against the test function and accumulate + // the result to the global solution vector + // @p dst. + template + void SineGordonOperation:: + local_apply (const MatrixFree &data, + parallel::distributed::Vector &dst, + const std::vector*>&src, + const std::pair &cell_range) const + { + AssertDimension (src.size(), 2); + FEEvaluationGL current (data), old (data); + for (unsigned int cell=cell_range.first; celloperator () method of the + // class SineGordonOperation, + // i.e., *this. One could also + // provide a function with the same signature + // that is not part of a class. + template + void SineGordonOperation:: + apply (parallel::distributed::Vector &dst, + const std::vector*> &src) const + { + dst = 0; + data.cell_loop (&SineGordonOperation::local_apply, + this, dst, src); + dst.scale(inv_mass_matrix); + } + + + //@sect3{Equation data} + + // We define a time-dependent function that is + // used as initial value. Different solutions + // can be obtained by varying the starting + // time. This function has already been + // explained in step-25. + template + class ExactSolution : public Function + { + public: + ExactSolution (const unsigned int n_components = 1, + const double time = 0.) : Function(n_components, time) {} + virtual double value (const Point &p, + const unsigned int component = 0) const; + }; + + template + double ExactSolution::value (const Point &p, + const unsigned int /* component */) const + { + double t = this->get_time (); + + const double m = 0.5; + const double c1 = 0.; + const double c2 = 0.; + const double factor = (m / std::sqrt(1.-m*m) * + std::sin(std::sqrt(1.-m*m)*t+c2)); + double result = 1.; + for (unsigned int d=0; d class by the + // MatrixFree class to store + // the geometry data. Also, we use a + // distributed triangulation in this example. + template + class SineGordonProblem + { + public: + SineGordonProblem (); + void run (); + + private: + ConditionalOStream pcout; + + void make_grid_and_dofs (); + void oldstyle_operation (); + void assemble_system (); + void output_results (const unsigned int timestep_number) const; + +#ifdef DEAL_II_USE_P4EST + parallel::distributed::Triangulation triangulation; +#else + Triangulation triangulation; +#endif + FE_Q fe; + DoFHandler dof_handler; + ConstraintMatrix constraints; + IndexSet locally_relevant_dofs; + + MatrixFree matrix_free_data; + + parallel::distributed::Vector solution, old_solution, old_old_solution; + + const unsigned int n_global_refinements; + double time, time_step; + const double final_time; + const double cfl_number; + const unsigned int output_timestep_skip; + }; + + + //@sect4{SineGordonProblem::SineGordonProblem} + + // This is the constructor of the + // SineGordonProblem class. The time interval + // and time step size are defined + // here. Moreover, we use the degree of the + // finite element that we defined at the top + // of the program to initialize a FE_Q finite + // element based on Gauss-Lobatto support + // points. These points are convenient because + // in conjunction with a QGaussLobatto + // quadrature rule of the same order they give + // a diagonal mass matrix without compromising + // accuracy too much (note that the + // integration is inexact, though), see also + // the discussion in the introduction. + template + SineGordonProblem::SineGordonProblem () + : + pcout (std::cout, + Utilities::System::get_this_mpi_process(MPI_COMM_WORLD)==0), +#ifdef DEAL_II_USE_P4EST + triangulation (MPI_COMM_WORLD), +#endif + fe (QGaussLobatto<1>(fe_degree+1)), + dof_handler (triangulation), + n_global_refinements (11-2*dim), + time (-10), + final_time (10), + cfl_number (.1/fe_degree), + output_timestep_skip (200) + {} + + //@sect4{SineGordonProblem::make_grid_and_dofs} + + // As in step-25 this functions sets up a cube + // grid in dim dimensions of + // extent $[-15,15]$. We refine the mesh more + // in the center of the domain since the + // solution is concentrated there. We first + // refine all cells whose center is within a + // radius of 11, and then refine once more for + // a radius 6. This is simple ad-hoc + // refinement could be done better by adapting + // the mesh to the solution using error + // estimators during the time stepping as done + // in other example programs, and using + // parallel::distributed::SolutionTransfer to + // transfer the solution to the new mesh. + template + void SineGordonProblem::make_grid_and_dofs () + { + GridGenerator::hyper_cube (triangulation, -15, 15); + triangulation.refine_global (n_global_refinements); + { + typename Triangulation::active_cell_iterator + cell = triangulation.begin_active(), + end_cell = triangulation.end(); + for ( ; cell != end_cell; ++cell) + if (cell->is_locally_owned()) + if (cell->center().norm() < 11) + cell->set_refine_flag(); + triangulation.execute_coarsening_and_refinement(); + + cell = triangulation.begin_active(); + end_cell = triangulation.end(); + for ( ; cell != end_cell; ++cell) + if (cell->is_locally_owned()) + if (cell->center().norm() < 6) + cell->set_refine_flag(); + triangulation.execute_coarsening_and_refinement(); + } + + pcout << " Number of global active cells: " +#ifdef DEAL_II_USE_P4EST + << triangulation.n_global_active_cells() +#else + << triangulation.n_active_cells() +#endif + << std::endl; + + dof_handler.distribute_dofs (fe); + + pcout << " Number of degrees of freedom: " + << dof_handler.n_dofs() + << std::endl; + + + // We generate hanging node constraints for + // ensuring continuity of the solution. As in + // step-40, we need to equip the constraint + // matrix with the IndexSet of locally + // relevant degrees of freedom to avoid it to + // consume too much memory for big + // problems. Next, the MatrixFree + // for the problem is set up. Note + // that we specify the MPI communicator which + // we are going to use, and that we also want + // to use shared-memory parallelization (hence + // one would use multithreading for intra-node + // parallelism and not MPI; note that we here + // choose the standard option &mdash if we + // wanted to disable shared memory + // parallelization, we would choose @p + // none). Finally, three solution vectors are + // initialized. MatrixFree stores the + // layout that is to be used by distributed + // vectors, so we just ask it to initialize + // the vectors. + DoFTools::extract_locally_relevant_dofs (dof_handler, + locally_relevant_dofs); + constraints.clear(); + constraints.reinit (locally_relevant_dofs); + DoFTools::make_hanging_node_constraints (dof_handler, constraints); + constraints.close(); + + QGaussLobatto<1> quadrature (fe_degree+1); + typename MatrixFree::AdditionalData additional_data; + additional_data.mpi_communicator = MPI_COMM_WORLD; + additional_data.tasks_parallel_scheme = + MatrixFree::AdditionalData::partition_partition; + + matrix_free_data.reinit (dof_handler, constraints, + quadrature, additional_data); + + matrix_free_data.initialize_dof_vector (solution); + old_solution.reinit (solution); + old_old_solution.reinit (solution); + } + + + + //@sect4{SineGordonProblem::output_results} + + // This function prints the norm of the + // solution and writes the solution vector to + // a file. The norm is standard (except for + // the fact that we need to be sure to only + // count norms on locally owned cells), and + // the second is similar to what we did in + // step-40. However, we first need to generate + // an appropriate vector for output: The ones + // we used during time stepping contained + // information about ghosts dofs that one + // needs write access to during the loops over + // cell. However, that is not the same as + // needed when outputting. So we first + // initialize a vector with locally relevant + // degrees of freedom by copying the solution + // (note how we use the function @p copy_from + // to transfer data between vectors with the + // same local range, but different layouts of + // ghosts). Then, we import the values on the + // ghost DoFs and then distribute the + // constraints (as constraints are zero in the + // vectors during loop over all cells). + template + void + SineGordonProblem::output_results (const unsigned int timestep_number) const + { + parallel::distributed::Vector locally_relevant_solution; + locally_relevant_solution.reinit (dof_handler.locally_owned_dofs(), + locally_relevant_dofs, + MPI_COMM_WORLD); + locally_relevant_solution.copy_from (solution); + locally_relevant_solution.update_ghost_values (); + constraints.distribute (locally_relevant_solution); + + Vector norm_per_cell (triangulation.n_active_cells()); + VectorTools::integrate_difference (dof_handler, + locally_relevant_solution, + ZeroFunction(), + norm_per_cell, + QGauss(fe_degree+1), + VectorTools::L2_norm); + const double solution_norm = + std::sqrt(Utilities::MPI::sum (norm_per_cell.norm_sqr(), MPI_COMM_WORLD)); + + pcout << " Time:" + << std::setw(8) << std::setprecision(3) << time + << ", solution norm: " + << std::setprecision(5) << std::setw(7) << solution_norm + << std::endl; + + DataOut data_out; + + data_out.attach_dof_handler (dof_handler); + data_out.add_data_vector (locally_relevant_solution, "solution"); + data_out.build_patches (); + + const std::string filename = + "solution-" + Utilities::int_to_string (timestep_number, 3) + + "." + Utilities::int_to_string (Utilities::MPI:: + this_mpi_process(MPI_COMM_WORLD),4); + + std::ofstream output ((filename + ".vtu").c_str()); + data_out.write_vtu (output); + + if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) + { + std::vector filenames; + for (unsigned int i=0; + i + void + SineGordonProblem::run () + { + make_grid_and_dofs(); + + const double local_min_cell_diameter = + triangulation.last()->diameter()/std::sqrt(dim); + const double global_min_cell_diameter + = -Utilities::MPI::max(-local_min_cell_diameter, MPI_COMM_WORLD); + time_step = cfl_number * global_min_cell_diameter; + time_step = (final_time-time)/(int((final_time-time)/time_step)); + pcout << " Time step size: " << time_step << ", finest cell: " + << global_min_cell_diameter << std::endl << std::endl; + + // Next the initial value is set. Since we + // have a two-step time stepping method, we + // also need a value of the solution at + // time-time_step. For accurate results, one + // would need to compute this from the time + // derivative of the solution at initial time, + // but here we ignore this difficulty and just + // set it to the initial value function at + // that artificial time. + + // We create an output of the initial + // value. Then we also need to collect + // the two starting solutions in an STL + // vector of pointers field and to set up + // an instance of the + // SineGordonOperation class + // based on the finite element degree + // specified at the top of this file. + VectorTools::interpolate (dof_handler, + ExactSolution (1, time), + solution); + VectorTools::interpolate (dof_handler, + ExactSolution (1, time-time_step), + old_solution); + output_results (0); + + std::vector*> previous_solutions; + previous_solutions.push_back(&old_solution); + previous_solutions.push_back(&old_old_solution); + + SineGordonOperation sine_gordon_op (matrix_free_data, + time_step); + + // Now loop over the time steps. In each + // iteration, we shift the solution + // vectors by one and call the + // apply function of the + // SineGordonOperator . Then, we + // write the solution to a file. We clock + // the wall times for the computational + // time needed as wall as the time needed + // to create the output and report the + // numbers when the time stepping is + // finished. + // + // Note how this shift is implemented: We + // simply call the swap method on the two + // vectors which swaps only some pointers + // without the need to copy data + // around. Obviously, this is a more + // efficient way to move data around. Let + // us see what happens in more detail: + // First, we exchange + // old_solution with + // old_old_solution, which + // means that + // old_old_solution gets + // old_solution, which is + // what we expect. Similarly, + // old_solution gets the + // content from solution in + // the next step. Afterward, + // solution holds + // old_old_solution, but + // that will be overwritten during this + // step. + unsigned int timestep_number = 1; + + Timer timer; + double wtime = 0; + double output_time = 0; + for (time+=time_step; time<=final_time; time+=time_step, ++timestep_number) + { + timer.restart(); + old_old_solution.swap (old_solution); + old_solution.swap (solution); + sine_gordon_op.apply (solution, previous_solutions); + wtime += timer.wall_time(); + + timer.restart(); + if (timestep_number % output_timestep_skip == 0) + output_results(timestep_number / output_timestep_skip); + + output_time += timer.wall_time(); + } + timer.restart(); + output_results(timestep_number / output_timestep_skip + 1); + output_time += timer.wall_time(); + + pcout << std::endl + << " Performed " << timestep_number << " time steps." + << std::endl; + + pcout << " Average wallclock time per time step: " + << wtime / timestep_number << "s" << std::endl; + + pcout << " Spent " << output_time << "s on output and " + << wtime << "s on computations." << std::endl; + } +} + + + + // @sect3{The main function} + + // This is as in all other programs: +int main (int argc, char ** argv) +{ + using namespace Step48; + using namespace dealii; + + Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv); + + try + { + deallog.depth_console (0); + + SineGordonProblem sg_problem; + sg_problem.run (); + } + catch (std::exception &exc) + { + std::cerr << std::endl << std::endl + << "----------------------------------------------------" + << std::endl; + std::cerr << "Exception on processing: " << std::endl + << exc.what() << std::endl + << "Aborting!" << std::endl + << "----------------------------------------------------" + << std::endl; + + return 1; + } + catch (...) + { + std::cerr << std::endl << std::endl + << "----------------------------------------------------" + << std::endl; + std::cerr << "Unknown exception!" << std::endl + << "Aborting!" << std::endl + << "----------------------------------------------------" + << std::endl; + return 1; + } + + return 0; +} diff --git a/deal.II/include/deal.II/base/config.h.in b/deal.II/include/deal.II/base/config.h.in index 8c0427040d..f65ec3436b 100644 --- a/deal.II/include/deal.II/base/config.h.in +++ b/deal.II/include/deal.II/base/config.h.in @@ -46,9 +46,6 @@ */ #undef DEAL_II_ABORT_NOTHROW_EXCEPTION -/* Forces the compiler to always inline functions, also in debug mode */ -#undef DEAL_II_ALWAYS_INLINE - /* Flag indicating whether there is a bug in the compiler that leads to bogus warnings for inline class members in anonymous namespaces */ #undef DEAL_II_ANON_NAMESPACE_BOGUS_WARNING @@ -100,6 +97,10 @@ types */ #undef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS +/* Equal to 0 in the generic case, equal to 1 if CPU compiled for supports + SSE2, equal to 2 if CPU compiled for supports AVX */ +#undef DEAL_II_COMPILER_VECTORIZATION_LEVEL + /* Defined if the compiler has a bug in deducing the type of pointers to const member functions. */ #undef DEAL_II_CONST_MEMBER_DEDUCTION_BUG diff --git a/deal.II/include/deal.II/base/parallel.h b/deal.II/include/deal.II/base/parallel.h index ed3b8b2a76..23d9bc7f92 100644 --- a/deal.II/include/deal.II/base/parallel.h +++ b/deal.II/include/deal.II/base/parallel.h @@ -340,7 +340,7 @@ namespace parallel namespace internal { -#if DEAL_II_USE_MT +#if DEAL_II_USE_MT == 1 /** * Take a range argument and call the * given function with its begin and end. @@ -479,6 +479,65 @@ namespace parallel + /** + * This is a class specialized to for loops with a fixed range given by + * unsigned integers. This is an abstract base class that an actual worker + * function is derived from. There is a public function apply that issues a + * for loop in parallel, subdividing the work onto available processor cores + * whenever there is enough work to be done (i.e., the number of elements is + * larger than grain_size). Inside the function, a virtual function + * apply_to_subrange specifying a range of two integers [lower, + * upper) is called which needs to be defined in a derived class. + * + * The parallelization cases covered by this class are a subset of what is + * possible with the function apply_to_subranges (which also covers the case + * of more general iterators that might not be described by an integer + * range). However, for simple integer ranges one might prefer this class, + * like when there are many structurally similar loops, e.g., some simple + * copy or arithmetic operations on an array of pointers. In that case, + * apply_to_subranges will generate a lot of code (or rather, a lot of + * symbols) because it passes the long names generated by std::bind to the + * templated parallel for functions in TBB. This can considerably increase + * compile times and the size of the object code. Similarly, the incorrect + * use of std::bind often results in very cryptic error messages, which can + * be avoided by this class (only a virtual function needs to be defined in + * a derived class). Finally, the additional cost of a virtual function is + * negligible in the context of parallel functions: It is much more + * expensive to actually issue the work onto a thread, which in turn should + * be much less than the actual work done in the for loop. + */ + struct ParallelForInteger + { + /** + * This function runs the for loop over the + * given range [lower,upper), + * possibly in parallel when end-begin is + * larger than the minimum parallel grain + * size. This function is marked const because + * it any operation that changes the data of a + * derived class will inherently not be + * thread-safe when several threads work with + * the same data simultaneously. + */ + void apply_parallel (const std::size_t begin, + const std::size_t end, + const std::size_t minimum_parallel_grain_size) const; + + /** + * Virtual function for working on subrange to + * be defined in a derived class. This + * function is marked const because it any + * operation that changes the data of a + * derived class will inherently not be + * thread-safe when several threads work with + * the same data simultaneously. + */ + virtual void apply_to_subrange (const std::size_t, + const std::size_t) const = 0; + }; + + + namespace internal { #if DEAL_II_USE_MT == 1 @@ -510,7 +569,7 @@ namespace parallel * operation. * * The second argument denotes a - * function object that will be use + * function object that will be used * to reduce the result of two * computations into one number. An * example if we want to simply @@ -647,18 +706,18 @@ namespace parallel * subranges accumulated internally. * * @warning If ResultType is a floating point - * type, then accumulation is not a - * commutative operation. In other words, + * type, then accumulation is not an + * associative operation. In other words, * if the given function object is called * three times on three subranges, * returning values $a,b,c$, then the * returned result of this function is - * $a+b+c$. However, depending on how the + * $(a+b)+c$. However, depending on how the * three sub-tasks are distributed on * available CPU resources, the result may - * also be $a+c+b$ or any other + * also be $(a+c)+b$ or any other * permutation; because floating point - * addition does not commute (as opposed, of + * addition is not associative (as opposed, of * course, to addition of real %numbers), * the result of invoking this function * several times may differ on the order of @@ -739,7 +798,64 @@ namespace internal */ extern unsigned int minimum_parallel_grain_size; } -} + +} // end of namespace internal + + +/* --------------------------- inline functions ------------------------- */ + +namespace parallel +{ + +#if DEAL_II_USE_MT == 1 + + namespace internal + { + /** + * This is the function actually called by TBB for the ParallelForInteger + * class. + */ + struct ParallelForWrapper + { + ParallelForWrapper (const parallel::ParallelForInteger &worker) + : + worker_ (worker) + {} + + void operator() (const tbb::blocked_range &range) const + { + worker_.apply_to_subrange (range.begin(), range.end()); + } + + const parallel::ParallelForInteger &worker_; + }; + } + +#endif + + + inline + void + ParallelForInteger::apply_parallel (const std::size_t begin, + const std::size_t end, + const std::size_t minimum_parallel_grain_size) const + { +#if DEAL_II_USE_MT == 0 + // make sure we don't get compiler + // warnings about unused arguments + (void) grainsize; + + apply_to_subrange (begin, end); +#else + internal::ParallelForWrapper worker(*this); + tbb::parallel_for (tbb::blocked_range + (begin, end, minimum_parallel_grain_size), + worker, + tbb::auto_partitioner()); +#endif + } + +} // end of namespace parallel DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/include/deal.II/base/point.h b/deal.II/include/deal.II/base/point.h index 250bdac915..a87204fe21 100644 --- a/deal.II/include/deal.II/base/point.h +++ b/deal.II/include/deal.II/base/point.h @@ -343,7 +343,7 @@ inline Number Point::square () const { - Number q=0; + Number q = Number(); for (unsigned int i=0; ivalues[i] * this->values[i]; return q; diff --git a/deal.II/include/deal.II/base/std_cxx1x/type_traits.h b/deal.II/include/deal.II/base/std_cxx1x/type_traits.h new file mode 100644 index 0000000000..09b5a3d602 --- /dev/null +++ b/deal.II/include/deal.II/base/std_cxx1x/type_traits.h @@ -0,0 +1,63 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 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__std_cxx1x_type_traits_h +#define __deal2__std_cxx1x_type_traits_h + + +#include + +#ifdef DEAL_II_CAN_USE_CXX1X + +# include +DEAL_II_NAMESPACE_OPEN +namespace std_cxx1x +{ + // TODO: could fill up with more types from + // C++11 type traits + using std::is_pod; + using std::is_standard_layout; + using std::is_trivial; +} +DEAL_II_NAMESPACE_CLOSE + +#else + +#include +DEAL_II_NAMESPACE_OPEN +namespace std_cxx1x +{ + using boost::is_pod; + + // boost does not have is_standard_layout and + // is_trivial, but those are both a subset of + // is_pod + template + struct is_standard_layout + { + static const bool value = boost::is_pod::value; + }; + + template + struct is_trivial + { + static const bool value = boost::has_trivial_copy::value && + boost::has_trivial_assign::value && + boost::has_trivial_constructor::value && + boost::has_trivial_destructor::value; + }; +} +DEAL_II_NAMESPACE_CLOSE + +#endif + +#endif diff --git a/deal.II/include/deal.II/base/symmetric_tensor.h b/deal.II/include/deal.II/base/symmetric_tensor.h index 0760e6514a..4bddb22908 100644 --- a/deal.II/include/deal.II/base/symmetric_tensor.h +++ b/deal.II/include/deal.II/base/symmetric_tensor.h @@ -898,6 +898,30 @@ class SymmetricTensor Number & operator [] (const TableIndices &indices); + /** + * Access to an element according + * to unrolled index. The + * function + * s.access_raw_entry(i) + * does the same as + * s[s.unrolled_to_component_indices(i)], + * but more efficiently. + */ + Number + access_raw_entry (const unsigned int unrolled_index) const; + + /** + * Access to an element according + * to unrolled index. The + * function + * s.access_raw_entry(i) + * does the same as + * s[s.unrolled_to_component_indices(i)], + * but more efficiently. + */ + Number & + access_raw_entry (const unsigned int unrolled_index); + /** * Return the Frobenius-norm of a tensor, * i.e. the square root of the sum of @@ -1569,7 +1593,7 @@ namespace internal } } - static Number dummy_but_referenceable = 0; + static Number dummy_but_referenceable = Number(); return dummy_but_referenceable; } @@ -1910,6 +1934,28 @@ SymmetricTensor::operator [] (const TableIndices &indices +template +inline +Number +SymmetricTensor::access_raw_entry (const unsigned int index) const +{ + AssertIndexRange (index, data.dimension); + return data[index]; +} + + + +template +inline +Number & +SymmetricTensor::access_raw_entry (const unsigned int index) +{ + AssertIndexRange (index, data.dimension); + return data[index]; +} + + + namespace internal { template diff --git a/deal.II/include/deal.II/base/tensor.h b/deal.II/include/deal.II/base/tensor.h index 85ae3e9330..cceccc92ce 100644 --- a/deal.II/include/deal.II/base/tensor.h +++ b/deal.II/include/deal.II/base/tensor.h @@ -1555,8 +1555,7 @@ template inline Number determinant (const Tensor<2,2,Number> &t) { - return ((t[0][0] * t[1][1]) - - (t[1][0] * t[0][1])); + return ((t[0][0] * t[1][1]) - (t[1][0] * t[0][1])); } @@ -1570,18 +1569,20 @@ template inline Number determinant (const Tensor<2,3,Number> &t) { - // get this using Maple: - // with(linalg); - // a := matrix(3,3); - // x := det(a); - // readlib(C); - // C(x, optimized); - return ( t[0][0]*t[1][1]*t[2][2] - -t[0][0]*t[1][2]*t[2][1] - -t[1][0]*t[0][1]*t[2][2] - +t[1][0]*t[0][2]*t[2][1] - +t[2][0]*t[0][1]*t[1][2] - -t[2][0]*t[0][2]*t[1][1] ); + // use exactly the same expression with the + // same order of operations as for the inverse + // to let the compiler use common + // subexpression elimination when using + // determinant and inverse in nearby code + const Number t4 = t[0][0]*t[1][1], + t6 = t[0][0]*t[1][2], + t8 = t[0][1]*t[1][0], + t00 = t[0][2]*t[1][0], + t01 = t[0][1]*t[2][0], + t04 = t[0][2]*t[2][0], + det = (t4*t[2][2]-t6*t[2][1]-t8*t[2][2]+ + t00*t[2][1]+t01*t[1][2]-t04*t[1][1]); + return det; } @@ -1608,7 +1609,7 @@ Number determinant (const Tensor<2,dim,Number> &t) for (unsigned int k=0; k minor; + Tensor<2,dim-1,Number> minor; for (unsigned int i=0; i &t) // this is Maple output, // thus a bit unstructured { - const Number t4 = 1.0/(t[0][0]*t[1][1]-t[0][1]*t[1][0]); + const Number det = t[0][0]*t[1][1]-t[1][0]*t[0][1]; + const Number t4 = 1.0/det; return_tensor[0][0] = t[1][1]*t4; return_tensor[0][1] = -t[0][1]*t4; return_tensor[1][0] = -t[1][0]*t4; @@ -1683,8 +1685,9 @@ invert (const Tensor<2,dim,Number> &t) t00 = t[0][2]*t[1][0], t01 = t[0][1]*t[2][0], t04 = t[0][2]*t[2][0], - t07 = 1.0/(t4*t[2][2]-t6*t[2][1]-t8*t[2][2]+ - t00*t[2][1]+t01*t[1][2]-t04*t[1][1]); + det = (t4*t[2][2]-t6*t[2][1]-t8*t[2][2]+ + t00*t[2][1]+t01*t[1][2]-t04*t[1][1]), + t07 = 1.0/det; return_tensor[0][0] = (t[1][1]*t[2][2]-t[1][2]*t[2][1])*t07; return_tensor[0][1] = (t[0][2]*t[2][1]-t[0][1]*t[2][2])*t07; return_tensor[0][2] = (t[0][1]*t[1][2]-t[0][2]*t[1][1])*t07; @@ -1701,7 +1704,6 @@ invert (const Tensor<2,dim,Number> &t) // if desired, take over the // inversion of a 4x4 tensor // from the FullMatrix - default: AssertThrow (false, ExcNotImplemented()); } diff --git a/deal.II/include/deal.II/base/tensor_base.h b/deal.II/include/deal.II/base/tensor_base.h index 252fe3546a..4a8b2e316b 100644 --- a/deal.II/include/deal.II/base/tensor_base.h +++ b/deal.II/include/deal.II/base/tensor_base.h @@ -887,13 +887,13 @@ inline Tensor<1,dim,Number>::Tensor (const bool initialize) { if (initialize) - // need to create an object Number(0) to + // need to create an object Number() to // initialize to zero to avoid confusion with // Tensor::operator=(scalar) when using // something like // Tensor<1,dim,Tensor<1,dim,Number> >. for (unsigned int i=0; i!=dim; ++i) - values[i] = Number(0); + values[i] = Number(); } diff --git a/deal.II/include/deal.II/base/vectorization.h b/deal.II/include/deal.II/base/vectorization.h new file mode 100644 index 0000000000..5308189f54 --- /dev/null +++ b/deal.II/include/deal.II/base/vectorization.h @@ -0,0 +1,2138 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 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__vectorization_h +#define __deal2__vectorization_h + +#include +#include +#include +#include +#include + +#include +#include + +// set flag DEAL_II_COMPILER_VECTORIZATION_LEVEL depending on whether we are +// in debug or optimized mode +// Note: +// The flag DEAL_II_COMPILER_VECTORIZATION_LEVEL is essentially constructed +// according to the following scheme +// #ifdef __AVX__ +// #define DEAL_II_COMPILER_VECTORIZATION_LEVEL 2 +// #elif defined (__SSE2__) +// #define DEAL_II_COMPILER_VECTORIZATION_LEVEL 1 +// #else +// #define DEAL_II_COMPILER_VECTORIZATION_LEVEL 0 +// #endif +// In addition to checking the flags __AVX__ and __SSE2__, a configure test +// ensures that these feature are not only present but also working properly. + +#if DEAL_II_COMPILER_VECTORIZATION_LEVEL == 2 // AVX + #include + #include +#elif DEAL_II_COMPILER_VECTORIZATION_LEVEL == 1 // SSE2 + #include + #include +#endif + + + +// forward declarations +namespace dealii +{ +template class VectorizedArray; +} +namespace std +{ + template ::dealii::VectorizedArray + sqrt(const ::dealii::VectorizedArray&); + template ::dealii::VectorizedArray + abs(const ::dealii::VectorizedArray&); + template ::dealii::VectorizedArray + max(const ::dealii::VectorizedArray&, const ::dealii::VectorizedArray&); + template ::dealii::VectorizedArray + min (const ::dealii::VectorizedArray&, const ::dealii::VectorizedArray&); +} + + +DEAL_II_NAMESPACE_OPEN + + +// for safety, also check that __AVX__ is defined in case the user manually +// set some conflicting compile flags which prevent compilation + +#if DEAL_II_COMPILER_VECTORIZATION_LEVEL == 2 && defined(__AVX__) + + /** + * Specialization of VectorizedArray class for double and AVX. + */ +template <> +class VectorizedArray +{ +public: + /** + * This gives the number of vectors collected + * in this class. + */ + static const unsigned int n_array_elements = 4; + + /** + * This function can be used to set all data + * fields to a given scalar. + */ + VectorizedArray& + operator = (const double x) + { + data = _mm256_set1_pd(x); + return *this; + } + + /** + * Access operator. + */ + double & + operator [] (const unsigned int comp) + { + AssertIndexRange (comp, 4); + return *(reinterpret_cast(&data)+comp); + } + + /** + * Constant access operator. + */ + const double & + operator [] (const unsigned int comp) const + { + AssertIndexRange (comp, 4); + return *(reinterpret_cast(&data)+comp); + } + + /** + * Addition. + */ + VectorizedArray & + operator += (const VectorizedArray &vec) + { + // if the compiler supports vector + // arithmetics, we can simply use += operator + // on the given data type. Otherwise, we need + // to use the built-in intrinsic command for + // __m256d +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data += vec.data; +#else + data = _mm256_add_pd(data,vec.data); +#endif + return *this; + } + + /** + * Subtraction. + */ + VectorizedArray & + operator -= (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data -= vec.data; +#else + data = _mm256_sub_pd(data,vec.data); +#endif + return *this; + } + /** + * Multiplication. + */ + VectorizedArray & + operator *= (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data *= vec.data; +#else + data = _mm256_mul_pd(data,vec.data); +#endif + return *this; + } + + /** + * Division. + */ + VectorizedArray & + operator /= (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data /= vec.data; +#else + data = _mm256_div_pd(data,vec.data); +#endif + return *this; + } + + /** + * Actual data field. Since this class + * represents a POD data type, it remains + * public. + */ + __m256d data; + +private: + /** + * Returns the square root of this field. Not + * for use in user code. Use sqrt(x) instead. + */ + VectorizedArray + get_sqrt () const + { + VectorizedArray res; + res.data = _mm256_sqrt_pd(data); + return res; + } + + /** + * Returns the absolute value of this + * field. Not for use in user code. Use + * abs(x) instead. + */ + VectorizedArray + get_abs () const + { + // to compute the absolute value, perform + // bitwise andnot with -0. This will leave all + // value and exponent bits unchanged but force + // the sign value to +. + __m256d mask = _mm256_set1_pd (-0.); + VectorizedArray res; + res.data = _mm256_andnot_pd(mask, data); + return res; + } + + /** + * Returns the component-wise maximum of this + * field and another one. Not for use in user + * code. Use max(x,y) instead. + */ + VectorizedArray + get_max (const VectorizedArray &other) const + { + VectorizedArray res; + res.data = _mm256_max_pd (data, other.data); + return res; + } + + /** + * Returns the component-wise minimum of this + * field and another one. Not for use in user + * code. Use min(x,y) instead. + */ + VectorizedArray + get_min (const VectorizedArray &other) const + { + VectorizedArray res; + res.data = _mm256_min_pd (data, other.data); + return res; + } + + /** + * Make a few functions friends. + */ + template friend VectorizedArray + std::sqrt (const VectorizedArray&); + template friend VectorizedArray + std::abs (const VectorizedArray&); + template friend VectorizedArray + std::max (const VectorizedArray&, const VectorizedArray&); + template friend VectorizedArray + std::min (const VectorizedArray&, const VectorizedArray&); +}; + + + + /** + * Specialization for float and AVX. + */ +template<> +class VectorizedArray +{ +public: + /** + * This gives the number of vectors collected + * in this class. + */ + static const unsigned int n_array_elements = 8; + + /** + * This function can be used to set all data + * fields to a given scalar. + */ + VectorizedArray& + operator = (const float x) + { + data = _mm256_set1_ps(x); + return *this; + } + + /** + * Access operator. + */ + float & + operator [] (const unsigned int comp) + { + AssertIndexRange (comp, 8); + return *(reinterpret_cast(&data)+comp); + } + + /** + * Constant access operator. + */ + const float & + operator [] (const unsigned int comp) const + { + AssertIndexRange (comp, 8); + return *(reinterpret_cast(&data)+comp); + } + + /** + * Addition. + */ + VectorizedArray & + operator += (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data += vec.data; +#else + data = _mm256_add_ps(data,vec.data); +#endif + return *this; + } + + /** + * Subtraction. + */ + VectorizedArray & + operator -= (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data -= vec.data; +#else + data = _mm256_sub_ps(data,vec.data); +#endif + return *this; + } + /** + * Multiplication. + */ + VectorizedArray & + operator *= (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data *= vec.data; +#else + data = _mm256_mul_ps(data,vec.data); +#endif + return *this; + } + + /** + * Division. + */ + VectorizedArray & + operator /= (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data /= vec.data; +#else + data = _mm256_div_ps(data,vec.data); +#endif + return *this; + } + + /** + * Actual data field. Since this class + * represents a POD data type, it remains + * public. + */ + __m256 data; + +private: + + /** + * Returns the square root of this field. Not + * for use in user code. Use sqrt(x) instead. + */ + VectorizedArray + get_sqrt () const + { + VectorizedArray res; + res.data = _mm256_sqrt_ps(data); + return res; + } + /** + * Returns the absolute value of this + * field. Not for use in user code. Use + * abs(x) instead. + */ + VectorizedArray + get_abs () const + { + // to compute the absolute value, perform + // bitwise andnot with -0. This will leave all + // value and exponent bits unchanged but force + // the sign value to +. + __m256 mask = _mm256_set1_ps (-0.f); + VectorizedArray res; + res.data = _mm256_andnot_ps(mask, data); + return res; + } + + /** + * Returns the component-wise maximum of this + * field and another one. Not for use in user + * code. Use max(x,y) instead. + */ + VectorizedArray + get_max (const VectorizedArray &other) const + { + VectorizedArray res; + res.data = _mm256_max_ps (data, other.data); + return res; + } + + /** + * Returns the component-wise minimum of this + * field and another one. Not for use in user + * code. Use min(x,y) instead. + */ + VectorizedArray + get_min (const VectorizedArray &other) const + { + VectorizedArray res; + res.data = _mm256_min_ps (data, other.data); + return res; + } + + /** + * Make a few functions friends. + */ + template friend VectorizedArray + std::sqrt (const VectorizedArray &); + template friend VectorizedArray + std::abs (const VectorizedArray &); + template friend VectorizedArray + std::max (const VectorizedArray&, const VectorizedArray&); + template friend VectorizedArray + std::min (const VectorizedArray&, const VectorizedArray&); +}; + + + +// for safety, also check that __SSE2__ is defined in case the user manually +// set some conflicting compile flags which prevent compilation + +#elif DEAL_II_COMPILER_VECTORIZATION_LEVEL >= 1 && defined(__SSE2__) + + + + /** + * Specialization for double and SSE2. + */ +template <> +class VectorizedArray +{ +public: + /** + * This gives the number of vectors collected + * in this class. + */ + static const unsigned int n_array_elements = 2; + + /** + * This function can be used to set all data + * fields to a given scalar. + */ + + VectorizedArray& + operator = (const double x) + { + data = _mm_set1_pd(x); + return *this; + } + + /** + * Access operator. + */ + double & + operator [] (const unsigned int comp) + { + AssertIndexRange (comp, 2); + return *(reinterpret_cast(&data)+comp); + } + + /** + * Constant access operator. + */ + const double & + operator [] (const unsigned int comp) const + { + AssertIndexRange (comp, 2); + return *(reinterpret_cast(&data)+comp); + } + + /** + * Addition. + */ + VectorizedArray & + operator += (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data += vec.data; +#else + data = _mm_add_pd(data,vec.data); +#endif + return *this; + } + + /** + * Subtraction. + */ + VectorizedArray & + operator -= (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data -= vec.data; +#else + data = _mm_sub_pd(data,vec.data); +#endif + return *this; + } + /** + * Multiplication. + */ + VectorizedArray & + operator *= (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data *= vec.data; +#else + data = _mm_mul_pd(data,vec.data); +#endif + return *this; + } + + /** + * Division. + */ + VectorizedArray & + operator /= (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data /= vec.data; +#else + data = _mm_div_pd(data,vec.data); +#endif + return *this; + } + + /** + * Actual data field. Since this class + * represents a POD data type, it remains + * public. + */ + __m128d data; + +private: + /** + * Returns the square root of this field. Not + * for use in user code. Use sqrt(x) instead. + */ + VectorizedArray + get_sqrt () const + { + VectorizedArray res; + res.data = _mm_sqrt_pd(data); + return res; + } + + /** + * Returns the absolute value of this + * field. Not for use in user code. Use abs(x) + * instead. + */ + VectorizedArray + get_abs () const + { + // to compute the absolute value, perform + // bitwise andnot with -0. This will leave all + // value and exponent bits unchanged but force + // the sign value to +. + __m128d mask = _mm_set1_pd (-0.); + VectorizedArray res; + res.data = _mm_andnot_pd(mask, data); + return res; + } + + /** + * Returns the component-wise maximum of this + * field and another one. Not for use in user + * code. Use max(x,y) instead. + */ + VectorizedArray + get_max (const VectorizedArray &other) const + { + VectorizedArray res; + res.data = _mm_max_pd (data, other.data); + return res; + } + + /** + * Returns the component-wise minimum of this + * field and another one. Not for use in user + * code. Use min(x,y) instead. + */ + VectorizedArray + get_min (const VectorizedArray &other) const + { + VectorizedArray res; + res.data = _mm_min_pd (data, other.data); + return res; + } + + /** + * Make a few functions friends. + */ + template friend VectorizedArray + std::sqrt (const VectorizedArray &); + template friend VectorizedArray + std::abs (const VectorizedArray &); + template friend VectorizedArray + std::max (const VectorizedArray&, const VectorizedArray&); + template friend VectorizedArray + std::min (const VectorizedArray&, const VectorizedArray&); +}; + + + + /** + * Specialization for float and SSE2. + */ +template <> +class VectorizedArray +{ +public: + /** + * This gives the number of vectors collected + * in this class. + */ + static const unsigned int n_array_elements = 4; + + /** + * This function can be used to set all data + * fields to a given scalar. + */ + + VectorizedArray& + operator = (const float x) + { + data = _mm_set1_ps(x); + return *this; + } + + /** + * Access operator. + */ + float & + operator [] (const unsigned int comp) + { + AssertIndexRange (comp, 4); + return *(reinterpret_cast(&data)+comp); + } + + /** + * Constant access operator. + */ + const float & + operator [] (const unsigned int comp) const + { + AssertIndexRange (comp, 4); + return *(reinterpret_cast(&data)+comp);; + } + + /** + * Addition. + */ + VectorizedArray & + operator += (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data += vec.data; +#else + data = _mm_add_ps(data,vec.data); +#endif + return *this; + } + + /** + * Subtraction. + */ + VectorizedArray & + operator -= (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data -= vec.data; +#else + data = _mm_sub_ps(data,vec.data); +#endif + return *this; + } + /** + * Multiplication. + */ + VectorizedArray & + operator *= (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data *= vec.data; +#else + data = _mm_mul_ps(data,vec.data); +#endif + return *this; + } + + /** + * Division. + */ + VectorizedArray & + operator /= (const VectorizedArray &vec) + { +#ifdef DEAL_II_COMPILER_USE_VECTOR_ARITHMETICS + data /= vec.data; +#else + data = _mm_div_ps(data,vec.data); +#endif + return *this; + } + + /** + * Actual data field. Since this class + * represents a POD data type, it remains + * public. + */ + __m128 data; + +private: + /** + * Returns the square root of this field. Not + * for use in user code. Use sqrt(x) instead. + */ + VectorizedArray + get_sqrt () const + { + VectorizedArray res; + res.data = _mm_sqrt_ps(data); + return res; + } + + /** + * Returns the absolute value of this + * field. Not for use in user code. Use + * abs(x) instead. + */ + VectorizedArray + get_abs () const + { + // to compute the absolute value, perform + // bitwise andnot with -0. This will leave all + // value and exponent bits unchanged but force + // the sign value to +. + __m128 mask = _mm_set1_ps (-0.f); + VectorizedArray res; + res.data = _mm_andnot_ps(mask, data); + return res; + } + + /** + * Returns the component-wise maximum of this + * field and another one. Not for use in user + * code. Use max(x,y) instead. + */ + VectorizedArray + get_max (const VectorizedArray &other) const + { + VectorizedArray res; + res.data = _mm_max_ps (data, other.data); + return res; + } + + /** + * Returns the component-wise minimum of this + * field and another one. Not for use in user + * code. Use min(x,y) instead. + */ + VectorizedArray + get_min (const VectorizedArray &other) const + { + VectorizedArray res; + res.data = _mm_min_ps (data, other.data); + return res; + } + + /** + * Make a few functions friends. + */ + template friend VectorizedArray + std::sqrt (const VectorizedArray&); + template friend VectorizedArray + std::abs (const VectorizedArray&); + template friend VectorizedArray + std::max (const VectorizedArray&, const VectorizedArray&); + template friend VectorizedArray + std::min (const VectorizedArray&, const VectorizedArray&); +}; + + +#endif // if DEAL_II_COMPILER_VECTORIZATION_LEVEL > 0 + + + /** + * This generic class defines a unified interface to a vectorized data + * type. For general template arguments, this class simply corresponds to + * the template argument. For example, VectorizedArray is + * nothing else but a wrapper around long double with exactly one + * data field of type long double and overloaded arithmetic + * operations. This means that VectorizedArray has + * a similar layout as ComplicatedType, provided that ComplicatedType + * defines basic arithmetic operations. For floats and doubles, an array of + * numbers are packed together, though. The number of elements packed + * together depend on the computer system and compiler flags that are used + * for compilation of deal.II. The fundamental idea of these packed data + * types is to use one single CPU instruction to perform arithmetic + * operations on the whole array using the processor's vector units. Most + * computer systems by 2010 standards will use an array of two doubles and + * four floats, respectively (this corresponds to the SSE/SSE2 data sets) + * when compiling deal.II on 64-bit operating systems. On Intel Sandy Bridge + * processors and newer or AMD Bulldozer processors and newer, four doubles + * and eight floats are used when deal.II is configured e.g. using gcc with + * --with-cpu=native or --with-cpu=corei7-avx. + * + * This behavior of this class is made similar to the basic data types + * double and float. The definition of a vectorized array does not + * initialize the data field but rather leaves it undefined, as is the case + * for double and float. However, when calling something like + * VectorType a = VectorType(), it sets all numbers in this + * field to zero. In other words, this class is a plain old data (POD) type + * which has an equivalent C representation and can e.g. be safely copied + * with std::memcpy. This POD layout is also necessary for ensuring correct + * alignment of data with address boundaries when collected in a vector + * (i.e., when the first element in a vector is properly aligned, all + * subsequent elements will be correctly aligned, too). + * + * Note that for proper functioning of this class, certain data alignment + * rules must be respected. This is because the computer expects the + * starting address of a VectorizedArray field at specific addresses + * in memory (usually, the address of the vectorized array should be a + * multiple of the length of the array in bytes). Otherwise, a segmentation + * fault or a severe loss of performance might occur. When creating a single + * data field on the stack like VectorizedArray a = + * VectorizedArray(), the compiler will take care of data + * alignment automatically. However, when allocating a long vector of + * VectorizedArray data, one needs to respect these rules. Use the + * class AlignedVector for this purpose. It is a class very similar to + * std::vector otherwise but always makes sure that data is correctly + * aligned. + * + * @author Katharina Kormann, Martin Kronbichler, 2010, 2011 + */ +template +class VectorizedArray +{ +public: + /** + * This gives the number of vectors collected + * in this class. + */ + static const unsigned int n_array_elements = 1; + + // POD means that there should be no + // user-defined constructors, destructors and + // copy functions (the standard is somewhat + // relaxed in C++2011, though). + + /** + * This function assigns a scalar to this + * class. + */ + + VectorizedArray& + operator = (const Number scalar) + { + data = scalar; + return *this; + } + + /** + * Access operator (only valid with component + * 0) + */ + Number & + operator [] (const unsigned int comp) + { + AssertIndexRange (comp, 1); + return data; + } + + /** + * Constant access operator (only valid with + * component 0) + */ + const Number & + operator [] (const unsigned int comp) const + { + AssertIndexRange (comp, 1); + return data; + } + + /** + * Addition + */ + VectorizedArray & + operator += (const VectorizedArray &vec) + { + data+=vec.data; + return *this; + } + + /** + * Subtraction + */ + VectorizedArray & + operator -= (const VectorizedArray &vec) + { + data-=vec.data; + return *this; + } + + /** + * Multiplication + */ + VectorizedArray & + operator *= (const VectorizedArray &vec) + { + data*=vec.data; + return *this; + } + + /** + * Division + */ + VectorizedArray & + operator /= (const VectorizedArray &vec) + { + data/=vec.data; + return *this; + } + + /** + * Actual data field. Since this class + * represents a POD data type, it is declared + * public. + */ + Number data; + +private: + /** + * Returns the square root of this field. Not + * for use in user code. Use sqrt(x) instead. + */ + VectorizedArray + get_sqrt () const + { + VectorizedArray res; + res.data = std::sqrt(data); + return res; + } + + /** + * Returns the absolute value of this + * field. Not for use in user code. Use + * abs(x) instead. + */ + VectorizedArray + get_abs () const + { + VectorizedArray res; + res.data = std::fabs(data); + return res; + } + + /** + * Returns the component-wise maximum of this + * field and another one. Not for use in user + * code. Use max(x,y) instead. + */ + VectorizedArray + get_max (const VectorizedArray &other) const + { + VectorizedArray res; + res.data = std::max (data, other.data); + return res; + } + + /** + * Returns the component-wise minimum of this + * field and another one. Not for use in user + * code. Use min(x,y) instead. + */ + VectorizedArray + get_min (const VectorizedArray &other) const + { + VectorizedArray res; + res.data = std::min (data, other.data); + return res; + } + + /** + * Make a few functions friends. + */ + template friend VectorizedArray + std::sqrt (const VectorizedArray&); + template friend VectorizedArray + std::abs (const VectorizedArray&); + template friend VectorizedArray + std::max (const VectorizedArray&, const VectorizedArray&); + template friend VectorizedArray + std::min (const VectorizedArray&, const VectorizedArray&); +}; + +/** + * Create a vectorized array that sets all entries in the array to the given + * scalar. + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +make_vectorized_array (const Number &u) +{ + VectorizedArray result; + result = u; + return result; +} + +/** + * Addition of two vectorized arrays with operator +. + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator + (const VectorizedArray &u, + const VectorizedArray &v) +{ + VectorizedArray tmp = u; + return tmp+=v; +} + +/** + * Subtraction of two vectorized arrays with operator -. + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator - (const VectorizedArray &u, + const VectorizedArray &v) +{ + VectorizedArray tmp = u; + return tmp-=v; +} + +/** + * Multiplication of two vectorized arrays with operator *. + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator * (const VectorizedArray &u, + const VectorizedArray &v) +{ + VectorizedArray tmp = u; + return tmp*=v; +} + +/** + * Division of two vectorized arrays with operator /. + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator / (const VectorizedArray &u, + const VectorizedArray &v) +{ + VectorizedArray tmp = u; + return tmp/=v; +} + +/** + * Addition of a scalar (expanded to a vectorized array with @p + * n_array_elements equal entries) and a vectorized array. + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator + (const Number &u, + const VectorizedArray &v) +{ + VectorizedArray tmp; + tmp = u; + return tmp+=v; +} + +/** + * Addition of a scalar (expanded to a vectorized array with @p + * n_array_elements equal entries) and a vectorized array in case the scalar + * is a double (needed in order to be able to write simple code with constants + * that are usually double numbers). + * + * @relates VectorizedArray + */ +inline +VectorizedArray +operator + (const double &u, + const VectorizedArray &v) +{ + VectorizedArray tmp; + tmp = u; + return tmp+=v; +} + +/** + * Addition of a vectorized array and a scalar (expanded to a vectorized array + * with @p n_array_elements equal entries). + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator + (const VectorizedArray &v, + const Number &u) +{ + return u + v; +} + +/** + * Addition of a vectorized array and a scalar (expanded to a vectorized array + * with @p n_array_elements equal entries) in case the scalar is a double + * (needed in order to be able to write simple code with constants that are + * usually double numbers). + * + * @relates VectorizedArray + */ +inline +VectorizedArray +operator + (const VectorizedArray &v, + const double &u) +{ + return u + v; +} + +/** + * Subtraction of a vectorized array from a scalar (expanded to a vectorized + * array with @p n_array_elements equal entries). + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator - (const Number &u, + const VectorizedArray &v) +{ + VectorizedArray tmp; + tmp = u; + return tmp-=v; +} + +/** + * Subtraction of a vectorized array from a scalar (expanded to a vectorized + * array with @p n_array_elements equal entries) in case the scalar is a + * double (needed in order to be able to write simple code with constants that + * are usually double numbers). + * + * @relates VectorizedArray + */ +inline +VectorizedArray +operator - (const double &u, + const VectorizedArray &v) +{ + VectorizedArray tmp; + tmp = float(u); + return tmp-=v; +} + +/** + * Subtraction of a scalar (expanded to a vectorized array with @p + * n_array_elements equal entries) from a vectorized array. + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator - (const VectorizedArray &v, + const Number &u) +{ + VectorizedArray tmp; + tmp = u; + return v-tmp; +} + +/** + * Subtraction of a scalar (expanded to a vectorized array with @p + * n_array_elements equal entries) from a vectorized array in case the scalar + * is a double (needed in order to be able to write simple code with constants + * that are usually double numbers). + * + * @relates VectorizedArray + */ +inline +VectorizedArray +operator - (const VectorizedArray &v, + const double &u) +{ + VectorizedArray tmp; + tmp = float(u); + return v-tmp; +} + +/** + * Multiplication of a scalar (expanded to a vectorized array with @p + * n_array_elements equal entries) and a vectorized array. + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator * (const Number &u, + const VectorizedArray &v) +{ + VectorizedArray tmp; + tmp = u; + return tmp*=v; +} + +/** + * Multiplication of a scalar (expanded to a vectorized array with @p + * n_array_elements equal entries) and a vectorized array in case the scalar + * is a double (needed in order to be able to write simple code with constants + * that are usually double numbers). + * + * @relates VectorizedArray + */ +inline +VectorizedArray +operator * (const double &u, + const VectorizedArray &v) +{ + VectorizedArray tmp; + tmp = float(u); + return tmp*=v; +} + +/** + * Multiplication of a vectorized array and a scalar (expanded to a vectorized + * array with @p n_array_elements equal entries). + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator * (const VectorizedArray &v, + const Number &u) +{ + return u * v; +} + +/** + * Multiplication of a vectorized array and a scalar (expanded to a vectorized + * array with @p n_array_elements equal entries) in case the scalar is a + * double (needed in order to be able to write simple code with constants that + * are usually double numbers). + * + * @relates VectorizedArray + */ +inline +VectorizedArray +operator * (const VectorizedArray &v, + const double &u) +{ + return u * v; +} + +/** + * Quotient between a scalar (expanded to a vectorized array with @p + * n_array_elements equal entries) and a vectorized array. + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator / (const Number &u, + const VectorizedArray &v) +{ + VectorizedArray tmp; + tmp = u; + return tmp/=v; +} + +/** + * Quotient between a scalar (expanded to a vectorized array with @p + * n_array_elements equal entries) and a vectorized array in case the scalar + * is a double (needed in order to be able to write simple code with constants + * that are usually double numbers). + * + * @relates VectorizedArray + */ +inline +VectorizedArray +operator / (const double &u, + const VectorizedArray &v) +{ + VectorizedArray tmp; + tmp = float(u); + return tmp/=v; +} + +/** + * Quotient between a vectorized array and a scalar (expanded to a vectorized + * array with @p n_array_elements equal entries). + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator / (const VectorizedArray &v, + const Number &u) +{ + VectorizedArray tmp; + tmp = u; + return v/tmp; +} + +/** + * Quotient between a vectorized array and a scalar (expanded to a vectorized + * array with @p n_array_elements equal entries) in case the scalar is a + * double (needed in order to be able to write simple code with constants that + * are usually double numbers). + * + * @relates VectorizedArray + */ +inline +VectorizedArray +operator / (const VectorizedArray &v, + const double &u) +{ + VectorizedArray tmp; + tmp = float(u); + return v/tmp; +} + +/** + * Unary operator + on a vectorized array. + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator + (const VectorizedArray &u) +{ + return u; +} + +/** + * Unary operator - on a vectorized array. + * + * @relates VectorizedArray + */ +template +inline +VectorizedArray +operator - (const VectorizedArray &u) +{ + // to get a negative sign, subtract the input from zero (could also + // multiply by -1, but this one is slightly simpler) + return VectorizedArray()-u; +} + + + +/** + * This namespace defines the copy and set functions used in + * AlignedVector. These functions operate in parallel when there are enough + * elements in the vector. + */ +namespace internal +{ + /** + * Move and class that actually issues the copy commands in + * AlignedVector. This class is based on the specialized for loop base class + * ParallelForLoop in parallel.h whose purpose is the following: When + * calling a parallel for loop on AlignedVector with apply_to_subranges, it + * generates different code for every different argument we might choose (as + * it is templated). This gives a lot of code (e.g. it triples the memory + * required for compiling the file matrix_free.cc and the final object size + * is several times larger) which is completely useless. Therefore, this + * class channels all copy commands through one call to apply_to_subrange + * for all possible types, which makes the copy operation much cleaner + * (thanks to a virtual function, whose cost is negligible in this context). + * + * @relates AlignedVector + */ + template + class AlignedVectorMove : private parallel::ParallelForInteger + { + static const std::size_t minimum_parallel_grain_size = 160000/sizeof(T)+1; + public: + /** + * Constructor. Issues a parallel call if + * there are sufficiently many elements, + * otherwise work in serial. Copies the data + * from source to destination and then calls + * destructor on the source. If the optional + * argument is set to true, the source is left + * untouched instead. + */ + AlignedVectorMove (T* source_begin, + T* source_end, + T* destination, + bool copy_only = false) + : + source_ (source_begin), + destination_ (destination), + copy_only_ (copy_only) + { + Assert (source_end >= source_begin, ExcInternalError()); + const std::size_t size = source_end - source_begin; + if (size < minimum_parallel_grain_size) + apply_to_subrange (0, size); + else + apply_parallel (0, size, minimum_parallel_grain_size); + } + + /** + * This method moves elements from the source + * to the destination given in the constructor + * on a subrange given by two integers. + */ + virtual void apply_to_subrange (const std::size_t begin, + const std::size_t end) const + { + // for classes trivial assignment can use + // memcpy + if (std_cxx1x::is_trivial::value == true) + std::memcpy (destination_+begin, source_+begin, (end-begin)*sizeof(T)); + else + if (copy_only_ == false) + for (std::size_t i=begin; i + class AlignedVectorSet : private parallel::ParallelForInteger + { + static const std::size_t minimum_parallel_grain_size = 160000/sizeof(T)+1; + public: + /** + * Constructor. Issues a parallel call if + * there are sufficiently many elements, + * otherwise work in serial. + */ + AlignedVectorSet (const std::size_t size, + const T& element, + T* destination) + : + element_ (element), + destination_ (destination), + trivial_element (false) + { + if (size == 0) + return; + + if (std_cxx1x::is_trivial::value == true) + { + const unsigned char zero [sizeof(T)] = {}; + if (std::memcmp(zero, &element, sizeof(T)) == 0) + trivial_element = true; + } + if (size < minimum_parallel_grain_size) + apply_to_subrange (0, size); + else + apply_parallel (0, size, minimum_parallel_grain_size); + } + + private: + + /** + * This sets elements on a subrange given by + * two integers. + */ + virtual void apply_to_subrange (const std::size_t begin, + const std::size_t end) const + { + // for classes with trivial assignment of zero + // can use memset + if (std_cxx1x::is_trivial::value == true && trivial_element) + std::memset (destination_+begin, 0, (end-begin)*sizeof(T)); + else + for (std::size_t i=begin; i +class AlignedVector +{ + public: + /** + * Declare standard types used in all + * containers. These types parallel those + * in the C++ standard libraries + * vector<...> class. + */ + typedef T value_type; + typedef value_type *pointer; + typedef const value_type *const_pointer; + typedef value_type *iterator; + typedef const value_type *const_iterator; + typedef value_type &reference; + typedef const value_type &const_reference; + typedef std::size_t size_type; + + /** + * Empty constructor. Sets the vector size to + * zero. + */ + AlignedVector () + : + _data (0), + _end_data (0), + _end_allocated (0) + {}; + + /** + * Sets the vector size to the given size and + * initializes all elements with T(). + */ + AlignedVector (const size_type size) + : + _data (0), + _end_data (0), + _end_allocated (0) + { + if (size > 0) + resize (size); + } + + /** + * Destructor. + */ + ~AlignedVector () + { + clear(); + } + + /** + * Copy constructor. + */ + AlignedVector (const AlignedVector &vec) + : + _data (0), + _end_data (0), + _end_allocated (0) + { + // do not invalidate old data + resize_fast (vec._end_data - vec._data); + internal::AlignedVectorMove (vec._data, vec._end_data, _data, true); + } + + /** + * Assignment to the input vector @p vec. + */ + AlignedVector & + operator = (const AlignedVector &vec) + { + clear(); + resize_fast (vec._end_data - vec._data); + internal::AlignedVectorMove (vec._data, vec._end_data, _data, true); + return *this; + } + + /** + * Change the size of the vector. It keeps old + * elements previously available but does not + * initialize the newly allocated memory, + * leaving it in an undefined state. + */ + void resize_fast (const size_type size) + { + reserve (size); + _end_data = _data + size; + } + + /** + * Change the size of the vector. It keeps old + * elements previously available, and + * initializes each element with the specified + * data. If the new vector size is shorter + * than the old one, the memory is not + * released unless the new size is zero. + */ + void resize (const size_type size_in, + const T &init = T()) + { + const size_type old_size = size(); + if (std_cxx1x::is_trivial::value == false && size_in < old_size) + { + // call destructor on fields that are released + while (_end_data != _data+size_in) + (--_end_data)->~T(); + } + + resize_fast (size_in); + // now _size is set correctly, need to set the + // values + if (size_in > old_size) + internal::AlignedVectorSet (size_in-old_size, init, + _data+old_size); + } + + /** + * Reserve memory space for @p size + * elements. If the argument @p size is set to + * zero, all previously allocated memory is + * released. + * + * In order to avoid too frequent reallocation + * (which involves copy of the data), this + * function doubles the amount of memory + * occupied when the given size is larger than + * the previously allocated size. + */ + void reserve (const size_type size_alloc) + { + const size_type old_size = _end_data - _data; + const size_type allocated_size = _end_allocated - _data; + if (size_alloc > allocated_size) + { + // if we continuously increase the size of the + // vector, we might be reallocating a lot of + // times. therefore, try to increase the size + // more aggressively + size_type new_size = size_alloc; + if (size_alloc < (2 * allocated_size)) + new_size = 2 * allocated_size; + + const size_type size_actual_allocate = new_size * sizeof(T); + +#if DEAL_II_COMPILER_VECTORIZATION_LEVEL > 0 + + // allocate and align along boundaries of the + // size of VectorizedArray, which is + // 16 bytes for SSE and 32 bytes for AVX + T* new_data = static_cast(_mm_malloc (size_actual_allocate, + sizeof(VectorizedArray))); +#else + T * new_data = static_cast(malloc (size_actual_allocate)); +#endif + if (new_data == 0) + throw std::bad_alloc(); + + // copy data in case there was some content + // before and release the old memory with the + // function corresponding to the one used for + // allocating + std::swap (_data, new_data); + _end_data = _data + old_size; + _end_allocated = _data + new_size; + if (_end_data != _data) + { + internal::AlignedVectorMove(new_data, new_data + old_size, + _data); +#if DEAL_II_COMPILER_VECTORIZATION_LEVEL > 0 + _mm_free(new_data); +#else + free(new_data); +#endif + } + } + else if (size_alloc == 0) + clear(); + } + + /** + * Releases all previously allocated memory + * and leaves the vector in a state equivalent + * to the state after the default constructor + * has been called. + */ + void clear () + { + if (_data != 0) + { + if (std_cxx1x::is_trivial::value == false) + while (_end_data != _data) + (--_end_data)->~T(); + +#if DEAL_II_COMPILER_VECTORIZATION_LEVEL > 0 + _mm_free(_data); +#else + free(_data); +#endif + } + _data = 0; + _end_data = 0; + _end_allocated = 0; + }; + + /** + * Inserts an element at the end of the + * vector, increasing the vector size by + * one. Note that the allocated size will + * double whenever the previous space is not + * enough to hold the new element. + */ + void push_back (const T in_data) + { + Assert (_end_data <= _end_allocated, ExcInternalError()); + if (_end_data == _end_allocated) + reserve (std::max(2*capacity(),static_cast(16))); + if (std_cxx1x::is_trivial::value == false) + new (_end_data) T; + *_end_data++ = in_data; + } + + /** + * Returns the last element of the vector + * (read and write access). + */ + reference back () + { + AssertIndexRange (0, size()); + T* field = _end_data - 1; + return *field; + } + + /** + * Returns the last element of the vector + * (read-only access). + */ + const_reference back () const + { + AssertIndexRange (0, size()); + const T* field = _end_data - 1; + return *field; + } + + /** + * Inserts several elements at the end of the + * vector given by a range of elements. + */ + template + void insert_back (ForwardIterator begin, + ForwardIterator end) + { + const unsigned int old_size = size(); + reserve (old_size + (end-begin)); + for ( ; begin != end; ++begin, ++_end_data) + { + if (std_cxx1x::is_trivial::value == false) + new (_end_data) T; + *_end_data = *begin; + } + } + + /** + * Swaps the given vector with the calling + * vector. + */ + void swap (AlignedVector &vec) + { + std::swap (_data, vec._data); + std::swap (_end_data, vec._end_data); + std::swap (_end_allocated, vec._end_allocated); + } + + /** + * Returns the size of the vector. + */ + size_type size () const + { + return _end_data - _data; + } + + /** + * Returns the capacity of the vector, i.e., + * the size this vector can hold without + * reallocation. Note that capacity() >= + * size(). + */ + size_type capacity () const + { + return _end_allocated - _data; + } + + /** + * Read-write access to entry @p index in the + * vector. + */ + reference + operator [] (const size_type index) + { + AssertIndexRange (index, size()); + return _data[index]; + }; + + /** + * Read-only access to entry @p index in the + * vector. + */ + const_reference operator [] (const size_type index) const + { + AssertIndexRange (index, size()); + return _data[index]; + }; + + /** + * Returns a read and write pointer to the + * beginning of the data array. + */ + iterator begin () + { + return _data; + } + + /** + * Returns a read and write pointer to the + * end of the data array. + */ + iterator end () + { + return _end_data; + } + + /** + * Returns a read-only pointer to the + * beginning of the data array. + */ + const_iterator begin () const + { + return _data; + } + + /** + * Returns a read-only pointer to the + * end of the data array. + */ + const_iterator end () const + { + return _end_data; + } + + /** + * Returns the memory consumption of the + * allocated memory in this class. If the + * underlying type @p T allocates memory by + * itself, this memory is not counted. + */ + size_type memory_consumption () const + { + size_type memory = sizeof(this); + memory += sizeof(T) * capacity(); + return memory; + } + + private: + + /** + * Pointer to actual class data. + */ + T* _data; + + /** + * Pointer to the end of valid data fields. + */ + T* _end_data; + + /** + * Pointer to the end of the allocated memory. + */ + T* _end_allocated; +}; + + +DEAL_II_NAMESPACE_CLOSE + + +/** + * Implementation of functions from cmath on VectorizedArray. These functions + * do not reside in the dealii namespace in order to ensure a similar + * interface as for the respective functions in cmath. Instead, call them + * using std::sin. + */ +namespace std +{ + /** + * Computes the sine of a vectorized + * data field. The result is return as + * vectorized array in the form + * {sin(x[0]), sin(x[1]), ..., + * sin(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ + template + inline + ::dealii::VectorizedArray + sin (const ::dealii::VectorizedArray &x) + { + ::dealii::VectorizedArray sin_val; + for (unsigned int i=0; i::n_array_elements; ++i) + sin_val[i] = std::sin(x[i]); + return sin_val; + } + + + + /** + * Computes the tangent of a vectorized + * data field. The result is return as + * vectorized array in the form + * {tan(x[0]), tan(x[1]), ..., + * tan(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ + template + inline + ::dealii::VectorizedArray + tan (const ::dealii::VectorizedArray &x) + { + ::dealii::VectorizedArray tan_val; + for (unsigned int i=0; i::n_array_elements; ++i) + tan_val[i] = std::tan(x[i]); + return tan_val; + } + + + /** + * Computes the cosine of a vectorized + * data field. The result is return as + * vectorized array in the form + * {cos(x[0]), cos(x[1]), ..., + * cos(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ + template + inline + ::dealii::VectorizedArray + cos (const ::dealii::VectorizedArray &x) + { + ::dealii::VectorizedArray cos_val; + for (unsigned int i=0; i::n_array_elements; ++i) + cos_val[i] = std::cos(x[i]); + return cos_val; + } + + + /** + * Computes the exponential of a vectorized + * data field. The result is return as + * vectorized array in the form + * {exp(x[0]), exp(x[1]), ..., + * exp(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ + template + inline + ::dealii::VectorizedArray + exp (const ::dealii::VectorizedArray &x) + { + ::dealii::VectorizedArray exp_val; + for (unsigned int i=0; i::n_array_elements; ++i) + exp_val[i] = std::exp(x[i]); + return exp_val; + } + + + /** + * Computes the natural logarithm of a + * vectorized data field. The result is return + * as vectorized array in the form + * {log(x[0]), log(x[1]), ..., + * log(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ + template + inline + ::dealii::VectorizedArray + log (const ::dealii::VectorizedArray &x) + { + ::dealii::VectorizedArray log_val; + for (unsigned int i=0; i::n_array_elements; ++i) + log_val[i] = std::log(x[i]); + return log_val; + } + + + + /** + * Computes the square root of a vectorized + * data field. The result is return as + * vectorized array in the form + * {sqrt(x[0]), sqrt(x[1]), ..., + * sqrt(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ + template + inline + ::dealii::VectorizedArray + sqrt (const ::dealii::VectorizedArray &x) + { + return x.get_sqrt(); + } + + + + /** + * Computes the absolute value (modulus) of a + * vectorized data field. The result is return + * as vectorized array in the form + * {abs(x[0]), abs(x[1]), ..., + * abs(x[n_array_elements-1])}. + * + * @relates VectorizedArray + */ + template + inline + ::dealii::VectorizedArray + abs (const ::dealii::VectorizedArray &x) + { + return x.get_abs(); + } + + + + /** + * Computes the componentwise maximum of two + * vectorized data fields. The result is + * return as vectorized array in the form + * {max(x[0],y[0]), max(x[1],y[1]), + * ...}. + * + * @relates VectorizedArray + */ + template + inline + ::dealii::VectorizedArray + max (const ::dealii::VectorizedArray &x, + const ::dealii::VectorizedArray &y) + { + return x.get_max(y); + } + + + + /** + * Computes the componentwise minimum of two + * vectorized data fields. The result is + * return as vectorized array in the form + * {min(x[0],y[0]), min(x[1],y[1]), + * ...}. + * + * @relates VectorizedArray + */ + template + inline + ::dealii::VectorizedArray + min (const ::dealii::VectorizedArray &x, + const ::dealii::VectorizedArray &y) + { + return x.get_min(y); + } + +} + +#endif diff --git a/deal.II/include/deal.II/lac/full_matrix.h b/deal.II/include/deal.II/lac/full_matrix.h index ce5d4907de..bf547473f3 100644 --- a/deal.II/include/deal.II/lac/full_matrix.h +++ b/deal.II/include/deal.II/lac/full_matrix.h @@ -926,6 +926,30 @@ class FullMatrix : public Table<2,number> */ void add(const unsigned int row, const unsigned int column, 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 full matrix. This + * function is present for + * compatibility with the various + * sparse matrices in deal.II. In + * particular, the two boolean fields + * @p elide_zero_values and @p + * col_indices_are_sorted do not impact + * the performance of this routine, as + * opposed to the sparse matrix case + * and are indeed ignored in the + * implementation. + */ + template + void add (const unsigned int row, + const unsigned int n_cols, + const unsigned int *col_indices, + const number2 *values, + const bool elide_zero_values = true, + const bool col_indices_are_sorted = false); + /** * A(i,1...n) += * s*A(j,1...n). Simple @@ -1776,6 +1800,27 @@ FullMatrix::add (const unsigned int r, const unsigned int c, const numbe } + +template +template +inline +void +FullMatrix::add (const unsigned int row, + const unsigned int n_cols, + const unsigned int *col_indices, + const number2 *values, + const bool, + const bool) +{ + AssertIndexRange(row, this->m()); + for (unsigned int col=0; coln()); + this->operator()(row,col_indices[col]) += values[col]; + } +} + + template 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 8edc9b91d1..4e8650b300 100644 --- a/deal.II/include/deal.II/lac/parallel_vector.h +++ b/deal.II/include/deal.II/lac/parallel_vector.h @@ -1495,7 +1495,8 @@ namespace parallel { // if we call Vector::operator=0, we want to // zero out all the entries plus ghosts. - vector_view.dealii::template Vector::operator= (s); + if (partitioner->local_size() > 0) + vector_view.dealii::template Vector::operator= (s); if (s==Number()) zero_out_ghosts(); @@ -1882,23 +1883,6 @@ namespace parallel - template - inline - void - Vector::swap (Vector &v) - { - std::swap (allocated_size, v.allocated_size); - std::swap (val, v.val); - std::swap (import_data, v.import_data); - std::swap (vector_view, v.vector_view); -#ifdef DEAL_II_COMPILER_SUPPROTS_MPI - std::swap (compress_requests, v.compress_requests); - std::swap (update_ghost_values_requests, v.update_ghost_values_requests); -#endif - } - - - 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 75cbb2b559..82e7d1fc72 100644 --- a/deal.II/include/deal.II/lac/parallel_vector.templates.h +++ b/deal.II/include/deal.II/lac/parallel_vector.templates.h @@ -464,6 +464,39 @@ namespace parallel + template + void + Vector::swap (Vector &v) + { +#ifdef DEAL_II_COMPILER_SUPPORTS_MPI + // introduce a Barrier over all MPI processes + // to make sure that the compress request are + // no longer used before changing the owner + if (v.partitioner->n_mpi_processes() > 1) + MPI_Barrier (v.partitioner->get_communicator()); + if (partitioner->n_mpi_processes() > 1 && + v.partitioner->n_mpi_processes() != + partitioner->n_mpi_processes()) + MPI_Barrier (partitioner->get_communicator()); + + std::swap (compress_requests, v.compress_requests); + std::swap (update_ghost_values_requests, v.update_ghost_values_requests); +#endif + + std::swap (partitioner, v.partitioner); + std::swap (allocated_size, v.allocated_size); + std::swap (val, v.val); + std::swap (import_data, v.import_data); + + // vector view cannot be swapped so reset it + // manually (without touching the vector + // elements) + vector_view.reinit (partitioner->local_size(), val); + v.vector_view.reinit (v.partitioner->local_size(), v.val); + } + + + 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 new file mode 100644 index 0000000000..b5d78e96cb --- /dev/null +++ b/deal.II/include/deal.II/matrix_free/dof_info.h @@ -0,0 +1,584 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2011 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__matrix_free_dof_info_h +#define __deal2__matrix_free_dof_info_h + + +#include +#include +#include +#include +#include +#include + +#include + + +DEAL_II_NAMESPACE_OPEN + +namespace internal +{ +namespace MatrixFreeFunctions +{ + /** + * The class that stores the indices of the degrees of freedom for all the + * cells. Essentially, this is a smart number cache in the style of a + * DoFHandler that also embeds the description of constraints directly on + * the cell level without the need to refer to the external + * ConstraintMatrix. + * + * This class only stores index relations. The weights for hanging node + * constraints are stored in a different field. This is because a different + * field allows for the same compressed weight data on different DoFHandlers + * for vector-valued problems. There, the indices might be constrained + * differently on different components (e.g. Dirichlet conditions only on + * selected components), whereas the weights from hanging nodes are the same + * and need to be stored only once. The combination will be handled in the + * MatrixFree class. + * + * @author Katharina Kormann and Martin Kronbichler, 2010, 2011 + */ + struct DoFInfo + { + /** + * Default empty constructor. + */ + DoFInfo (); + + /** + * Copy constructor. + */ + DoFInfo (const DoFInfo &dof_info); + + /** + * Clears all data fields in this class. + */ + void clear (); + + /** + * Returns a pointer to the first index in the + * DoF row @p row. + */ + const unsigned int * begin_indices (const unsigned int row) const; + + /** + * Returns a pointer to the one past the last + * DoF index in the row @p row. + */ + const unsigned int * end_indices (const unsigned int row) const; + + /** + * Returns the number of entries in the + * indices field for the given row. + */ + unsigned int row_length_indices (const unsigned int row) const; + + /** + * Returns a pointer to the first constraint + * indicator in the row @p row. + */ + const std::pair * + begin_indicators (const unsigned int row) const; + + /** + * Returns a pointer to the one past the last + * constraint indicator in the row @p row. + */ + const std::pair * + end_indicators (const unsigned int row) const; + + /** + * Returns the number of entries in the + * constraint indicator field for the given + * row. + */ + unsigned int row_length_indicators (const unsigned int row) const; + + /** + * Returns a pointer to the first index in the + * DoF row @p row for plain indices (i.e., the + * entries where constraints are not + * embedded). + */ + const unsigned int * begin_indices_plain (const unsigned int row) const; + + /** + * Returns a pointer to the one past the last + * DoF index in the row @p row (i.e., the + * entries where constraints are not + * embedded). + */ + const unsigned int * end_indices_plain (const unsigned int row) const; + + /** + * Returns the FE index for a given finite + * element degree. If not in hp mode, this + * function always returns index 0. If an + * index is not found in hp mode, it returns + * max_fe_degree, i.e., one index past the + * last valid one. + */ + unsigned int fe_index_from_degree (const unsigned int fe_degree) const; + + + /** + * Returns the FE index for a given finite + * element degree. If not in hp mode or if the + * index is not found, this function always + * returns index 0. Hence, this function does + * not check whether the given degree is + * actually present. + */ + unsigned int + fe_index_from_dofs_per_cell (const unsigned int dofs_per_cell) const; + + /** + * This internal method takes the local + * indices on a cell and fills them into this + * class. It resolves the constraints and + * distributes the results. Ghost indices, + * i.e., indices that are located on another + * processor, get a temporary number by this + * function, and will later be assigned the + * correct index after all the ghost indices + * have been collected by the call to @p + * assign_ghosts. + */ + void read_dof_indices (const std::vector &local_indices, + const std::vector &lexicographic_inv, + const ConstraintMatrix &constraints, + const unsigned int cell_number, + internal::ConstraintValues&constraint_values, + bool &cell_at_boundary); + + /** + * This method assigns the correct indices to + * ghost indices from the temporary numbering + * employed by the @p read_dof_indices + * function. The numbers are localized with + * respect to the MPI process, and ghosts + * start at the end of the locally owned + * range. This way, we get direct access to + * all vector entries. + */ + void assign_ghosts(const std::vector &boundary_cells); + + /** + * This method reorders the way cells are gone + * through based on a given renumbering of the + * cells. It also takes @p n_vectors cells + * together and interprets them as one cell + * only, as is needed for vectorization. + */ + void reorder_cells (const SizeInfo &size_info, + const std::vector &renumbering, + const std::vector &constraint_pool_row_index, + const std::vector &irregular_cells, + const unsigned int n_vectors); + + /** + * This helper function determines a block + * size if the user decided not to force a + * block size through + * MatrixFree::AdditionalData. This is + * computed based on the number of hardware + * threads on the system + * (multithread_info.n_default_threads) and + * the number of macro cells that we should + * work on. + */ + void guess_block_size (const SizeInfo &size_info, + TaskInfo &task_info); + + /** + * This method goes through all cells that + * have been filled into @p dof_indices and + * finds out which cells can be worked on + * independently and which ones are + * neighboring and need to be done at + * different times when used in parallel. + * + * The strategy is based on a two-level + * approach. The outer level is subdivided + * into partitions similar to the type of + * neighbors in Cuthill-McKee, and the inner + * level is subdivided via colors (for chunks + * within the same color, can work + * independently). One task is represented by + * a chunk of cells. The cell chunks are + * formed before subdivision into partitions + * and colors. + */ + void + make_thread_graph_partition_color (SizeInfo &size_info, + TaskInfo &task_info, + std::vector &renumbering, + std::vector &irregular_cells, + const bool hp_bool); + + /** + * This function goes through all cells that + * have been filled into @p dof_indices and + * finds out which cells can be worked on + * independently and which ones are + * neighboring and need to be done at + * different times when used in parallel. + * + * The strategy is based on a two-level + * approach. The outer level is subdivided + * into partitions similar to the type of + * neighbors in Cuthill-McKee, and the inner + * level is again subdivided into + * Cuthill-McKee-like partitions (partitions + * whose level differs by more than 2 can be + * worked on independently). One task is + * represented by a chunk of cells. The cell + * chunks are formed after subdivision into + * the two levels of partitions. + */ + void + make_thread_graph_partition_partition (SizeInfo &size_info, + TaskInfo &task_info, + std::vector &renumbering, + std::vector &irregular_cells, + const bool hp_bool); + + /** + * This function computes the connectivity of + * the currently stored indices and fills the + * structure into a sparsity pattern. The + * parameter block_size can be used to specify + * whether several cells should be treated as + * one. + */ + void + make_connectivity_graph (const SizeInfo &size_info, + const TaskInfo &task_info, + const std::vector &renumbering, + const std::vector &irregular_cells, + const bool do_blocking, + CompressedSimpleSparsityPattern &connectivity) const; + + /** + * Renumbers the degrees of freedom to give + * good access for this class. + */ + void renumber_dofs (std::vector &renumbering); + + /** + * Returns the memory consumption in bytes of + * this class. + */ + std::size_t memory_consumption() const; + + /** + * Prints a detailed summary of memory + * consumption in the different structures of + * this class to the given output stream. + */ + template + void print_memory_consumption(STREAM &out, + const SizeInfo &size_info) const; + + /** + * Returns the memory consumption in bytes of + * this class. + */ + template + void print (const CompressedMatrix &constraint_pool, + std::ostream &out) const; + + /** + * Stores the rowstart indices of the + * compressed row storage in the @p + * dof_indices and @p constraint_indicator + * fields. These two fields are always + * accessed together, so it is simpler to keep + * just one variable for them. This also + * obviates keeping two rowstart vectors in + * synch. + * + * In addition, the third field stores whether + * a particular cell has a certain structure + * in the indices, like indices for + * vector-valued problems or for cells where + * not all vector components are filled. + */ + std::vector > row_starts; + + /** + * Stores the indices of the degrees of + * freedom for each cell. This array also + * includes the indirect contributions from + * constraints, which are described by the @p + * constraint_indicator field. Because of + * variable lengths of rows, this would be a + * vector of a vector. However, we use one + * contiguous memory region and store the + * rowstart in the variable @p row_starts. + */ + std::vector dof_indices; + + /** + * This variable describes the position of + * constraints in terms of the local numbering + * of degrees of freedom on a cell. The first + * number stores the distance from one + * constrained degree of freedom to the + * next. This allows to identify the position + * of constrained DoFs as we loop through the + * local degrees of freedom of the cell when + * reading from or writing to a vector. The + * second number stores the index of the + * constraint weights, stored in the variable + * constraint_pool. + */ + std::vector > constraint_indicator; + + /** + * This stores the parallel partitioning that + * can be used to set up vectors. The + * partitioner includes the description of the + * local range in the vector, and also + * includes how the ghosts look like. This + * enables initialization of vectors based on + * the DoFInfo field. + */ + std_cxx1x::shared_ptr vector_partitioner; + + /** + * This stores a (sorted) list of all locally + * owned degrees of freedom that are + * constrained. + */ + std::vector constrained_dofs; + + /** + * Stores the rowstart indices of the + * compressed row storage in the @p + * dof_indices_plain + * fields. + */ + std::vector row_starts_plain_indices; + + /** + * Stores the indices of the degrees of + * freedom for each cell. This array does not + * include the indirect contributions from + * constraints, which are included in @p + * dof_indices. Because of variable lengths of + * rows, this would be a vector of a + * vector. However, we use one contiguous + * memory region and store the rowstart in the + * variable @p row_starts_plain_indices. + */ + std::vector plain_dof_indices; + + /** + * Stores the number of components in the + * DoFHandler where the indices have been read + * from. + */ + unsigned int n_components; + + /** + * Stores the number of degrees of freedom per + * cell. + */ + std::vector dofs_per_cell; + + /** + * Stores the number of degrees of freedom per + * face. + */ + std::vector dofs_per_face; + + /** + * Informs on whether plain indices are + * cached. + */ + bool store_plain_indices; + + /** + * Stores the index of the active + * finite element in the hp case. + */ + std::vector cell_active_fe_index; + + /** + * Stores the maximum degree of + * different finite elements for the + * hp case. + */ + unsigned int max_fe_index; + + /** + * This variable stores the dofs per cell and + * the finite element degree associated for + * all fe indices in the underlying element + * for easier access to data in the hp case. + */ + std::vector > fe_index_conversion; + + /** + * Temporarily stores the numbers of ghosts + * during setup. Cleared when calling @p + * assign_ghosts. Then, all information is + * collected by the partitioner. + */ + std::vector ghost_dofs; + }; + + +/*----------------------- Inline functions ----------------------------------*/ + +#ifndef DOXYGEN + + inline + const unsigned int * + DoFInfo::begin_indices (const unsigned int row) const + { + AssertIndexRange (row, row_starts.size()-1); + const unsigned int index = std_cxx1x::get<0>(row_starts[row]); + AssertIndexRange(index, dof_indices.size()+1); + return &dof_indices[index]; + } + + + + inline + const unsigned int * + DoFInfo::end_indices (const unsigned int row) const + { + AssertIndexRange (row, row_starts.size()-1); + const unsigned int index = std_cxx1x::get<0>(row_starts[row+1]); + AssertIndexRange(index, dof_indices.size()+1); + return &dof_indices[0] + index; + } + + + + inline + unsigned int + DoFInfo::row_length_indices (const unsigned int row) const + { + AssertIndexRange (row, row_starts.size()-1); + return (std_cxx1x::get<0>(row_starts[row+1]) - + std_cxx1x::get<0>(row_starts[row])); + } + + + + inline + const std::pair * + DoFInfo::begin_indicators (const unsigned int row) const + { + AssertIndexRange (row, row_starts.size()-1); + const unsigned int index = std_cxx1x::get<1>(row_starts[row]); + AssertIndexRange (index, constraint_indicator.size()+1); + return &constraint_indicator[index]; + } + + + + inline + const std::pair * + DoFInfo::end_indicators (const unsigned int row) const + { + AssertIndexRange (row, row_starts.size()-1); + const unsigned int index = std_cxx1x::get<1>(row_starts[row+1]); + AssertIndexRange (index, constraint_indicator.size()+1); + return &constraint_indicator[0] + index; + } + + + + inline + unsigned int + DoFInfo::row_length_indicators (const unsigned int row) const + { + AssertIndexRange (row, row_starts.size()-1); + return (std_cxx1x::get<1>(row_starts[row+1]) - + std_cxx1x::get<1>(row_starts[row])); + } + + + + inline + const unsigned int * + DoFInfo::begin_indices_plain (const unsigned int row) const + { + // if we have no constraints, should take the + // data from dof_indices + if (row_length_indicators(row) == 0) + { + Assert (row_starts_plain_indices[row] == numbers::invalid_unsigned_int, + ExcInternalError()); + return begin_indices(row); + } + else + { + AssertDimension (row_starts.size(), row_starts_plain_indices.size()); + const unsigned int index = row_starts_plain_indices[row]; + AssertIndexRange(index, plain_dof_indices.size()+1); + return &plain_dof_indices[index]; + } + } + + + + inline + const unsigned int * + DoFInfo::end_indices_plain (const unsigned int row) const + { + return begin_indices_plain(row) + + dofs_per_cell[(cell_active_fe_index.size()==0)? + 0:cell_active_fe_index[row]]; + } + + + + inline + unsigned int + DoFInfo::fe_index_from_degree (const unsigned int fe_degree) const + { + const unsigned int n_indices = fe_index_conversion.size(); + for (unsigned int i=0; i +#include +#include +#include +#include + +DEAL_II_NAMESPACE_OPEN + +namespace internal +{ +namespace MatrixFreeFunctions +{ + namespace internal + { + /** + * A struct that takes entries describing + * a constraint and puts them into a + * sorted list where duplicates are + * filtered out + */ + template + struct ConstraintValues + { + ConstraintValues(); + + /** + * This function inserts some + * constrained entries to the + * collection of all + * values. It stores the + * (reordered) numbering of + * the dofs (according to the + * ordering that matches with + * the function) in + * new_indices, and returns + * the storage position the + * double array for access + * later on. + */ + unsigned short + insert_entries (const std::vector > &entries); + + CompressedMatrix constraint_pool; + std::vector > pool_locations; + std::vector > constraint_entries; + std::vector constraint_indices; + std::vector one_constraint; + HashValue hashes; + }; + + + template + ConstraintValues::ConstraintValues () + : + hashes (1.) + { + constraint_pool.row_index.push_back (0); + } + + template + unsigned short + ConstraintValues:: + insert_entries (const std::vector > &entries) + { + unsigned int insert_position = deal_II_numbers::invalid_unsigned_int; + + typedef std::vector >::iterator iter; + constraint_entries.resize(entries.size()); + one_constraint.resize(entries.size()); + constraint_indices.resize(entries.size()); + for (unsigned int j=0;j test (hash_val, 0); + + // Try to find a constraint in the pool with + // the same hash value. + iter pos = std::lower_bound (pool_locations.begin(), + pool_locations.end(), + test); + + // If constraint has to be added, which will + // be its no. + test.second = constraint_pool.row_index.size()-1; + + // Hash value larger than all the ones + // before. We need to add it. + if (pos == pool_locations.end()) + goto insert; + + // A constraint in the pool with the same hash + // value identified. + else if (pos->first == test.first) + { + bool is_same = true; + while(is_same == true) + { + if(one_constraint.size()!= + (constraint_pool.row_length(pos->second))) + // The constraints have different length, and + // hence different. + is_same = false; + else + for (unsigned int q=0; qsecond]+q]- + one_constraint[q])>hashes.scaling) + { + is_same = false; + break; + } + if (is_same == false) + { + // Try if there is another constraint with the + // same hash value. + ++pos; + if (pos != pool_locations.end() && pos->first == test.first) + is_same = true; + else + goto insert; + } + else + { + // The constraint is the same as the + // (pos->second)th in the pool. Add the + // location of constraint in pool + insert_position = pos->second; + break; + } + } + } + else + { + // A new constraint has been identified. It + // needs to be added (at the position + // test->second in pool_locations). + insert: + pool_locations.insert (pos, test); + + // Remember hash value and location of + // constraint. + constraint_pool.data.insert_back(one_constraint.begin(), + one_constraint.end()); + constraint_pool.complete_last_row(); + + // Add the location of constraint in pool. + insert_position = test.second; + } + + // we want to store the result as a short + // variable, so we have to make sure that the + // result does not exceed the limits when + // casting. + Assert(insert_position < (1<<(8*sizeof(unsigned short))), + ExcInternalError()); + return static_cast(insert_position); + } + } // end of namespace internal + + + + // ----------------- actual DoFInfo functions ----------------------------- + + DoFInfo::DoFInfo () + { + clear(); + } + + + DoFInfo::DoFInfo (const DoFInfo &dof_info_in) + : + row_starts (dof_info_in.row_starts), + dof_indices (dof_info_in.dof_indices), + constraint_indicator (dof_info_in.constraint_indicator), + vector_partitioner (dof_info_in.vector_partitioner), + 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), + 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), + store_plain_indices (dof_info_in.store_plain_indices), + ghost_dofs (dof_info_in.ghost_dofs) + {} + + + + void + DoFInfo::clear () + { + row_starts.clear(); + dof_indices.clear(); + constraint_indicator.clear(); + vector_partitioner.reset(); + ghost_dofs.clear(); + dofs_per_cell.clear(); + dofs_per_face.clear(); + row_starts_plain_indices.clear(); + plain_dof_indices.clear(); + store_plain_indices = false; + } + + + + void + DoFInfo::read_dof_indices (const std::vector &local_indices, + const std::vector &lexicographic_inv, + const ConstraintMatrix &constraints, + const unsigned int cell_number, + internal::ConstraintValues &constraint_values, + bool &cell_at_boundary) + { + Assert (vector_partitioner.get() !=0, ExcInternalError()); + const unsigned int n_mpi_procs = vector_partitioner->n_mpi_processes(); + const unsigned int first_owned = vector_partitioner->local_range().first; + const unsigned int last_owned = vector_partitioner->local_range().second; + const unsigned int n_owned = last_owned - first_owned; + std::pair constraint_iterator (0,0); + + unsigned int dofs_this_cell = (cell_active_fe_index.size() == 0) ? + dofs_per_cell[0] : dofs_per_cell[cell_active_fe_index[cell_number]]; + for (unsigned int i=0; i > + * entries_ptr = + constraints.get_constraint_entries(current_dof); + + // dof is constrained + if(entries_ptr != 0) + { + // in case we want to access plain indices, we + // need to know about the location of + // constrained indices as well (all the other + // indices are collected by the cases below) + if (current_dof < first_owned || current_dof >= last_owned) + { + ghost_dofs.push_back (current_dof); + cell_at_boundary = true; + } + + // check whether this dof is identity + // constrained to another dof. then we can + // simply insert that dof and there is no need + // to actually resolve the constraint entries + const std::vector > + & entries = *entries_ptr; + const unsigned int n_entries = entries.size(); + if (n_entries == 1 && std::fabs(entries[0].second-1.)<1e-14) + { + current_dof = entries[0].first; + goto no_constraint; + } + + // append a new index to the indicators + constraint_indicator.push_back (constraint_iterator); + constraint_indicator.back().second = + constraint_values.insert_entries (entries); + + // reset constraint iterator for next round + constraint_iterator.first = 0; + + // add the local_to_global indices computed in + // the insert_entries function. transform the + // index to local index space or mark it as + // ghost if necessary + if (n_entries > 0) + { + const std::vector &constraint_indices = + constraint_values.constraint_indices; + for (unsigned int j=0; j 1 && + (constraint_indices[j] < first_owned || + constraint_indices[j] >= last_owned)) + { + dof_indices.push_back + (n_owned + ghost_dofs.size()); + + // collect ghosts so that we can later + // construct an IndexSet for them. also store + // whether the current cell is on the boundary + ghost_dofs.push_back(constraint_indices[j]); + cell_at_boundary = true; + } + else + // not ghost, so transform to the local index + // space directly + dof_indices.push_back + (constraint_indices[j] - first_owned); + } + } + } + else + { + no_constraint: + // Not constrained, we simply have to add the + // local index to the indices_local_to_global + // list and increment constraint + // iterator. transform to local index + // space/mark as ghost + if (n_mpi_procs > 1 && + (current_dof < first_owned || + current_dof >= last_owned)) + { + ghost_dofs.push_back(current_dof); + current_dof = n_owned + ghost_dofs.size()-1; + cell_at_boundary = true; + } + else + current_dof -= first_owned; + + dof_indices.push_back (current_dof); + + // make sure constraint_iterator.first is + // always within the bounds of unsigned short + Assert (constraint_iterator.first < + (1<<(8*sizeof(unsigned short)))-1, + ExcInternalError()); + constraint_iterator.first++; + } + } + row_starts[cell_number+1] = std_cxx1x::tuple + (dof_indices.size(), constraint_indicator.size(), 0); + + // now to the plain indices: in case we have + // constraints on this cell, store the indices + // without the constraints resolve once again + if (store_plain_indices == true) + { + if (cell_number == 0) + row_starts_plain_indices.resize (row_starts.size()); + row_starts_plain_indices[cell_number] = plain_dof_indices.size(); + bool cell_has_constraints = + std_cxx1x::get<1>(row_starts[cell_number+1]) > + std_cxx1x::get<1>(row_starts[cell_number]); + if (cell_has_constraints == true) + { + for (unsigned int i=0; i 1 && + (current_dof < first_owned || + current_dof >= last_owned)) + { + ghost_dofs.push_back(current_dof); + current_dof = n_owned + ghost_dofs.size()-1; + cell_at_boundary = true; + } + else + current_dof -= first_owned; + plain_dof_indices.push_back (current_dof); + } + } + } + +#ifdef DEBUG + { + unsigned int n_dofs = 0; + const std::pair * blb = begin_indicators(cell_number); + for (unsigned int j=0; j &boundary_cells) + { + Assert (boundary_cells.size() < row_starts.size(), ExcInternalError()); + + // sort ghost dofs and compress out duplicates + const unsigned int n_owned = (vector_partitioner->local_range().second- + vector_partitioner->local_range().first); + const unsigned int n_ghosts = ghost_dofs.size(); + unsigned int n_unique_ghosts= 0; +#ifdef DEBUG + for (std::vector::iterator dof = dof_indices.begin(); + dof!=dof_indices.end(); ++dof) + AssertIndexRange (*dof, n_owned+n_ghosts); +#endif + + std::vector ghost_numbering (n_ghosts); + IndexSet ghost_indices (vector_partitioner->size()); + if (n_ghosts > 0) + { + // since we need to go back to the + // local_to_global indices and replace the + // temporary numbering of ghosts by the real + // number in the index set, we need to store + // these values + std::vector > ghost_origin(n_ghosts); + for (unsigned int i=0; ighost_origin[i-1].first+1) + { + ghost_indices.add_range (last_contiguous_start, + ghost_origin[i-1].first+1); + last_contiguous_start = ghost_origin[i].first; + } + if (ghost_origin[i].first>ghost_origin[i-1].first) + ++n_unique_ghosts; + ghost_numbering[ghost_origin[i].second] = n_unique_ghosts; + } + ++n_unique_ghosts; + ghost_indices.add_range (last_contiguous_start, + ghost_origin.back().first+1); + ghost_indices.compress(); + + // make sure that we got the correct local + // numbering of the ghost dofs. the ghost + // index set should store the same number + { + AssertDimension (n_unique_ghosts, ghost_indices.n_elements()); + for (unsigned int i=0; i + (begin_indices(boundary_cells[i])); + const unsigned int * row_end = end_indices(boundary_cells[i]); + for ( ; data_ptr != row_end; ++data_ptr) + *data_ptr = ((*data_ptr < n_owned) + ? + *data_ptr + : + n_owned + + ghost_numbering[*data_ptr - n_owned]); + + // now the same procedure for plain indices + if (store_plain_indices == true) + { + if (row_length_indicators(boundary_cells[i]) > 0) + { + unsigned int * data_ptr = const_cast + (begin_indices_plain(boundary_cells[i])); + const unsigned int * row_end = + (end_indices_plain(boundary_cells[i])); + for ( ; data_ptr != row_end; ++data_ptr) + *data_ptr = ((*data_ptr < n_owned) + ? + *data_ptr + : + n_owned + + ghost_numbering[*data_ptr - n_owned]); + } + } + } + } + + std::vector new_ghosts; + ghost_dofs.swap(new_ghosts); + // set the ghost indices now. need to cast + // away constness here, but that is uncritical + // since we reset the Partitioner in the same + // initialize call as this call here. + Utilities::MPI::Partitioner * vec_part = + const_cast(vector_partitioner.get()); + vec_part->set_ghost_indices (ghost_indices); + } + + + + void + DoFInfo::reorder_cells (const SizeInfo &size_info, + const std::vector &renumbering, + const std::vector &constraint_pool_row_index, + const std::vector &irregular_cells, + const unsigned int n_vectors) + { + // first reorder the active fe index. + if (cell_active_fe_index.size() > 0) + { + std::vector new_active_fe_index; + new_active_fe_index.reserve (size_info.n_macro_cells); + std::vector fe_indices(n_vectors); + unsigned int position_cell = 0; + for (unsigned int cell=0; cell 0 ? + irregular_cells[cell] : n_vectors); + for (unsigned int j=0; j > new_row_starts; + std::vector new_dof_indices; + std::vector > + new_constraint_indicator; + std::vector new_plain_indices, new_rowstart_plain; + unsigned int position_cell = 0; + new_row_starts.resize (size_info.n_macro_cells + 1); + new_dof_indices.reserve (dof_indices.size()); + new_constraint_indicator.reserve (constraint_indicator.size()); + if (store_plain_indices == true) + { + new_rowstart_plain.resize (size_info.n_macro_cells + 1, + numbers::invalid_unsigned_int); + new_plain_indices.reserve (plain_dof_indices.size()); + } + + // copy the indices and the constraint + // indicators to the new data field: Store the + // indices in a way so that adjacent data + // fields in local vectors are adjacent, i.e., + // first dof index 0 for all vectors, then dof + // index 1 for all vectors, and so on. This + // involves some extra resorting. + std::vector glob_indices (n_vectors); + std::vector plain_glob_indices (n_vectors); + std::vector*> + constr_ind(n_vectors), constr_end(n_vectors); + std::vector index(n_vectors); + for (unsigned int i=0; i + (new_dof_indices.size(), new_constraint_indicator.size(), + irregular_cells[i]); + + const unsigned int n_comp = (irregular_cells[i]>0 ? + irregular_cells[i] : n_vectors); + + for (unsigned int j=0; j= n_comp) + { + ++m_ind_local; + continue; + } + + // otherwise, check if we are a constrained + // dof. The dof is not constrained if we are + // at the end of the row for the constraints + // (indi[j] == n_indi[j]) or if the local + // index[j] is smaller than the next position + // for a constraint. Then, just copy + // it. otherwise, copy all the entries that + // come with this dof + if (constr_ind[j] == constr_end[j] || + index[j] < constr_ind[j]->first) + { + new_dof_indices.push_back (*glob_indices[j]); + ++m_index; + ++index[j]; + ++glob_indices[j]; + } + else + { + const unsigned int constraint_loc = constr_ind[j]->second; + new_constraint_indicator.push_back + (std::pair (m_index, constraint_loc)); + for(unsigned int k=constraint_pool_row_index[constraint_loc]; + k + (new_dof_indices.size(), new_constraint_indicator.size(), 0); + + AssertDimension(dof_indices.size(), new_dof_indices.size()); + AssertDimension(constraint_indicator.size(), + new_constraint_indicator.size()); + + new_row_starts.swap (row_starts); + new_dof_indices.swap (dof_indices); + new_constraint_indicator.swap (constraint_indicator); + new_plain_indices.swap (plain_dof_indices); + new_rowstart_plain.swap (row_starts_plain_indices); + +#ifdef DEBUG + // sanity check 1: all indices should be + // smaller than the number of dofs locally + // owned plus the number of ghosts + const unsigned int index_range = (vector_partitioner->local_range().second- + vector_partitioner->local_range().first) + + vector_partitioner->ghost_indices().n_elements(); + for (unsigned int i=0; i + * con_it = begin_indicators(row), * end_con = end_indicators(row); + for ( ; con_it != end_con; ++con_it) + { + AssertIndexRange (con_it->first, row_length_ind+1); + AssertIndexRange (con_it->second, + constraint_pool_row_index.size()-1); + } + } + + // sanity check 3: all non-boundary cells + // should have indices that only refer to the + // locally owned range + const unsigned int local_size = (vector_partitioner->local_range().second- + vector_partitioner->local_range().first); + for (unsigned int row=0; row size_info.n_macro_cells) + task_info.block_size = size_info.n_macro_cells; + } + + + + void DoFInfo::make_thread_graph_partition_color + (SizeInfo &size_info, + TaskInfo &task_info, + std::vector &renumbering, + std::vector &irregular_cells, + const bool hp_bool) + { + if (size_info.n_macro_cells == 0) + return; + + const std::size_t n_vectors = size_info.n_vectors; + Assert (n_vectors > 0, ExcInternalError()); + + guess_block_size (size_info, task_info); + + // set up partitions. if we just use coloring + // without partitions, do nothing here, assume + // all cells to belong to the zero partition + // (that we otherwise use for MPI boundary + // cells) + unsigned int partition = 0, start_up = 0, counter = 0; + unsigned int start_nonboundary = numbers::invalid_unsigned_int; + bool work = true; + if (task_info.use_coloring_only == false) + { + start_nonboundary = + std::min(((size_info.boundary_cells_end+task_info.block_size-1)/ + task_info.block_size)*task_info.block_size, + size_info.n_macro_cells); + start_up = start_nonboundary; + size_info.boundary_cells_end = start_nonboundary; + } + else + { + start_nonboundary = size_info.n_macro_cells; + start_up = size_info.n_macro_cells; + size_info.boundary_cells_start = 0; + size_info.boundary_cells_end = size_info.n_macro_cells; + } + if (hp_bool == true) + { + irregular_cells.resize (0); + irregular_cells.resize (size_info.n_macro_cells+2*max_fe_index); + std::vector > renumbering_fe_index; + renumbering_fe_index.resize(max_fe_index); + unsigned int counter,n_macro_cells_before = 0; + for(counter=0;counter cell_partition(task_info.n_blocks, + size_info.n_macro_cells); + std::vector neighbor_list; + std::vector neighbor_neighbor_list; + + // In element j of this variable, one puts the + // old number of the block that should be the + // jth block in the new numeration. + std::vector partition_list (task_info.n_blocks,0); + std::vector partition_color_list(task_info.n_blocks,0); + + // This vector points to the start of each + // partition. + std::vector partition_blocks (2,0); + std::vector cell_color(task_info.n_blocks, + size_info.n_macro_cells); + std::vector color_finder; + + while(work) + { + // put all cells up to begin_inner_cells into + // first partition. if the numbers do not add + // up exactly, assign an additional block + if(start_nonboundary>0 && start_up == start_nonboundary) + { + unsigned int n_blocks = ((start_nonboundary+task_info.block_size-1) + /task_info.block_size); + for(unsigned int cell=0; cell0) + { + partition++; + partition_blocks.push_back(partition_blocks.back()); + for(unsigned int j=0;j max_color) + max_color = cell_color[cell]; + } + // Reorder within partition: First, all blocks + // that belong the 0 and then so on until + // those with color max (Note that the smaller + // the number the larger the partition) + for(unsigned int color=0; color<=max_color; color++) + { + task_info.partition_color_blocks.data.push_back(color_counter); + index_counter++; + for (unsigned int k=partition_blocks[part]; + k sorted_pc_list (partition_color_list); + std::sort(sorted_pc_list.begin(), sorted_pc_list.end()); + for (unsigned int i=0; i block_start(size_info.n_macro_cells+1); + std::vector irregular(size_info.n_macro_cells); + + unsigned int mcell_start=0; + block_start[0] = 0; + for(unsigned int block=0; block0) + ?irregular_cells[mcell]:size_info.n_vectors; + block_start[block+1] += n_comp; + ++counter; + } + mcell_start += task_info.block_size; + } + counter = 0; + unsigned int counter_macro = 0; + for(unsigned int block=0; block sorted_renumbering (renumbering); + std::sort(sorted_renumbering.begin(), sorted_renumbering.end()); + for (unsigned int i=0; i>1; + task_info.odds = (partition)>>1; + task_info.n_blocked_workers = task_info.odds- + (task_info.odds+task_info.evens+1)%2; + task_info.n_workers = task_info.partition_color_blocks.data.size()-1- + task_info.n_blocked_workers; + } + + + + void + DoFInfo::make_thread_graph_partition_partition + (SizeInfo &size_info, + TaskInfo &task_info, + std::vector &renumbering, + std::vector &irregular_cells, + const bool hp_bool) + { + if (size_info.n_macro_cells == 0) + return; + + const std::size_t n_vectors = size_info.n_vectors; + Assert (n_vectors > 0, ExcInternalError()); + + guess_block_size (size_info, task_info); + + // assume that all FEs have the same + // connectivity graph, so take the zeroth FE + task_info.n_blocks = (size_info.n_macro_cells+task_info.block_size-1)/ + task_info.block_size; + task_info.block_size_last = size_info.n_macro_cells- + (task_info.block_size*(task_info.n_blocks-1)); + task_info.position_short_block = task_info.n_blocks-1; + unsigned int cluster_size = task_info.block_size*n_vectors; + + // create the connectivity graph without + // internal blocking + CompressedSimpleSparsityPattern connectivity; + make_connectivity_graph (size_info, task_info, renumbering,irregular_cells, + false, connectivity); + + // Create cell-block partitioning. + + // For each block of cells, this variable + // saves to which partitions the block + // belongs. Initialize all to n_macro_cells to + // mark them as not yet assigned a partition. + std::vector cell_partition (size_info.n_active_cells, + size_info.n_active_cells); + std::vector neighbor_list; + std::vector neighbor_neighbor_list; + + // In element j of this variable, one puts the + // old number of the block that should be the + // jth block in the new numeration. + std::vector partition_list(size_info.n_active_cells,0); + std::vector partition_partition_list(size_info.n_active_cells,0); + + // This vector points to the start of each + // partition. + std::vector partition_size(2,0); + + unsigned int partition = 0,start_up=0,counter=0; + unsigned int start_nonboundary = n_vectors * size_info.boundary_cells_end; + if (start_nonboundary > size_info.n_active_cells) + start_nonboundary = size_info.n_active_cells; + bool work = true; + unsigned int remainder = cluster_size; + while (work) + { + // put the cells with neighbors on remote MPI + // processes up front + if(start_nonboundary>0) + { + for(unsigned int cell=0; cell0) + { + if(index==index_stop) + { + index = neighbor_list.size(); + if(index == index_before) + { + neighbor_list.resize(0); + goto not_connect; + } + index_stop = index_before; + index_before = index; + } + index--; + unsigned int additional = neighbor_list[index]; + CompressedSimpleSparsityPattern::row_iterator neighbor = + connectivity.row_begin(additional), + end = connectivity.row_end(additional); + for(; neighbor!=end ;++neighbor) + { + if(cell_partition[*neighbor]==size_info.n_active_cells) + { + partition_size.back()++; + cell_partition[*neighbor] = partition; + neighbor_list.push_back(*neighbor); + partition_list[counter++] = *neighbor; + remainder--; + if(remainder == 0) + break; + } + } + } + + while(neighbor_list.size()>0) + { + partition++; + unsigned int partition_counter = 0; + partition_size.push_back(partition_size.back()); + + for(unsigned int j=0;j0) + { + if(index==index_stop) + { + index = neighbor_neighbor_list.size(); + if(index == index_before) + { + neighbor_neighbor_list.resize(0); + break; + } + index_stop = index_before; + index_before = index; + } + index--; + unsigned int additional = neighbor_neighbor_list[index]; + CompressedSimpleSparsityPattern::row_iterator neighbor = + connectivity.row_begin(additional), + end = connectivity.row_end(additional); + for(; neighbor!=end ;++neighbor) + { + if(cell_partition[*neighbor]==size_info.n_active_cells) + { + partition_size.back()++; + cell_partition[*neighbor] = partition; + neighbor_neighbor_list.push_back(*neighbor); + partition_list[counter++] = *neighbor; + remainder--; + if(remainder == 0) + break; + } + } + } + + neighbor_list = neighbor_neighbor_list; + neighbor_neighbor_list.resize(0); + } + not_connect: + // One has to check if the graph is not + // connected so we have to find another + // partition. + work = false; + for (unsigned int j=start_up;j cell_partition_l2(size_info.n_active_cells, + size_info.n_active_cells); + task_info.partition_color_blocks.row_index.resize(partition+1,0); + task_info.partition_color_blocks.data.resize(1,0); + + start_up = 0; + counter = 0; + unsigned int missing_macros; + for (unsigned int part=0;part0) + { + int index_before = neighbor_neighbor_list.size(), + index = index_before; + { + // put the cells into separate lists for each + // FE index within one partition-partition + missing_macros = 0; + std::vector remaining_per_macro_cell + (max_fe_index); + std::vector > + renumbering_fe_index; + unsigned int cell; + bool filled = true; + if(hp_bool == true) + { + renumbering_fe_index.resize(max_fe_index); + for(cell=counter-partition_counter; cell0 || filled == false) + { + if(index==0) + { + index = neighbor_neighbor_list.size(); + if(index == index_before) + { + if(missing_macros != 0) + { + neighbor_neighbor_list.resize(0); + } + break;//goto not_connected; + } + index_before = index; + } + index--; + unsigned int additional = neighbor_neighbor_list + [index]; + + // go through the neighbors of the last cell + // in the current partition and check if we + // find some to fill up with. + CompressedSimpleSparsityPattern::row_iterator + neighbor = + connectivity.row_begin(additional), + end = connectivity.row_end(additional); + for(; neighbor!=end ;++neighbor) + { + if(cell_partition[*neighbor] == part && + cell_partition_l2[*neighbor] == + size_info.n_active_cells) + { + unsigned int this_index = 0; + if(hp_bool == true) + this_index = cell_active_fe_index[*neighbor]; + + // Only add this cell if we need more macro + // cells in the current block or if there is a + // macro cell with the FE index that is not + // yet fully populated + if (missing_macros > 0 || + remaining_per_macro_cell[this_index] > 0) + { + cell_partition_l2[*neighbor] = partition_l2; + neighbor_neighbor_list.push_back(*neighbor); + if(hp_bool == true) + renumbering_fe_index[this_index]. + push_back(*neighbor); + partition_partition_list[counter] = + *neighbor; + counter++; + partition_counter++; + if (remaining_per_macro_cell[this_index] + == 0 && missing_macros > 0) + missing_macros--; + remaining_per_macro_cell[this_index]++; + if (remaining_per_macro_cell[this_index] + == n_vectors) + { + remaining_per_macro_cell[this_index] = 0; + } + if(missing_macros == 0) + { + filled = true; + for(unsigned int fe_ind=0; + fe_ind0) + size_info.boundary_cells_end = task_info.partition_color_blocks. + data[task_info.partition_color_blocks.row_index[1]]; + + if (hp_bool == false) + renumbering.swap(partition_partition_list); + irregular_cells.resize(n_macro_cells_before); + size_info.n_macro_cells = n_macro_cells_before; + + task_info.evens = (partition+1)/2; + task_info.odds = partition/2; + task_info.n_blocked_workers = + task_info.odds-(task_info.odds+task_info.evens+1)%2; + task_info.n_workers = task_info.evens+task_info.odds- + task_info.n_blocked_workers; + task_info.partition_evens.resize(partition); + task_info.partition_odds.resize(partition); + task_info.partition_n_blocked_workers.resize(partition); + task_info.partition_n_workers.resize(partition); + for(unsigned int part=0;part additional_data; + }; + + SimplifiedCompressedSparsity (const unsigned int n_rows, + const unsigned int n_cols) + : + rows (n_rows), + n_cols (n_cols) + {} + + void add (unsigned int row, unsigned int entry) + { + AssertIndexRange (row, rows.size()); + AssertIndexRange (entry, n_cols); + rows[row].add (entry); + } + + unsigned int row_length (unsigned int row) const + { + return rows[row].n_entries + rows[row].additional_data.size(); + } + + std::vector rows; + unsigned int n_cols; + }; + + // rudimentary version of a vector that keeps + // entries always ordered + class ordered_vector : public std::vector + { + public: + ordered_vector () { reserve (2000); } + + void reserve (const unsigned int size) + { + if (size > 0) + this->std::vector::reserve (size); + } + + + // insert a given entry. dat is a pointer + // within this vector (make sure that it + // really stays there, otherwise there will be + // an error) + void insert (const unsigned int entry, + std::vector::iterator &dat) + { + AssertIndexRange (static_cast(dat - begin()), size()+1); + AssertIndexRange (static_cast(end() - dat), size()+1); + AssertIndexRange (size(), capacity()); + while (dat != end() && *dat < entry) + ++dat; + + if (dat == end()) + { + push_back(entry); + } + else if (*dat > entry) + { + this->std::vector::insert (dat, entry); + } + ++dat; + } + }; + } + + + void + DoFInfo::make_connectivity_graph + (const SizeInfo &size_info, + const TaskInfo &task_info, + const std::vector &renumbering, + const std::vector &irregular_cells, + const bool do_blocking, + CompressedSimpleSparsityPattern &connectivity) const + { + AssertDimension (row_starts.size()-1, size_info.n_active_cells); + const unsigned int n_rows = (vector_partitioner->local_range().second- + vector_partitioner->local_range().first) + + vector_partitioner->ghost_indices().n_elements(); + const unsigned int n_blocks = (do_blocking == true) ? + task_info.n_blocks : size_info.n_active_cells; + + internal::SimplifiedCompressedSparsity + connectivity_dof (n_rows, n_blocks); + unsigned int cell_start = 0,mcell_start = 0; + for (unsigned int block = 0; block < n_blocks; ++block) + { + + // if we have the blocking variant (used in + // the coloring scheme), we want to build a + // graph with the blocks with interaction with + // remote MPI processes up front. in the + // non-blocking variant, we do not do this + // here. TODO: unify this approach!!! + if (do_blocking == true) + { + for(unsigned int mcell=mcell_start;mcell< + std::min(mcell_start+task_info.block_size, + size_info.n_macro_cells); + ++mcell) + { + unsigned int n_comp = (irregular_cells[mcell]>0) + ?irregular_cells[mcell]:size_info.n_vectors; + for (unsigned int cell = cell_start; cell < cell_start+n_comp; + ++cell) + { + const unsigned int * it = begin_indices + (renumbering[cell]), + * end_cell = end_indices (renumbering[cell]); + for ( ; it != end_cell; ++it) + connectivity_dof.add(*it, block); + } + cell_start += n_comp; + } + mcell_start += task_info.block_size; + } + else + { + const unsigned int * it = begin_indices (block), + * end_cell = end_indices (block); + for ( ; it != end_cell; ++it) + connectivity_dof.add(*it, block); + } + } + + connectivity.reinit (n_blocks, n_blocks); + internal::ordered_vector row_entries; + cell_start = 0; + mcell_start = 0; + for (unsigned int block=0; block < n_blocks; ++block) + { + row_entries.clear(); + + if(do_blocking==true) + { + for(unsigned int mcell=mcell_start;mcell< + std::min(mcell_start+task_info.block_size, + size_info.n_macro_cells); + ++mcell) + { + unsigned int n_comp = (irregular_cells[mcell]>0) + ?irregular_cells[mcell]:size_info.n_vectors; + for (unsigned int cell = cell_start; cell < cell_start+n_comp; + ++cell) + { + // apply renumbering when we do blocking + const unsigned int * it = begin_indices (renumbering[cell]), + * end_cell = end_indices (renumbering[cell]); + row_entries.reserve (row_entries.size() + end_cell - it); + for ( ; it != end_cell; ++it) + { + // the simplified sparsity pattern has two + // fields: a short field for dofs that only + // belong to a few cells, and a long field for + // all dofs with many cells that spills the + // short data field. so go them through both + const internal::SimplifiedCompressedSparsity::LineData & dat = + connectivity_dof.rows[*it]; + std::vector::iterator insert_pos = row_entries.begin(); + for (unsigned int i=0; i= block) + goto end_this; + else + row_entries.insert (dat.short_data[i], insert_pos); + if (dat.n_entries == + internal::SimplifiedCompressedSparsity::short_length) + { + for (unsigned int i=0; i= block) + goto end_this; + else + row_entries.insert (dat.additional_data[i], insert_pos); + } + end_this: + {} + } + } + cell_start +=n_comp; + } + mcell_start += task_info.block_size; + } + else + { + const unsigned int * it = begin_indices (block), + * end_cell = end_indices (block); + row_entries.reserve (row_entries.size() + end_cell - it); + for ( ; it != end_cell; ++it) + { + // the simplified sparsity pattern has two + // fields: a short field for dofs that only + // belong to a few cells, and a long field for + // all dofs with many cells that spills the + // short data field. so go them through both + const internal::SimplifiedCompressedSparsity::LineData & dat = + connectivity_dof.rows[*it]; + std::vector::iterator insert_pos = row_entries.begin(); + for (unsigned int i=0; i= block) + goto end_this_nb; + else + row_entries.insert (dat.short_data[i], insert_pos); + if (dat.n_entries == + internal::SimplifiedCompressedSparsity::short_length) + { + for (unsigned int i=0; i= block) + goto end_this_nb; + else + row_entries.insert (dat.additional_data[i], insert_pos); + } + end_this_nb: + {} + } + } + connectivity.add_entries (block, row_entries.begin(), row_entries.end()); + } + connectivity.symmetrize (); + } + + + + void DoFInfo::renumber_dofs (std::vector &renumbering) + { + // first renumber all locally owned degrees of + // freedom + AssertDimension (vector_partitioner->local_size(), + vector_partitioner->size()); + const unsigned int local_size = vector_partitioner->local_size(); + renumbering.resize (0); + renumbering.resize (local_size, numbers::invalid_unsigned_int); + unsigned int counter = 0; + std::vector::iterator dof_ind = dof_indices.begin(), + end_ind = dof_indices.end(); + for ( ; dof_ind != end_ind; ++dof_ind) + { + if (*dof_ind < local_size) + { + if (renumbering[*dof_ind] == numbers::invalid_unsigned_int) + renumbering[*dof_ind] = counter++; + *dof_ind = renumbering[*dof_ind]; + } + } + + AssertIndexRange (counter, local_size+1); + for (unsigned int i=0; i new_constrained_dofs (constrained_dofs.size()); + for (unsigned int i=0; i new_constrained_dofs[i-1], ExcInternalError()); +#endif + std::swap (constrained_dofs, new_constrained_dofs); + + AssertDimension (counter, renumbering.size()); + } + + + + std::size_t + DoFInfo::memory_consumption () const + { + std::size_t memory = sizeof(*this); + memory += (row_starts.capacity()*sizeof(std_cxx1x::tuple)); + memory += MemoryConsumption::memory_consumption (dof_indices); + memory += MemoryConsumption::memory_consumption (row_starts_plain_indices); + memory += MemoryConsumption::memory_consumption (plain_dof_indices); + memory += MemoryConsumption::memory_consumption (constraint_indicator); + memory += MemoryConsumption::memory_consumption (*vector_partitioner); + return memory; + } + + + + template + void + DoFInfo::print_memory_consumption (STREAM &out, + const SizeInfo &size_info) const + { + out << " Memory row starts indices: "; + size_info.print_mem (out, (row_starts.capacity()* + sizeof(std_cxx1x::tuple))); + out << " Memory dof indices: "; + size_info.print_mem (out, MemoryConsumption::memory_consumption (dof_indices)); + out << " Memory constraint indicators: "; + size_info.print_mem (out, MemoryConsumption::memory_consumption (constraint_indicator)); + out << " Memory plain indices: "; + size_info.print_mem (out, MemoryConsumption::memory_consumption (row_starts_plain_indices)+ + MemoryConsumption::memory_consumption (plain_dof_indices)); + out << " Memory vector partitioner: "; + size_info.print_mem (out, MemoryConsumption::memory_consumption (*vector_partitioner)); + } + + + + template + void + DoFInfo::print (const CompressedMatrix &constraint_pool, + std::ostream &out) const + { + const unsigned int n_rows = row_starts.size() - 1; + for (unsigned int row=0 ; row + * con_it = begin_indicators(row), + * end_con = end_indicators(row); + for ( ; con_it != end_con; ++con_it) + { + for( ; indexfirst; index++) + { + Assert (glob_indices+index != end_row, ExcInternalError()); + out << glob_indices[index] << " "; + } + + out << "[ "; + for(unsigned int k=constraint_pool.row_index[con_it->second]; + ksecond+1]; + k++,index++) + { + Assert (glob_indices+index != end_row, ExcInternalError()); + out << glob_indices[index] << "/" + << constraint_pool.data[k]; + if (ksecond+1]-1) + out << " "; + } + out << "] "; + } + glob_indices += index; + for(; glob_indices != end_row; ++glob_indices) + out << *glob_indices << " "; + out << std::endl; + } + } + + +} // end of namespace MatrixFreeFunctions +} // end of namespace internal + +DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/include/deal.II/matrix_free/fe_evaluation.h b/deal.II/include/deal.II/matrix_free/fe_evaluation.h new file mode 100644 index 0000000000..c6e36a3a46 --- /dev/null +++ b/deal.II/include/deal.II/matrix_free/fe_evaluation.h @@ -0,0 +1,5776 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 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__matrix_free_fe_evaluation_h +#define __deal2__matrix_free_fe_evaluation_h + + +#include +#include +#include +#include +#include +#include + + +DEAL_II_NAMESPACE_OPEN + +namespace parallel +{ + namespace distributed + { + template class Vector; + } +} + + + +namespace internal +{ + DeclException0 (ExcAccessToUninitializedField); +} + + + +/** + * This is the base class for the FEEvaluation classes. This class is a base + * class and needs usually not be called in user code. Use one of the derived + * classes instead. It implements access functions to vectors for the @p + * read_dof_values, @p set_dof_values, and @p distributed_local_to_global + * functions, as well as the @p reinit method. + * + * This class has five template arguments: + * + * @param dim Dimension in which this class is to be used + * + * @param dofs_per_cell Number of degrees of freedom of the FE per cell, + * usually (fe_degree+1)^dim for elements based on a tensor + * product + * + * @param n_q_points Number of points in the quadrature formula, usually + * (fe_degree+1)^dim for tensor-product quadrature formulas + * + * @param n_components Number of vector components when solving a system of + * PDEs. If the same operation is applied to several + * components of a PDE (e.g. a vector Laplace equation), they + * can be applied simultaneously with one call (and often + * more efficiently) + * + * @param Number Number format, usually @p double or @p float + * + * @author Katharina Kormann and Martin Kronbichler, 2010, 2011 + */ +template +class FEEvaluationBase +{ +public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = + VectorizedArray::n_array_elements; + static const unsigned int dofs_per_cell = dofs_per_cell_; + static const unsigned int n_q_points = n_q_points_; + + /** + * Constructor. Takes all data stored in + * MatrixFree. If applied to problems with + * more than one finite element or more than + * one quadrature formula selected during + * construction of @p matrix_free, @p + * fe_no and @p quad_no allow to select the + * appropriate components. + */ + FEEvaluationBase (const MatrixFree &matrix_free, + const unsigned int fe_no = 0, + const unsigned int quad_no = 0); + + /** + * Initializes the operation pointer to the + * current cell. Unlike the FEValues::reinit + * function, where the information related to + * a particular cell is generated in the + * reinit call, this function is very cheap + * since all data is pre-computed in @p + * matrix_free, and only a few indices + * have to be set appropriately. + */ + void reinit (const unsigned int cell); + + /** + * For the transformation information stored + * in MappingInfo, this function returns the + * index which belongs to the current cell as + * specified in @p reinit. Note that + * MappingInfo has different fields for + * Cartesian cells, cells with linear mapping + * and with general mappings, so in order to + * access the correct data, this interface + * must be used together with get_cell_type. + */ + unsigned int get_cell_data_number() const; + + /** + * Returns the type of the cell the @p reinit + * function has been called for. 0 means + * Cartesian cells (which allows for + * considerable data compression), 1 means + * cells with linear mappings, and 2 means + * general cells without any compressed + * storage applied. + */ + unsigned int get_cell_type() const; + + /** + * Returns a read-only pointer to the first + * field of function values on quadrature + * points. First come the function values on + * all quadrature points for the first + * component, then all values for the second + * component, and so on. This is related to + * the internal data structures used in this + * class. The raw data after a call to @p + * evaluate only contains unit cell + * operations, so possible transformations, + * quadrature weights etc. must be applied + * manually. In general, it is safer to use + * the get_value() function instead, which + * does all the transformation internally. + */ + const vector_t * begin_values () const; + + /** + * Returns a read and write pointer to the + * first field of function values on + * quadrature points. First come the function + * values on all quadrature points for the + * first component, then all values for the + * second component, and so on. This is + * related to the internal data structures + * used in this class. The raw data after a + * call to @p evaluate only contains unit + * cell operations, so possible + * transformations, quadrature weights + * etc. must be applied manually. In general, + * it is safer to use the get_value() function + * instead, which does all the transformation + * internally. + */ + vector_t * begin_values (); + + /** + * Returns a read-only pointer to the first + * field of function gradients on quadrature + * points. First comes the x-component of the + * gradient for the first component on all + * quadrature points, then the y-component, + * and so on. Next comes the x-component of + * the second component, and so on. This is + * related to the internal data structures + * used in this class. The raw data after a + * call to @p evaluate only contains unit + * cell operations, so possible + * transformations, quadrature weights + * etc. must be applied manually. In general, + * it is safer to use the get_gradient() function + * instead, which does all the transformation + * internally. + */ + const vector_t * begin_gradients () const; + + /** + * Returns a read and write pointer to the + * first field of function gradients on + * quadrature points. First comes the + * x-component of the gradient for the first + * component on all quadrature points, then + * the y-component, and so on. Next comes the + * x-component of the second component, and so + * on. This is related to the internal data + * structures used in this class. The raw data + * after a call to @p evaluate only + * contains unit cell operations, so possible + * transformations, quadrature weights + * etc. must be applied manually. In general, + * it is safer to use the get_gradient() + * function instead, which does all the + * transformation internally. + */ + vector_t * begin_gradients (); + + /** + * Returns a read-only pointer to the first + * field of function hessians on quadrature + * points. First comes the xx-component of the + * hessian for the first component on all + * quadrature points, then the yy-component, + * zz-component in (3D), then the + * xy-component, and so on. Next comes the + * xx-component of the second component, and + * so on. This is related to the internal data + * structures used in this class. The raw data + * after a call to @p evaluate only + * contains unit cell operations, so possible + * transformations, quadrature weights + * etc. must be applied manually. In general, + * it is safer to use the get_laplacian() or + * get_hessian() functions instead, which does + * all the transformation internally. + */ + const vector_t * begin_hessians () const; + + /** + * Returns a read and write pointer to the + * first field of function hessians on + * quadrature points. First comes the + * xx-component of the hessian for the first + * component on all quadrature points, then + * the yy-component, zz-component in (3D), + * then the xy-component, and so on. Next + * comes the xx-component of the second + * component, and so on. This is related to + * the internal data structures used in this + * class. The raw data after a call to @p + * evaluate only contains unit cell + * operations, so possible transformations, + * quadrature weights etc. must be applied + * manually. In general, it is safer to use + * the get_laplacian() or get_hessian() + * functions instead, which does all the + * transformation internally. + */ + vector_t * begin_hessians (); + + /** + * For the vector @p src, read out the values + * on the degrees of freedom of the current + * cell, and store them internally. Similar + * functionality as the function + * DoFAccessor::get_interpolated_dof_values + * when no constraints are present, but it + * also includes constraints from hanging + * nodes, so one can see it as a similar + * function to + * ConstraintMatrix::read_dof_values as + * well. Note that if vectorization is + * enabled, the DoF values for several cells + * are set. + * + * If some constraints on the vector are + * inhomogeneous, use the function + * read_dof_values_plain instead and provide + * the vector with useful data also in + * constrained positions by calling + * ConstraintMatrix::distribute. When + * accessing vector entries during the + * solution of linear systems, the temporary + * solution should always have homogeneous + * constraints and this method is the correct + * one. + */ + template + void read_dof_values (const VectorType &src); + + /** + * For a collection of several vector @p src, + * read out the values on the degrees of + * freedom of the current cell for @p + * n_components (template argument), starting + * at @p first_index, and store them + * internally. Similar functionality as the + * function ConstraintMatrix::read_dof_values. + * Note that if vectorization is enabled, the + * DoF values for several cells are set. + */ + template + void read_dof_values (const std::vector &src, + const unsigned int first_index=0); + + /** + * Reads data from several vectors. Same as + * other function with std::vector, but + * accepts a vector of pointers to vectors. + */ + template + void read_dof_values (const std::vector &src, + const unsigned int first_index=0); + + /** + * For a collection of several vector @p src, + * read out the values on the degrees of + * freedom of the current cell for @p + * n_components (template argument), and store + * them internally. Similar functionality as + * the function + * ConstraintMatrix::read_dof_values. Note + * that if vectorization is enabled, the DoF + * values for several cells are set. + */ + template + void read_dof_values (const VectorType * src_data[]); + + /** + * For the vector @p src, read out the values + * on the degrees of freedom of the current + * cell, and store them internally. Similar + * functionality as the function + * DoFAccessor::get_interpolated_dof_values. As + * opposed to the read_dof_values function, + * this function reads out the plain entries + * from vectors, without taking stored + * constraints into account. This way of + * access is appropriate when the constraints + * have been distributed on the vector by a + * call to ConstraintMatrix::distribute + * previously. This function is also necessary + * when inhomogeneous constraints are to be + * used, as MatrixFree can only handle + * homogeneous constraints. Note that if + * vectorization is enabled, the DoF values + * for several cells are set. + */ + template + void read_dof_values_plain (const VectorType &src); + + /** + * For a collection of several vector @p src, + * read out the values on the degrees of + * freedom of the current cell for @p + * n_components (template argument), starting + * at @p first_index, and store them + * internally. Similar functionality as the + * function DoFAccessor::read_dof_values. + * Note that if vectorization is enabled, the + * DoF values for several cells are set. + */ + template + void read_dof_values_plain (const std::vector &src, + const unsigned int first_index=0); + + /** + * Reads data from several vectors without + * resolving constraints. Same as other + * function with std::vector, but accepts a + * vector of pointers to vectors. + */ + template + void read_dof_values_plain (const std::vector &src, + const unsigned int first_index=0); + + /** + * For a collection of several vector @p src, + * read out the values on the degrees of + * freedom of the current cell for @p + * n_components (template argument), and store + * them internally. Similar functionality as + * the function + * DoFAccessor::read_dof_values. Note + * that if vectorization is enabled, the DoF + * values for several cells are set. + */ + template + void read_dof_values_plain (const VectorType * src_data[]); + + /** + * Takes the values stored internally on dof + * values of the current cell and sums them + * into the vector @p dst. The function also + * applies constraints during the write + * operation. The functionality is hence + * similar to the function + * ConstraintMatrix::distribute_local_to_global. + * Note that if vectorization is enabled, the + * DoF values for several cells are used. + */ + template + void distribute_local_to_global (VectorType &dst) const; + + /** + * Takes the values stored internally on dof + * values of the current cell for a + * vector-valued problem consisting of @p + * n_components (template argument) and sums + * them into the collection of vectors vector + * @p dst, starting at index @p + * first_index. The function also applies + * constraints during the write operation. The + * functionality is hence similar to the + * function + * ConstraintMatrix::distribute_local_to_global. + * Note that if vectorization is enabled, the + * DoF values for several cells are used. + */ + template + void distribute_local_to_global (std::vector &dst, + const unsigned int first_index=0) const; + + /** + * Writes data to several vectors. Same as + * other function with std::vector, but + * accepts a vector of pointers to vectors. + */ + template + void distribute_local_to_global (std::vector &dst, + const unsigned int first_index=0) const; + + /** + * Takes the values stored internally on dof + * values of the current cell for a + * vector-valued problem consisting of @p + * n_components (template argument) and sums + * them into the collection of vectors vector + * @p dst. The function also applies + * constraints during the write operation. The + * functionality is hence similar to the + * function + * ConstraintMatrix::distribute_local_to_global. + * Note that if vectorization is enabled, the + * DoF values for several cells are used. + */ + template + void distribute_local_to_global (VectorType * dst_data[]) const; + + /** + * Takes the values stored internally on dof + * values of the current cell and sums them + * into the vector @p dst. The function also + * applies constraints during the write + * operation. The functionality is hence + * similar to the function + * ConstraintMatrix::distribute_local_to_global. + * Note that if vectorization is enabled, the + * DoF values for several cells are used. + */ + template + void set_dof_values (VectorType &dst) const; + + /** + * Takes the values stored internally on dof + * values of the current cell for a + * vector-valued problem consisting of @p + * n_components (template argument) and sums + * them into the collection of vectors vector + * @p dst, starting at index @p + * first_index. The function also applies + * constraints during the write operation. The + * functionality is hence similar to the + * function + * ConstraintMatrix::distribute_local_to_global. + * Note that if vectorization is enabled, the + * DoF values for several cells are used. + */ + template + void set_dof_values (std::vector &dst, + const unsigned int first_index=0) const; + + /** + * Writes data to several vectors. Same as + * other function with std::vector, but + * accepts a vector of pointers to vectors. + */ + template + void set_dof_values (std::vector &dst, + const unsigned int first_index=0) const; + + /** + * Takes the values stored internally on dof + * values of the current cell for a + * vector-valued problem consisting of @p + * n_components (template argument) and sums + * them into the collection of vectors vector + * @p dst. The function also applies + * constraints during the write operation. The + * functionality is hence similar to the + * function + * ConstraintMatrix::distribute_local_to_global. + * Note that if vectorization is enabled, the + * DoF values for several cells are used. + */ + template + void set_dof_values (VectorType * dst_data[]) const; + + /** + * Returns the value stored for the local + * degree of freedom with index @p dof. If the + * object is vector-valued, a vector-valued + * return argument is given. Note that when + * vectorization is enabled, values from + * several cells are grouped together. If @p + * set_dof_values was called last, the value + * corresponds to the one set there. If @p + * integrate was called last, it instead + * corresponds to the value of the integrated + * function with the test function of the + * given index. + */ + Tensor<1,n_components,vector_t> + get_dof_value (unsigned int dof) const; + + /** + * Write a value to the field containing the + * degrees of freedom with component @p + * dof. Access to the same field as through @p + * get_dof_value. + */ + void submit_dof_value (Tensor<1,n_components,vector_t> val_in, + unsigned int dof); + + /** + * Returns the value of a finite + * element function at quadrature + * point number @p q_point after a + * call to @p evaluate(true,...), or + * the value that has been stored + * there with a call to @p + * submit_value. If the object is + * vector-valued, a vector-valued + * return argument is given. Note that + * when vectorization is enabled, + * values from several cells are + * grouped together. + */ + Tensor<1,n_components,vector_t> + get_value (unsigned int q_point) const; + + /** + * Write a value to the field containing the + * values on quadrature points with component + * @p q_point. Access to the same field as + * through @p get_value. If applied before the + * function @p integrate(true,...) is + * called, this specifies the value which is + * tested by all basis function on the current + * cell and integrated over. + */ + void submit_value (Tensor<1,n_components,vector_t> val_in, + unsigned int q_point); + + /** + * Returns the gradient of a finite element + * function at quadrature point number @p + * q_point after a call to @p + * evaluate(...,true,...), or the value + * that has been stored there with a call to + * @p submit_gradient. + */ + Tensor<1,n_components,Tensor<1,dim,vector_t> > + get_gradient (unsigned int q_point) const; + + /** + * Write a gradient to the field containing + * the values on quadrature points with + * component @p q_point. Access to the same + * field as through @p get_gradient. If + * applied before the function @p + * integrate(...,true) is called, + * this specifies the gradient which is tested + * by all basis function gradients on the + * current cell and integrated over. + */ + void submit_gradient(Tensor<1,n_components,Tensor<1,dim,vector_t> >grad_in, + unsigned int q_point); + + /** + * Returns the Hessian of a finite element + * function at quadrature point number @p + * q_point after a call to @p + * evaluate(...,true). If only the + * diagonal or even the trace of the Hessian, + * the Laplacian, is needed, use the other + * functions below. + */ + Tensor<1,n_components,Tensor<2,dim,vector_t> > + get_hessian (unsigned int q_point) const; + + /** + * Returns the diagonal of the Hessian of a + * finite element function at quadrature point + * number @p q_point after a call to @p + * evaluate(...,true). + */ + Tensor<1,n_components,Tensor<1,dim,vector_t> > + get_hessian_diagonal (unsigned int q_point) const; + + /** + * Returns the Laplacian of a finite element + * function at quadrature point number @p + * q_point after a call to @p + * evaluate(...,true). + */ + Tensor<1,n_components,vector_t> + get_laplacian (unsigned int q_point) const; + + /** + * Takes values on quadrature points, + * multiplies by the Jacobian determinant and + * quadrature weights (JxW) and sums the + * values for all quadrature points on the + * cell. The result is a scalar, representing + * the integral over the function over the + * cell. If a vector-element is used, the + * resulting components are still + * separated. Moreover, if vectorization is + * enabled, the integral values of several + * cells are represented together. + */ + Tensor<1,n_components,vector_t> + integrate_value (); + + /** + * Stores a reference to the underlying data. + */ + const MatrixFree &matrix_info; + + /** + * Stores a reference to the underlying DoF + * indices and constraint description for the + * component specified at construction. Also + * contained in matrix_info, but it simplifies + * code if we store a reference to it. + */ + const internal::MatrixFreeFunctions::DoFInfo &dof_info; + + /** + * Stores the constraints weights that + * supplement DoFInfo. Also contained in + * matrix_info, but it simplifies code if we + * store a reference to it. + */ + const internal::MatrixFreeFunctions::CompressedMatrix &constraint_pool; + + /** + * Stores a reference to the underlying + * transformation data from unit to real cells + * for the given quadrature formula specified + * at construction. Also contained in + * matrix_info, but it simplifies code if we + * store a reference to it. + */ + const internal::MatrixFreeFunctions::MappingInfo &mapping_info; + + /** + * Stores the active fe index for this class + * for efficient indexing in the hp case. + */ + const unsigned int active_fe_index; + + /** + * Stores the active quadrature index for this + * class for efficient indexing in the hp + * case. + */ + const unsigned int active_quad_index; + + /** + * Stores a reference to the unit cell data, + * i.e., values, gradients and Hessians in 1D + * at the quadrature points that constitute + * the tensor product. Also contained in + * matrix_info, but it simplifies code if we + * store a reference to it. + */ + const internal::MatrixFreeFunctions::FEEvaluationData &data; + +protected: + /** + * Internal data fields that store the + * values. Since all array lengths are known + * at compile time and since they are rarely + * more than a few kilobytes, allocate them on + * the stack. This makes it possible to + * cheaply set up a FEEvaluation object and + * write thread-safe programs by letting each + * thread own a private object of this type. + */ + vector_t values_dofs[n_components][dofs_per_cell>0?dofs_per_cell:1]; + vector_t values_quad[n_components][n_q_points>0?n_q_points:1]; + vector_t gradients_quad[n_components][dim][n_q_points>0?n_q_points:1]; + vector_t hessians_quad[n_components][(dim*(dim+1))/2][n_q_points>0?n_q_points:1]; + + /** + * Stores the indices of the current cell. + */ + const unsigned int quad_no; + const unsigned int n_fe_components; + unsigned int cell; + unsigned int cell_type; + unsigned int cell_data_number; + bool at_irregular_cell; + unsigned int n_irreg_components_filled; + + /** + * A pointer to the Cartesian Jacobian + * information of the present cell. Only set + * to a useful value if on a Cartesian cell, + * otherwise zero. + */ + const Tensor<1,dim,vector_t> * cartesian; + + /** + * A pointer to the Jacobian information of + * the present cell. Only set to a useful + * value if on a non-Cartesian cell. + */ + const Tensor<2,dim,vector_t> * jacobian; + + /** + * A pointer to the Jacobian determinant of + * the present cell. If on a Cartesian cell or + * on a cell with constant Jacobian, this is + * just the Jacobian determinant, otherwise + * the Jacobian determinant times the + * quadrature weight. + */ + const vector_t * J_value; + + /** + * A pointer to the quadrature weights of the + * underlying quadrature formula. + */ + const vector_t * quadrature_weights; + + /** + * A pointer to the quadrature points on the + * present cell. + */ + const Point * quadrature_points; + + /** + * A pointer to the diagonal part of the + * Jacobian gradient on the present + * cell. Only set to a useful value if on a + * general cell with non-constant Jacobian. + */ + const Tensor<2,dim,vector_t> * jacobian_grad; + + /** + * A pointer to the upper diagonal part of the + * Jacobian gradient on the present cell. Only + * set to a useful value if on a general cell + * with non-constant Jacobian. + */ + const Tensor<1,(dim>1?dim*(dim-1)/2:1),Tensor<1,dim,vector_t> > * jacobian_grad_upper; + + /** + * Debug information to track whether we + * uninitialized fields are accessed. + */ + bool dof_values_initialized; + bool values_quad_initialized; + bool gradients_quad_initialized; + bool hessians_quad_initialized; + bool values_quad_submitted; + bool gradients_quad_submitted; +}; + + + +/** + * This class provides access to the data fields of the FEEvaluation + * classes. Generic access is achieved through the base class, and + * specializations for scalar and vector-valued elements are defined + * separately. + * + * @author Katharina Kormann and Martin Kronbichler, 2010, 2011 + */ +template +class FEEvaluationAccess : + public FEEvaluationBase +{ + public: + typedef VectorizedArray vector_t; + typedef Tensor<1,n_components,vector_t> value_type; + typedef Tensor<1,n_components,Tensor<1,dim,vector_t> > gradient_type; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + static const unsigned int dofs_per_cell = dofs_per_cell_; + static const unsigned int n_q_points = n_q_points_; + typedef FEEvaluationBase BaseClass; + + /** + * Constructor. Takes all data stored in + * MatrixFree. If applied to problems with + * more than one finite element or more than + * one quadrature formula selected during + * construction of @p matrix_free, @p + * fe_no and @p quad_no allow to select the + * appropriate components. + */ + FEEvaluationAccess (const MatrixFree &matrix_free, + const unsigned int fe_no = 0, + const unsigned int quad_no = 0); +}; + + + + +/** + * This class provides access to the data fields of the FEEvaluation + * classes. Partial specialization for scalar fields that defines access with + * simple data fields, i.e., scalars for the values and Tensor<1,dim> for the + * gradients. + * + * @author Katharina Kormann and Martin Kronbichler, 2010, 2011 + */ +template +class FEEvaluationAccess : + public FEEvaluationBase +{ + public: + typedef Number number_type; + typedef VectorizedArray vector_t; + typedef VectorizedArray value_type; + typedef Tensor<1,dim,vector_t> gradient_type; + static const unsigned int dimension = dim; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + static const unsigned int dofs_per_cell = dofs_per_cell_; + static const unsigned int n_q_points = n_q_points_; + typedef FEEvaluationBase BaseClass; + + /** + * Constructor. Takes all data stored in + * MatrixFree. If applied to problems with + * more than one finite element or more than + * one quadrature formula selected during + * construction of @p matrix_free, @p + * fe_no and @p quad_no allow to select the + * appropriate components. + */ + FEEvaluationAccess (const MatrixFree &matrix_free, + const unsigned int fe_no = 0, + const unsigned int quad_no = 0); + + /** + * Returns the value stored for the local + * degree of freedom with index @p dof. If the + * object is vector-valued, a vector-valued + * return argument is given. Note that when + * vectorization is enabled, values from + * several cells are grouped together. If @p + * set_dof_values was called last, the value + * corresponds to the one set there. If @p + * integrate was called last, it instead + * corresponds to the value of the integrated + * function with the test function of the + * given index. + */ + vector_t + get_dof_value (unsigned int dof) const; + + /** + * Write a value to the field containing the + * degrees of freedom with component @p + * dof. Access to the same field as through @p + * get_dof_value. + */ + void submit_dof_value (vector_t val_in, + unsigned int dof); + + /** + * Returns the value of a finite element + * function at quadrature point number @p + * q_point after a call to @p + * evaluate(true,...), or the value that + * has been stored there with a call to @p + * submit_value. If the object is + * vector-valued, a vector-valued return + * argument is given. Note that when + * vectorization is enabled, values from + * several cells are grouped together. + */ + vector_t + get_value (unsigned int q_point) const; + + /** + * Write a value to the field + * containing the values on quadrature + * points with component @p + * q_point. Access to the same field + * as through @p get_value. If applied + * before the function @p + * integrate(true,...) is called, this + * specifies the value which is tested + * by all basis function on the + * current cell and integrated over. + */ + void submit_value (vector_t val_in, + unsigned int q_point); + + /** + * Returns the gradient of a finite + * element function at quadrature + * point number @p q_point after a + * call to @p evaluate(...,true,...), + * or the value that has been stored + * there with a call to @p + * submit_gradient. + */ + gradient_type + get_gradient (unsigned int q_point) const; + + /** + * Write a gradient to the field + * containing the values on quadrature + * points with component @p + * q_point. Access to the same field + * as through @p get_gradient. If + * applied before the function @p + * integrate(...,true) is called, this + * specifies the gradient which is + * tested by all basis function + * gradients on the current cell and + * integrated over. + */ + void submit_gradient(gradient_type grad_in, + unsigned int q_point); + + /** + * Returns the Hessian of a finite + * element function at quadrature + * point number @p q_point after a + * call to @p evaluate(...,true). If + * only the diagonal part of the + * Hessian or its trace, the + * Laplacian, are needed, use the + * respective functions below. + */ + Tensor<2,dim,vector_t> + get_hessian (unsigned int q_point) const; + + /** + * Returns the diagonal of the Hessian + * of a finite element function at + * quadrature point number @p q_point + * after a call to @p + * evaluate(...,true). + */ + gradient_type + get_hessian_diagonal (unsigned int q_point) const; + + /** + * Returns the Laplacian of a finite + * element function at quadrature + * point number @p q_point after a + * call to @p evaluate(...,true). + */ + value_type + get_laplacian (unsigned int q_point) const; + + /** + * Takes values on quadrature points, + * multiplies by the Jacobian determinant and + * quadrature weights (JxW) and sums the + * values for all quadrature points on the + * cell. The result is a scalar, representing + * the integral over the function over the + * cell. If a vector-element is used, the + * resulting components are still + * separated. Moreover, if vectorization is + * enabled, the integral values of several + * cells are represented together. + */ + value_type + integrate_value (); +}; + + + +/** + * This class provides access to the data fields of the FEEvaluation + * classes. Partial specialization for fields with as many components as the + * underlying space dimension, i.e., values are of type Tensor<1,dim> and + * gradients of type Tensor<2,dim>. Provides some additional functions for + * access, like the symmetric gradient and divergence. + * + * @author Katharina Kormann and Martin Kronbichler, 2010, 2011 + */ +template +class FEEvaluationAccess : + public FEEvaluationBase +{ + public: + typedef VectorizedArray vector_t; + typedef Tensor<1,dim,vector_t> value_type; + typedef Tensor<2,dim,vector_t> gradient_type; + typedef SymmetricTensor<2,dim,vector_t> sym_gradient_type; + typedef Tensor<1,dim==2?1:dim,vector_t> curl_type; + + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + static const unsigned int dofs_per_cell = dofs_per_cell_; + static const unsigned int n_q_points = n_q_points_; + typedef FEEvaluationBase BaseClass; + + /** + * Constructor. Takes all data stored in + * MatrixFree. If applied to problems with + * more than one finite element or more than + * one quadrature formula selected during + * construction of @p matrix_free, @p + * fe_no and @p quad_no allow to select the + * appropriate components. + */ + FEEvaluationAccess (const MatrixFree &matrix_free, + const unsigned int fe_no = 0, + const unsigned int quad_no = 0); + + /** + * Returns the gradient of a finite element + * function at quadrature point number @p + * q_point after a call to @p + * evaluate(...,true,...). + */ + gradient_type + get_gradient (unsigned int q_point) const; + + /** + * Returns the divergence of a vector-valued + * finite element at quadrature point number + * @p q_point after a call to @p + * evaluate(...,true,...). + */ + vector_t + get_divergence (unsigned int q_point) const; + + /** + * Returns the symmetric gradient of a + * vector-valued finite element at + * quadrature point number @p q_point + * after a call to @p + * evaluate(...,true,...). It + * corresponds to 0.5 + * (grad+gradT). + */ + sym_gradient_type + get_symmetric_gradient (unsigned int q_point) const; + + /** + * Returns the curl of the vector field, + * $nabla \times v$ after a call to @p + * evaluate(...,true,...). + */ + curl_type + get_curl (unsigned int q_point) const; + + /** + * Returns the Hessian of a finite + * element function at quadrature + * point number @p q_point after a + * call to @p evaluate(...,true). If + * only the diagonal of the Hessian or + * its trace, the Laplacian, is + * needed, use the respective + * functions. + */ + Tensor<3,dim,vector_t> + get_hessian (unsigned int q_point) const; + + /** + * Returns the diagonal of the Hessian + * of a finite element function at + * quadrature point number @p q_point + * after a call to @p + * evaluate(...,true). + */ + gradient_type + get_hessian_diagonal (unsigned int q_point) const; + + /** + * Write a gradient to the field containing + * the values on quadrature points with + * component @p q_point. Access to the same + * field as through @p get_gradient. If + * applied before the function @p + * integrate(...,true) is called, + * this specifies the gradient which is tested + * by all basis function gradients on the + * current cell and integrated over. + */ + void submit_gradient(gradient_type grad_in, + unsigned int q_point); + + /** + * Write a gradient to the field containing + * the values on quadrature points with + * component @p q_point. This function is an + * alternative to the other submit_gradient + * function when using a system of fixed + * number of equations which happens to + * coincide with the dimension for some + * dimensions, but not all. To allow for + * dimension-independent programming, this + * function can be used instead. + */ + void submit_gradient(Tensor<1,dim,Tensor<1,dim,vector_t> > grad_in, + unsigned int q_point); + + /** + * Write a gradient to the field containing + * the values on quadrature points with + * component @p q_point. Access to the same + * field as through @p get_gradient. If + * applied before the function @p + * integrate(...,true) is called, + * this specifies the gradient which is tested + * by all basis function gradients on the + * current cell and integrated over. + */ + void submit_symmetric_gradient(sym_gradient_type grad_in, + unsigned int q_point); + + /** + * Write the components of a curl containing + * the values on quadrature point @p + * q_point. Access to the same data field as + * through @p get_gradient. + */ + void submit_curl (curl_type curl_in, + unsigned int q_point); +}; + + + +/** + * The class that provides all functions necessary to evaluate functions at + * quadrature points and cell integrations. In functionality, this class is + * similar to FEValues, however, it includes a lot of specialized + * functions that make it much faster (between 5 and 500 times as fast, + * depending on the polynomial order). Access to the data fields is provided + * through functionality in the class FEEvaluationAccess. + * + * This class is designed for general local finite element operations based on + * tensor products of 1D polynomials and quadrature points. Often, there are + * some symmetries or zeros in the unit cell data that allow for a more + * efficient operator application. FEEvaluation is specialized to standard + * FE_Q/FE_DGQ elements and quadrature points symmetric around 0.5 (like Gauss + * quadrature), and hence the most common situation. FEEvaluationGL is a + * specialization for elements where quadrature formula and support points are + * chosen so that a orthogonal relation between the values holds. This is the + * case for FE_Q based on Gauss-Lobatto support points with Gauss-Lobatto + * quadrature formula of the same order. + * + * This class has five template arguments: + * + * @param dim Dimension in which this class is to be used + * + * @param n_dofs_1d Number of degrees of freedom of the FE in 1D, usually + * fe_degree+1, for elements based on a tensor product + * + * @param n_q_points_1d Number of points in the quadrature formula in 1D, + * usually chosen as fe_degree+1 + * + * @param n_components Number of vector components when solving a system of + * PDEs. If the same operation is applied to several + * components of a PDE (e.g. a vector Laplace equation), they + * can be applied simultaneously with one call (and often + * more efficiently) + * + * @param Number Number format, usually @p double or @p float + * + * @author Katharina Kormann and Martin Kronbichler, 2010, 2011 + */ +template +class FEEvaluationGeneral : + public FEEvaluationAccess1?n_dofs_1d:1)*(dim>2?n_dofs_1d:1)), + (n_q_points_1d*(dim>1?n_q_points_1d:1)*(dim>2?n_q_points_1d:1)), + n_components,Number> +{ + public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + typedef FEEvaluationAccess1?n_dofs_1d:1)* + (dim>2?n_dofs_1d:1)), + (n_q_points_1d*(dim>1?n_q_points_1d:1)*(dim>2?n_q_points_1d:1)), + n_components, Number> BaseClass; + static const unsigned int dofs_per_cell = BaseClass::dofs_per_cell; + static const unsigned int n_q_points = BaseClass::n_q_points; + + /** + * Constructor. Takes all data stored in + * MatrixFree. If applied to problems with + * more than one finite element or more than + * one quadrature formula selected during + * construction of @p matrix_free, @p + * fe_no and @p quad_no allow to select the + * appropriate components. + */ + FEEvaluationGeneral (const MatrixFree &matrix_free, + const unsigned int fe_no = 0, + const unsigned int quad_no = 0); + + /** + * Evaluates the function values, the + * gradients, and the Laplacians of the FE + * function given at the DoF values in the + * input vector at the quadrature points. The + * function arguments specify which parts + * shall actually be computed. Needs to be + * called before the functions @p get_value(), + * @p get_gradient() or @p get_laplacian + * return useful information. + */ + void evaluate (bool evaluate_val, bool evaluate_grad, + bool evaluate_hess=false); + + /** + * This function takes the values and/or + * gradients that are stored on quadrature + * points, tests them by all the basis + * functions/gradients on the cell and + * performs the cell integration. The two + * function arguments @p integrate_val and @p + * integrate_grad are used to enable/disable + * some of values or gradients. + */ + void integrate (bool integrate_val, bool integrate_grad); + + /** + * Returns the q-th quadrature point stored in + * MappingInfo. + */ + Point quadrature_point (const unsigned int q_point) const; + +protected: + + /** + * Internal function that applies the shape + * function data of the tensor product in a + * given coordinate direction (first template + * argument), from polynomials to values on + * quadrature points (second flag set to true) + * or in an integration loop from values on + * quadrature points to values tested by + * different test function (second flag set to + * false), and if the result is to be added to + * some previous results or not. + */ + template + void apply_tensor_prod (const vector_t * shape_data, + const vector_t in [], + vector_t out []); +}; + + + +/** + * The class that provides all functions necessary to evaluate functions at + * quadrature points and cell integrations. In functionality, this class is + * similar to FEValues, however, it includes a lot of specialized + * functions that make it much faster (between 5 and 500, depending on the + * polynomial order). + * + * This class is a specialization of FEEvaluationGeneral designed for standard + * FE_Q or FE_DGQ elements and quadrature points symmetric around 0.5 (like + * Gauss quadrature), and hence the most common situation. + * + * This class has five template arguments: + * + * @param dim Dimension in which this class is to be used + * + * @param n_dofs_1d Number of degrees of freedom of the FE in 1D, usually + * fe_degree+1, for elements based on a tensor product + * + * @param n_q_points_1d Number of points in the quadrature formula in 1D, + * usually chosen as fe_degree+1 + * + * @param n_components Number of vector components when solving a system of + * PDEs. If the same operation is applied to several + * components of a PDE (e.g. a vector Laplace equation), they + * can be applied simultaneously with one call (and often + * more efficiently) + * + * @param Number Number format, usually @p double or @p float + * + * @author Katharina Kormann and Martin Kronbichler, 2010, 2011 + */ +template +class FEEvaluation : + public FEEvaluationGeneral +{ + public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + typedef FEEvaluationGeneral BaseClass; + static const unsigned int dofs_per_cell = BaseClass::dofs_per_cell; + static const unsigned int n_q_points = BaseClass::n_q_points; + + /** + * Constructor. Takes all data stored in + * MatrixFree. If applied to problems with + * more than one finite element or more than + * one quadrature formula selected during + * construction of @p matrix_free, @p + * fe_no and @p quad_no allow to select the + * appropriate components. + */ + FEEvaluation (const MatrixFree &matrix_free, + const unsigned int fe_no = 0, + const unsigned int quad_no = 0); + + /** + * Evaluates the function values, the + * gradients, and the Laplacians of the FE + * function given at the DoF values in the + * input vector at the quadrature points on + * the unit cell. The function arguments + * specify which parts shall actually be + * computed. Needs to be called before the + * functions @p get_value(), @p get_gradient() + * or @p get_laplacian give useful information + * (unless these values have been set + * manually). + */ + void evaluate (bool evaluate_val, bool evaluate_grad, + bool evaluate_hess=false); + + /** + * This function takes the values and/or + * gradients that are stored on quadrature + * points, tests them by all the basis + * functions/gradients on the cell and + * performs the cell integration. The two + * function arguments @p integrate_val and @p + * integrate_grad are used to enable/disable + * some of values or gradients. + */ + void integrate (bool integrate_val, bool integrate_grad); + +protected: + /** + * Internal function that applies the function + * values of the tensor product in a given + * coordinate direction (first template + * argument), from polynomials to values on + * quadrature points (second flag set to true) + * or in an integration loop from values on + * quadrature points to values tested by + * different test function (second flag set to + * false), and if the result is to be added to + * previous content in the data fields or + * not. + */ + template + void apply_values (const vector_t in [], vector_t out []); + + /** + * Internal function that applies the gradient + * operation of the tensor product in a given + * coordinate direction (first template + * argument), from polynomials to values on + * quadrature points (second flag set to true) + * or in an integration loop from values on + * quadrature points to values tested by + * different test function (second flag set to + * false), and if the result is to be added to + * previous content in the data fields or + * not. + */ + template + void apply_gradients (const vector_t in [], vector_t out []); + + /** + * Internal function that applies the second + * derivative operation (Hessian) of the + * tensor product in a given coordinate + * direction (first template argument), from + * polynomials to values on quadrature points + * (second flag set to true) or in an + * integration loop from values on quadrature + * points to values tested by different test + * function (second flag set to false), and if + * the result is to be added to previous + * content in the data fields or not. + */ + template + void apply_hessians (const vector_t in [], vector_t out []); +}; + + + +/** + * The class that provides all functions necessary to evaluate functions at + * quadrature points and cell integrations. In functionality, this class is + * similar to FEValues, however, it includes a lot of specialized + * functions that make it much faster (between 5 and 500, depending on the + * polynomial order). + * + * This class is a specialization of FEEvaluation for elements where + * quadrature formula and support points are chosen so that a orthonormal + * relation between the values holds. This is the case for FE_Q based on + * Gauss-Lobatto support points with Gauss-Lobatto quadrature formula of the + * same order (QGaussLobatto). In that case, application of values is trivial + * (as the transformation is the identity matrix), and application of + * gradients is considerably simpler (since all value applications in + * directions other than the gradient direction are again identity + * operations). + * + * This class has five template arguments: + * + * @param dim Dimension in which this class is to be used + * + * @param n_dofs_1d Number of degrees of freedom of the FE in 1D, usually + * fe_degree+1, for elements based on a tensor product + * + * @param n_q_points_1d Number of points in the quadrature formula in 1D, + * usually chosen as fe_degree+1 + * + * @param n_components Number of vector components when solving a system of + * PDEs. If the same operation is applied to several + * components of a PDE (e.g. a vector Laplace equation), they + * can be applied simultaneously with one call (and often + * more efficiently) + * + * @param Number Number format, usually @p double or @p float + * + * @author Katharina Kormann and Martin Kronbichler, 2010, 2011 + */ +template +class FEEvaluationGL : + public FEEvaluation +{ + public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + typedef FEEvaluation BaseClass; + static const unsigned int dofs_per_cell = BaseClass::dofs_per_cell; + static const unsigned int n_q_points = BaseClass::n_q_points; + + /** + * Constructor. Takes all data stored in + * MatrixFree. If applied to problems with + * more than one finite element or more than + * one quadrature formula selected during + * construction of @p matrix_free, @p + * fe_no and @p quad_no allow to select the + * appropriate components. + */ + FEEvaluationGL (const MatrixFree &matrix_free, + const unsigned int fe_no = 0, + const unsigned int quad_no = 0); + + /** + * Evaluates the function values, the + * gradients, and the Hessians of the FE + * function given at the DoF values in the + * input vector at the quadrature points of + * the unit cell. The function arguments + * specify which parts shall actually be + * computed. Needs to be called before the + * functions @p get_value(), @p get_gradient() + * or @p get_laplacian give useful information + * (unless these values have been set + * manually). + */ + void evaluate (bool evaluate_val, bool evaluate_grad, + bool evaluate_lapl=false); + + /** + * This function takes the values and/or + * gradients that are stored on quadrature + * points, tests them by all the basis + * functions/gradients on the cell and + * performs the cell integration. The two + * function arguments @p integrate_val and @p + * integrate_grad are used to enable/disable + * some of values or gradients. + */ + void integrate (bool integrate_val, bool integrate_grad); + +protected: + /** + * Internal function that applies the gradient + * operation of the tensor product in a given + * coordinate direction (first template + * argument), from polynomials to values on + * quadrature points (second flag set to true) + * or in an integration loop from values on + * quadrature points to values tested by + * different test function (second flag set to + * false), and if the result is to be added to + * some previous results or not. + */ + template + void apply_gradients (const vector_t in [], vector_t out []); +}; + + + + +/*----------------------- Inline functions ----------------------------------*/ + +#ifndef DOXYGEN + + +/*----------------------- FEEvaluationBase -------------------------------*/ + +template +inline +FEEvaluationBase:: +FEEvaluationBase (const MatrixFree &data_in, + const unsigned int fe_no_in, + const unsigned int quad_no_in) + : + matrix_info (data_in), + dof_info (data_in.get_dof_info(fe_no_in)), + constraint_pool (data_in.get_constraint_pool()), + mapping_info (data_in.get_mapping_info()), + active_fe_index (dof_info.fe_index_from_dofs_per_cell + (dofs_per_cell_ * dof_info.n_components)), + active_quad_index (mapping_info. + mapping_data_gen[quad_no_in]. + quad_index_from_n_q_points(n_q_points_)), + data (data_in.get_fe_evaluation + (fe_no_in, quad_no_in, active_fe_index, + active_quad_index)), + quad_no (quad_no_in), + n_fe_components (dof_info.n_components), + cell (numbers::invalid_unsigned_int), + cell_type (numbers::invalid_unsigned_int), + cartesian (0), + jacobian (0), + J_value (0), + quadrature_weights (mapping_info.mapping_data_gen[quad_no]. + quadrature_weights[active_quad_index].begin()), + quadrature_points (0), + jacobian_grad (0), + jacobian_grad_upper(0) +{ + Assert (matrix_info.indices_initialized() == true, + ExcNotInitialized()); + Assert (matrix_info.mapping_initialized() == true, + ExcNotInitialized()); + AssertDimension (matrix_info.get_size_info().n_vectors, n_vectors); + Assert (n_fe_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 " + "as the number of underlying vector components.")); + + + // do not check for correct dimensions of data + // fields here, should be done in derived + // classes +} + + + +template +inline +void +FEEvaluationBase:: +reinit (const unsigned int cell_in) +{ + AssertIndexRange (cell_in, dof_info.row_starts.size()-1); + AssertDimension (((dof_info.cell_active_fe_index.size() > 0) ? + dof_info.cell_active_fe_index[cell_in] : 0), + active_fe_index); + cell = cell_in; + cell_type = mapping_info.get_cell_type(cell); + cell_data_number = mapping_info.get_cell_data_index(cell); + + if (mapping_info.quadrature_points_initialized == true) + { + AssertIndexRange (cell_data_number, mapping_info. + mapping_data_gen[quad_no].rowstart_q_points.size()); + const unsigned int index = mapping_info.mapping_data_gen[quad_no]. + rowstart_q_points[cell]; + AssertIndexRange (index, mapping_info.mapping_data_gen[quad_no]. + quadrature_points.size()); + quadrature_points = + &mapping_info.mapping_data_gen[quad_no].quadrature_points[index]; + } + + if (cell_type == 0) + { + cartesian = &mapping_info.cartesian[cell_data_number].first; + J_value = &mapping_info.cartesian[cell_data_number].second; + } + else if (cell_type == 1) + { + jacobian = &mapping_info.linear[cell_data_number].first; + J_value = &mapping_info.linear[cell_data_number].second; + } + else + { + const unsigned int rowstart = mapping_info. + mapping_data_gen[quad_no].rowstart_jacobians[cell_data_number]; + AssertIndexRange (rowstart, mapping_info. + mapping_data_gen[quad_no].jacobians.size()); + jacobian = + &mapping_info.mapping_data_gen[quad_no].jacobians[rowstart]; + if (mapping_info.JxW_values_initialized == true) + { + AssertIndexRange (rowstart, mapping_info. + mapping_data_gen[quad_no].JxW_values.size()); + J_value = &(mapping_info.mapping_data_gen[quad_no]. + JxW_values[rowstart]); + } + if (mapping_info.second_derivatives_initialized == true) + { + AssertIndexRange(rowstart, mapping_info. + mapping_data_gen[quad_no].jacobians_grad_diag.size()); + jacobian_grad = &mapping_info.mapping_data_gen[quad_no]. + jacobians_grad_diag[rowstart]; + AssertIndexRange(rowstart, mapping_info. + mapping_data_gen[quad_no].jacobians_grad_upper.size()); + jacobian_grad_upper = &mapping_info.mapping_data_gen[quad_no]. + jacobians_grad_upper[rowstart]; + } + } + + 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; + gradients_quad_initialized = false; + hessians_quad_initialized = false; +#endif +} + + + +template +inline +unsigned int +FEEvaluationBase:: +get_cell_data_number () const +{ + Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + return cell_data_number; +} + + + +template +inline +unsigned int +FEEvaluationBase:: +get_cell_type () const +{ + Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + return cell_type; +} + + + +template +inline +const VectorizedArray * +FEEvaluationBase:: +begin_values () const +{ + Assert (values_quad_initialized || values_quad_submitted, + ExcNotInitialized()); + return &values_quad[0][0]; +} + + + +template +inline +VectorizedArray * +FEEvaluationBase:: +begin_values () +{ +#ifdef DEBUG + values_quad_submitted = true; +#endif + return &values_quad[0][0]; +} + + + +template +inline +const VectorizedArray * +FEEvaluationBase:: +begin_gradients () const +{ + Assert (gradients_quad_initialized || gradients_quad_submitted, + ExcNotInitialized()); + return &gradients_quad[0][0][0]; +} + + + +template +inline +VectorizedArray * +FEEvaluationBase:: +begin_gradients () +{ +#ifdef DEBUG + gradients_quad_submitted = true; +#endif + return &gradients_quad[0][0][0]; +} + + + +template +inline +const VectorizedArray * +FEEvaluationBase:: +begin_hessians () const +{ + Assert (hessians_quad_initialized, ExcNotInitialized()); + return &hessians_quad[0][0][0]; +} + + + +template +inline +VectorizedArray * +FEEvaluationBase:: +begin_hessians () +{ + return &hessians_quad[0][0][0]; +} + + + +namespace internal +{ + // write access to generic vectors that have + // operator (). + template + inline + typename VectorType::value_type & + vector_access (VectorType &vec, + const unsigned int entry) + { + return vec(entry); + } + + + + // read access to generic vectors that have + // operator (). + template + inline + typename VectorType::value_type + vector_access (const VectorType &vec, + const unsigned int entry) + { + return vec(entry); + } + + + + // write access to distributed MPI vectors + // that have operator [] to access data in + // local index space, which is what we use in + // DoFInfo and hence in read_dof_values etc. + template + inline + Number & + vector_access (parallel::distributed::Vector &vec, + const unsigned int entry) + { + return vec.local_element(entry); + } + + + + // read access to distributed MPI vectors that + // have operator [] to access data in local + // index space, which is what we use in + // DoFInfo and hence in read_dof_values etc. + template + inline + Number + vector_access (const parallel::distributed::Vector &vec, + const unsigned int entry) + { + return vec.local_element(entry); + } + + + + // this is to make sure that the parallel + // partitioning in the + // parallel::distributed::Vector is really the + // same as stored in MatrixFree + template + inline + void check_vector_compatibility (const VectorType &vec, + const internal::MatrixFreeFunctions::DoFInfo &dof_info) + { + AssertDimension (vec.size(), + dof_info.vector_partitioner->size()); + } + + template + inline + void check_vector_compatibility (const parallel::distributed::Vector &vec, + const internal::MatrixFreeFunctions::DoFInfo &dof_info) + { + Assert (vec.partitioners_are_compatible(*dof_info.vector_partitioner), + ExcMessage("The parallel layout of the given vector is not " + "compatible with the parallel partitioning in MatrixFree. " + "Use MatrixFree::initialize_dof_vector to get a " + "compatible vector.")); + } +} + + + +template +template +inline +void +FEEvaluationBase:: +read_dof_values (const VectorType &src) +{ + AssertDimension (n_components, n_fe_components); + // only need one component, but to avoid + // compiler warnings, use n_components copies + // here (but these will not be used) + const VectorType * src_data[n_components]; + for (unsigned int d=0; d +template +inline +void +FEEvaluationBase:: +read_dof_values (const std::vector &src, + const unsigned int first_index) +{ + AssertIndexRange (first_index, src.size()); + Assert (n_fe_components == 1, ExcNotImplemented()); + Assert ((n_fe_components == 1 ? + (first_index+n_components <= src.size()) : true), + ExcIndexRange (first_index + n_components, 0, src.size())); + const VectorType * src_data [n_components]; + for (unsigned int comp=0; comp +template +inline +void +FEEvaluationBase:: +read_dof_values (const std::vector &src, + const unsigned int first_index) +{ + AssertIndexRange (first_index, src.size()); + Assert (n_fe_components == 1, ExcNotImplemented()); + Assert ((n_fe_components == 1 ? + (first_index+n_components <= src.size()) : true), + ExcIndexRange (first_index + n_components, 0, src.size())); + const VectorType * src_data [n_components]; + for (unsigned int comp=0; comp +template +inline +void +FEEvaluationBase:: +read_dof_values (const VectorType * src[]) +{ + Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + + // loop over all local dofs. ind_local holds + // local number on cell, index iterates over + // the elements of index_local_to_global and + // dof_indices points to the global indices + // stored in index_local_to_global + const unsigned int * dof_indices = dof_info.begin_indices(cell); + const std::pair * indicators = + dof_info.begin_indicators(cell); + const std::pair * indicators_end = + dof_info.end_indicators(cell); + unsigned int ind_local = 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) + { + for (unsigned int comp=0; comp(values_dofs[comp]); + + // standard case where there are sufficiently + // many cells to fill all vectors + if (at_irregular_cell == false) + { + // check whether there is any constraint on + // the current cell + if (indicators != indicators_end) + { + for ( ; indicators != indicators_end; ++indicators) + { + // run through values up to next constraint + for (unsigned int j=0; jfirst; ++j) + for (unsigned int comp=0; compfirst; + dof_indices += indicators->first; + + // constrained case: build the local value as + // a linear combination of the global value + // according to constraints + Number value [n_components]; + for (unsigned int comp=0; compsecond); + const unsigned int row_length = + constraint_pool.row_length(indicators->second); + for (unsigned int k=0; k 0, ExcInternalError()); + for ( ; indicators != indicators_end; ++indicators) + { + for(unsigned int j=0; jfirst; ++j) + { + // non-constrained case: copy the data from + // the global vector, src, to the local one, + // local_src. + for (unsigned int comp=0; comp= n_irreg_components_filled) + { + for (unsigned int comp=0; compfirst; + + // constrained case: build the local value as + // a linear combination of the global value + // according to constraint + Number value [n_components]; + for (unsigned int comp=0; compsecond); + const unsigned int row_length = + constraint_pool.row_length(indicators->second); + for (unsigned int k=0; k= n_irreg_components_filled) + { + for (unsigned int comp=0; comp= n_irreg_components_filled) + { + for (unsigned int comp=0; comp(values_dofs[0]); + if (at_irregular_cell == false) + { + // check whether there is any constraint on + // the current cell + if (indicators != indicators_end) + { + for ( ; indicators != indicators_end; ++indicators) + { + // run through values up to next constraint + for (unsigned int j=0; jfirst; ++j) + local_src_number[ind_local+j] = + internal::vector_access (*src[0], dof_indices[j]); + ind_local += indicators->first; + dof_indices += indicators->first; + + // constrained case: build the local value as + // a linear combination of the global value + // according to constraints + Number value = 0; + const Number * data_val = + constraint_pool.begin(indicators->second); + const unsigned int row_length = + constraint_pool.row_length(indicators->second); + for (unsigned int k=0; k(total_dofs_per_cell)); + for (unsigned int j=0; j 0, ExcInternalError()); + for ( ; indicators != indicators_end; ++indicators) + { + for(unsigned int j=0; jfirst; ++j) + { + // non-constrained case: copy the data from + // the global vector, src, to the local one, + // local_src. + local_src_number[ind_local] = + internal::vector_access (*src[0], dof_indices[j]); + + // here we jump over all the components that + // are artificial + ++ind_local; + while (ind_local % n_vectors >= n_irreg_components_filled) + { + local_src_number[ind_local] = 0.; + ++ind_local; + } + } + dof_indices += indicators->first; + + // constrained case: build the local value as + // a linear combination of the global value + // according to constraint + Number value = 0; + const Number * data_val = + constraint_pool.begin(indicators->second); + const unsigned int row_length = + constraint_pool.row_length(indicators->second); + for (unsigned int k=0; k= n_irreg_components_filled) + { + local_src_number[ind_local] = 0.; + ++ind_local; + } + } + for(; ind_local= n_irreg_components_filled) + { + local_src_number[ind_local] = 0.; + ++ind_local; + } + } + } + } + +#ifdef DEBUG + dof_values_initialized = true; +#endif +} + + + + + + +template +template +inline +void +FEEvaluationBase:: +read_dof_values_plain (const VectorType &src) +{ + AssertDimension (n_components, n_fe_components); + // only need one component, but to avoid + // compiler warnings, use n_components copies + // here (but these will not be used) + const VectorType * src_data[n_components]; + for (unsigned int d=0; d +template +inline +void +FEEvaluationBase:: +read_dof_values_plain (const std::vector &src, + const unsigned int first_index) +{ + AssertIndexRange (first_index, src.size()); + Assert (n_fe_components == 1, ExcNotImplemented()); + Assert ((n_fe_components == 1 ? + (first_index+n_components <= src.size()) : true), + ExcIndexRange (first_index + n_components, 0, src.size())); + const VectorType * src_data [n_components]; + for (unsigned int comp=0; comp +template +inline +void +FEEvaluationBase:: +read_dof_values_plain (const std::vector &src, + const unsigned int first_index) +{ + AssertIndexRange (first_index, src.size()); + Assert (n_fe_components == 1, ExcNotImplemented()); + Assert ((n_fe_components == 1 ? + (first_index+n_components <= src.size()) : true), + ExcIndexRange (first_index + n_components, 0, src.size())); + const VectorType * src_data [n_components]; + for (unsigned int comp=0; comp +template +inline +void +FEEvaluationBase:: +read_dof_values_plain (const VectorType * src[]) +{ + Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + Assert (dof_info.store_plain_indices == true, ExcNotInitialized()); + + // loop over all local dofs. ind_local holds + // local number on cell, index iterates over + // the elements of index_local_to_global and + // dof_indices points to the global indices + // stored in index_local_to_global + const unsigned int * dof_indices = dof_info.begin_indices_plain(cell); + + // 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) + { + for (unsigned int comp=0; comp(values_dofs[comp]); + + // standard case where there are sufficiently + // many cells to fill all vectors + if (at_irregular_cell == false) + { + for (unsigned int j=0; j 0, ExcInternalError()); + for(unsigned int ind_local=0; ind_local= n_irreg_components_filled) + { + for (unsigned int comp=0; comp(values_dofs[0]); + if (at_irregular_cell == false) + { + for (unsigned int j=0; j 0, ExcInternalError()); + for(unsigned int ind_local=0; ind_local= n_irreg_components_filled) + { + local_src_number[ind_local] = 0.; + ++ind_local; + } + } + } + } + +#ifdef DEBUG + dof_values_initialized = true; +#endif +} + + + +template +template +inline +void +FEEvaluationBase:: +distribute_local_to_global (VectorType &dst) const +{ + AssertDimension (n_components, n_fe_components); + // only need one component, but to avoid + // compiler warnings, use n_components copies + // here (but these will not be used) + VectorType * dst_data [n_components]; + for (unsigned int d=0; d +template +inline +void +FEEvaluationBase:: +distribute_local_to_global (std::vector &dst, + const unsigned int first_index) const +{ + AssertIndexRange (first_index, dst.size()); + Assert (n_fe_components == 1, ExcNotImplemented()); + Assert ((n_fe_components == 1 ? + (first_index+n_components <= dst.size()) : true), + ExcIndexRange (first_index + n_components, 0, dst.size())); + + VectorType * dst_data [n_components]; + for (unsigned int comp=0; comp +template +inline +void +FEEvaluationBase:: +distribute_local_to_global (std::vector &dst, + const unsigned int first_index) const +{ + AssertIndexRange (first_index, dst.size()); + Assert (n_fe_components == 1, ExcNotImplemented()); + Assert ((n_fe_components == 1 ? + (first_index+n_components <= dst.size()) : true), + ExcIndexRange (first_index + n_components, 0, dst.size())); + + VectorType * dst_data [n_components]; + for (unsigned int comp=0; comp +template +inline +void +FEEvaluationBase:: +distribute_local_to_global (VectorType * dst[]) const +{ + Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + Assert (dof_values_initialized==true, + internal::ExcAccessToUninitializedField()); + + // loop over all local dofs. ind_local holds + // local number on cell, index iterates over + // the elements of index_local_to_global and + // dof_indices points to the global indices + // stored in index_local_to_global + const unsigned int * dof_indices = dof_info.begin_indices(cell); + const std::pair * indicators = + dof_info.begin_indicators(cell); + const std::pair * indicators_end = + dof_info.end_indicators(cell); + unsigned int ind_local = 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) + { + for (unsigned int comp=0; comp(values_dofs[comp]); + if (at_irregular_cell == false) + { + // check whether there is no constraint at all + if (indicators != indicators_end) + { + // run from one constraint to the next + for ( ; indicators != indicators_end; ++indicators) + { + // distribute values up to the constraint + // (values not constrained) + for (unsigned int j=0; jfirst; ++j) + for (unsigned int comp=0; compfirst; + ind_local += indicators->first; + + // constrained case: build the local value as + // a linear combination of the global value + // according to constraint + const Number * data_val = + constraint_pool.begin(indicators->second); + const unsigned int row_length = + constraint_pool.row_length(indicators->second); + for (unsigned int k=0; k 0, ExcInternalError()); + for ( ; indicators != indicators_end; ++indicators) + { + for(unsigned int j=0; jfirst; ++j) + { + // non-constrained case: copy the data from + // the local vector to the global one. + for (unsigned int comp=0; compfirst; + + // constrained case: distribute according to + // the constraint + const Number * data_val = + constraint_pool.begin(indicators->second); + const unsigned int row_length = + constraint_pool.row_length(indicators->second); + for (unsigned int k=0; k(values_dofs[0]); + if (at_irregular_cell == false) + { + // check whether there is no constraint at all + if (indicators != indicators_end) + { + // run from one constraint to the next + for ( ; indicators != indicators_end; ++indicators) + { + // distribute values up to the constraint + // (values not constrained) + for (unsigned int j=0; jfirst; ++j) + internal::vector_access (*dst[0], dof_indices[j]) + += local_dst_number[ind_local+j]; + dof_indices += indicators->first; + ind_local += indicators->first; + + // constrained case: build the local value as + // a linear combination of the global value + // according to constraint + const Number * data_val = + constraint_pool.begin(indicators->second); + const unsigned int row_length = + constraint_pool.row_length(indicators->second); + for (unsigned int k=0; k(total_dofs_per_cell)); + for (unsigned int j=0; j 0, ExcInternalError()); + for ( ; indicators != indicators_end; ++indicators) + { + for(unsigned int j=0; jfirst; ++j) + { + // non-constrained case: copy the data from + // the local vector to the global one. + internal::vector_access (*dst[0], dof_indices[j]) + += local_dst_number[ind_local]; + ++ind_local; + if (ind_local % n_vectors == n_irreg_components_filled) + ind_local += n_vectors-n_irreg_components_filled; + } + dof_indices += indicators->first; + + // constrained case: distribute according to + // the constraint + const Number * data_val = + constraint_pool.begin(indicators->second); + const unsigned int row_length = + constraint_pool.row_length(indicators->second); + for (unsigned int k=0; k +template +inline +void +FEEvaluationBase:: +set_dof_values (VectorType &dst) const +{ + AssertDimension (n_components, n_fe_components); + // only need one component, but to avoid + // compiler warnings, use n_components copies + // here (but these will not be used) + VectorType * dst_data [n_components]; + for (unsigned int d=0; d +template +inline +void +FEEvaluationBase:: +set_dof_values (std::vector &dst, + const unsigned int first_index) const +{ + AssertIndexRange (first_index, dst.size()); + Assert (n_fe_components == 1, ExcNotImplemented()); + Assert ((n_fe_components == 1 ? + (first_index+n_components <= dst.size()) : true), + ExcIndexRange (first_index + n_components, 0, dst.size())); + + VectorType * dst_data [n_components]; + for (unsigned int comp=0; comp +template +inline +void +FEEvaluationBase:: +set_dof_values (std::vector &dst, + const unsigned int first_index) const +{ + AssertIndexRange (first_index, dst.size()); + Assert (n_fe_components == 1, ExcNotImplemented()); + Assert ((n_fe_components == 1 ? + (first_index+n_components <= dst.size()) : true), + ExcIndexRange (first_index + n_components, 0, dst.size())); + + VectorType * dst_data [n_components]; + for (unsigned int comp=0; comp +template +inline +void +FEEvaluationBase:: +set_dof_values (VectorType * dst[]) const +{ + Assert (cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + Assert (dof_values_initialized==true, + internal::ExcAccessToUninitializedField()); + + // loop over all local dofs. ind_local holds + // local number on cell, index iterates over + // the elements of index_local_to_global and + // glob_indices points to the global indices + // stored in index_local_to_global + const unsigned int * dof_indices = dof_info.begin_indices(cell); + const std::pair * indicators = + dof_info.begin_indicators(cell); + const std::pair * indicators_end = + dof_info.end_indicators(cell); + unsigned int ind_local = 0; + + if (n_fe_components == 1) + { + for (unsigned int comp=0; compsize(), + dof_info.vector_partitioner->size()); + + const Number * local_dst_number [n_components]; + for (unsigned int comp=0; comp(values_dofs[comp]); + if (at_irregular_cell == false) + { + // check whether there is no constraint at all + if (indicators != indicators_end) + { + // run from one constraint to the next + for ( ; indicators != indicators_end; ++indicators) + { + // distribute values up to the constraint + // (values not constrained) + for (unsigned int j=0; jfirst; ++j) + for (unsigned int comp=0; compfirst; + ind_local += indicators->first; + + // jump over constraints + const unsigned int row_length = + constraint_pool.row_length(indicators->second); + dof_indices += row_length; + ++ind_local; + } + // distribute values after the last constraint + // (values not constrained) + for(; ind_local 0, ExcInternalError()); + for ( ; indicators != indicators_end; ++indicators) + { + for(unsigned int j=0; jfirst; ++j) + { + // non-constrained case + for (unsigned int comp=0; compfirst; + + // jump over constraint + const unsigned int row_length = + constraint_pool.row_length(indicators->second); + dof_indices += row_length; + ++ind_local; + if (ind_local % n_vectors == n_irreg_components_filled) + ind_local += n_vectors-n_irreg_components_filled; + } + for(; ind_localsize(), + dof_info.vector_partitioner->size()); + Assert (n_fe_components == n_components, ExcNotImplemented()); + const unsigned int total_dofs_per_cell = + dofs_per_cell * n_vectors * n_components; + const Number * local_dst_number = + reinterpret_cast(values_dofs[0]); + + if (at_irregular_cell == false) + { + // check whether there is no constraint at all + if (indicators != indicators_end) + { + // run from one constraint to the next + for ( ; indicators != indicators_end; ++indicators) + { + // distribute values up to the constraint + // (values not constrained) + for (unsigned int j=0; jfirst; ++j) + internal::vector_access (*dst[0], dof_indices[j]) + = local_dst_number[ind_local+j]; + dof_indices += indicators->first; + ind_local += indicators->first; + + // jump over constraints + const unsigned int row_length = + constraint_pool.row_length(indicators->second); + dof_indices += row_length; + ++ind_local; + } + // distribute values after the last constraint + // (values not constrained) + for(; ind_local 0, ExcInternalError()); + for ( ; indicators != indicators_end; ++indicators) + { + for(unsigned int j=0; jfirst; ++j) + { + // non-constrained case + internal::vector_access (*dst[0], dof_indices[j]) + = local_dst_number[ind_local]; + ++ind_local; + if (ind_local % n_vectors == n_irreg_components_filled) + ind_local += n_vectors-n_irreg_components_filled; + } + dof_indices += indicators->first; + + // jump over constraint + const unsigned int row_length = + constraint_pool.row_length(indicators->second); + dof_indices += row_length; + ++ind_local; + if (ind_local % n_vectors == n_irreg_components_filled) + ind_local += n_vectors-n_irreg_components_filled; + } + for(; ind_local +inline +Tensor<1,n_components,VectorizedArray > +FEEvaluationBase:: +get_dof_value (unsigned int dof) const +{ + AssertIndexRange (dof, dofs_per_cell); + Tensor<1,n_components,vector_t> return_value (false); + for(unsigned int comp=0;compvalues_dofs[comp][dof]; + return return_value; +} + + + +template +inline +Tensor<1,n_components,VectorizedArray > +FEEvaluationBase:: +get_value (unsigned int q_point) const +{ + Assert (this->values_quad_initialized==true, + internal::ExcAccessToUninitializedField()); + AssertIndexRange (q_point, n_q_points); + Tensor<1,n_components,vector_t> return_value (false); + for(unsigned int comp=0;compvalues_quad[comp][q_point]; + return return_value; +} + + + +template +inline +Tensor<1,n_components,Tensor<1,dim,VectorizedArray > > +FEEvaluationBase:: +get_gradient (unsigned int q_point) const +{ + Assert (this->gradients_quad_initialized==true, + internal::ExcAccessToUninitializedField()); + AssertIndexRange (q_point, n_q_points); + + Tensor<1,n_components,Tensor<1,dim,vector_t> > grad_out (false); + + // Cartesian cell + if (this->cell_type == 0) + { + for (unsigned int comp=0;compgradients_quad[comp][d][q_point] * + cartesian[0][d]); + } + // cell with general Jacobian + else if (this->cell_type == 2) + { + for(unsigned int comp=0;compgradients_quad[comp][0][q_point]); + for (unsigned e=1; egradients_quad[comp][e][q_point]); + } + } + } + // cell with general Jacobian, but constant + // within the cell + else // if (this->cell_type == 1) + { + for(unsigned int comp=0;compgradients_quad[comp][0][q_point]); + for (unsigned e=1; egradients_quad[comp][e][q_point]); + } + } + } + return grad_out; +} + + + +template +inline +Tensor<1,n_components,Tensor<2,dim,VectorizedArray > > +FEEvaluationBase:: +get_hessian (unsigned int q_point) const +{ + Assert (this->hessians_quad_initialized==true, + internal::ExcAccessToUninitializedField()); + AssertIndexRange (q_point, n_q_points); + + Tensor<2,dim,vector_t> hessian_out [n_components]; + + // Cartesian cell + if (this->cell_type == 0) + { + const Tensor<1,dim,vector_t> &jac = cartesian[0]; + for (unsigned int comp=0;comphessians_quad[comp][d][q_point] * + jac[d] * jac[d]); + switch (dim) + { + case 1: break; + case 2: + hessian_out[comp][0][1] = (this->hessians_quad[comp][2][q_point] * + jac[0] * jac[1]); + break; + case 3: + hessian_out[comp][0][1] = (this->hessians_quad[comp][3][q_point] * + jac[0] * jac[1]); + hessian_out[comp][0][2] = (this->hessians_quad[comp][4][q_point] * + jac[0] * jac[2]); + hessian_out[comp][1][2] = (this->hessians_quad[comp][5][q_point] * + jac[1] * jac[2]); + break; + default: Assert (false, ExcNotImplemented()); + } + for (unsigned int e=d+1; ecell_type == 2) + { + Assert (this->mapping_info.second_derivatives_initialized == true, + ExcNotInitialized()); + const Tensor<2,dim,vector_t> & jac = jacobian[q_point]; + const Tensor<2,dim,vector_t> & jac_grad = jacobian_grad[q_point]; + const typename internal::MatrixFreeFunctions::MappingInfo::tensorUT + & jac_grad_UT = jacobian_grad_upper[q_point]; + for(unsigned int comp=0;comphessians_quad[comp][0][q_point]; + break; + case 2: + tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] + + jac[d][1] * this->hessians_quad[comp][2][q_point]); + tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][2][q_point] + + jac[d][1] * this->hessians_quad[comp][1][q_point]); + break; + case 3: + tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] + + jac[d][1] * this->hessians_quad[comp][3][q_point] + + jac[d][2] * this->hessians_quad[comp][4][q_point]); + tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][3][q_point] + + jac[d][1] * this->hessians_quad[comp][1][q_point] + + jac[d][2] * this->hessians_quad[comp][5][q_point]); + tmp[2][d] = (jac[d][0] * this->hessians_quad[comp][4][q_point] + + jac[d][1] * this->hessians_quad[comp][5][q_point] + + jac[d][2] * this->hessians_quad[comp][2][q_point]); + break; + default: Assert (false, ExcNotImplemented()); + } + } + // compute first part of hessian, + // J * tmp = J * hess_unit(u) * J^T + for (unsigned int d=0; dgradients_quad[comp][e][q_point]); + + // add off-diagonal part of J' * grad(u) + for (unsigned int d=0, count=0; dgradients_quad[comp][f][q_point]); + + // take symmetric part + for (unsigned int d=0; dcell_type == 1) + { + const Tensor<2,dim,vector_t> &jac = jacobian[0]; + for(unsigned int comp=0;comphessians_quad[comp][0][q_point]; + break; + case 2: + tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] + + jac[d][1] * this->hessians_quad[comp][2][q_point]); + tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][2][q_point] + + jac[d][1] * this->hessians_quad[comp][1][q_point]); + break; + case 3: + tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] + + jac[d][1] * this->hessians_quad[comp][3][q_point] + + jac[d][2] * this->hessians_quad[comp][4][q_point]); + tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][3][q_point] + + jac[d][1] * this->hessians_quad[comp][1][q_point] + + jac[d][2] * this->hessians_quad[comp][5][q_point]); + tmp[2][d] = (jac[d][0] * this->hessians_quad[comp][4][q_point] + + jac[d][1] * this->hessians_quad[comp][5][q_point] + + jac[d][2] * this->hessians_quad[comp][2][q_point]); + break; + default: Assert (false, ExcNotImplemented()); + } + } + // compute first part of hessian, + // J * tmp = J * hess_unit(u) * J^T + for (unsigned int d=0; d >(hessian_out); +} + + + +template +inline +Tensor<1,n_components,Tensor<1,dim,VectorizedArray > > +FEEvaluationBase:: +get_hessian_diagonal (unsigned int q_point) const +{ + Assert (this->hessians_quad_initialized==true, + internal::ExcAccessToUninitializedField()); + AssertIndexRange (q_point, n_q_points); + + Tensor<1,n_components,Tensor<1,dim,vector_t> > hessian_out (false); + + // Cartesian cell + if (this->cell_type == 0) + { + const Tensor<1,dim,vector_t> &jac = cartesian[0]; + for (unsigned int comp=0;comphessians_quad[comp][d][q_point] * + jac[d] * jac[d]); + } + // cell with general Jacobian + else if (this->cell_type == 2) + { + Assert (this->mapping_info.second_derivatives_initialized == true, + ExcNotInitialized()); + const Tensor<2,dim,vector_t> &jac = jacobian[q_point]; + const Tensor<2,dim,vector_t> &jac_grad = jacobian_grad[q_point]; + for(unsigned int comp=0;comphessians_quad[comp][0][q_point]; + break; + case 2: + tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] + + jac[d][1] * this->hessians_quad[comp][2][q_point]); + tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][2][q_point] + + jac[d][1] * this->hessians_quad[comp][1][q_point]); + break; + case 3: + tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] + + jac[d][1] * this->hessians_quad[comp][3][q_point] + + jac[d][2] * this->hessians_quad[comp][4][q_point]); + tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][3][q_point] + + jac[d][1] * this->hessians_quad[comp][1][q_point] + + jac[d][2] * this->hessians_quad[comp][5][q_point]); + tmp[2][d] = (jac[d][0] * this->hessians_quad[comp][4][q_point] + + jac[d][1] * this->hessians_quad[comp][5][q_point] + + jac[d][2] * this->hessians_quad[comp][2][q_point]); + break; + default: Assert (false, ExcNotImplemented()); + } + } + // compute only the trace part of hessian, + // J * tmp = J * hess_unit(u) * J^T + for (unsigned int d=0; dgradients_quad[comp][e][q_point]); + } + } + // cell with general Jacobian, but constant + // within the cell + else // if (this->cell_type == 1) + { + const Tensor<2,dim,vector_t> & jac = jacobian[0]; + for(unsigned int comp=0;comphessians_quad[comp][0][q_point]; + break; + case 2: + tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] + + jac[d][1] * this->hessians_quad[comp][2][q_point]); + tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][2][q_point] + + jac[d][1] * this->hessians_quad[comp][1][q_point]); + break; + case 3: + tmp[0][d] = (jac[d][0] * this->hessians_quad[comp][0][q_point] + + jac[d][1] * this->hessians_quad[comp][3][q_point] + + jac[d][2] * this->hessians_quad[comp][4][q_point]); + tmp[1][d] = (jac[d][0] * this->hessians_quad[comp][3][q_point] + + jac[d][1] * this->hessians_quad[comp][1][q_point] + + jac[d][2] * this->hessians_quad[comp][5][q_point]); + tmp[2][d] = (jac[d][0] * this->hessians_quad[comp][4][q_point] + + jac[d][1] * this->hessians_quad[comp][5][q_point] + + jac[d][2] * this->hessians_quad[comp][2][q_point]); + break; + default: Assert (false, ExcNotImplemented()); + } + } + // compute only the trace part of hessian, + // J * tmp = J * hess_unit(u) * J^T + for (unsigned int d=0; d +inline +Tensor<1,n_components,VectorizedArray > +FEEvaluationBase:: +get_laplacian (unsigned int q_point) const +{ + Assert (this->hessians_quad_initialized==true, + internal::ExcAccessToUninitializedField()); + AssertIndexRange (q_point, n_q_points); + Tensor<1,n_components,vector_t> laplacian_out (false); + const Tensor<1,n_components,Tensor<1,dim,vector_t> > hess_diag + = get_hessian_diagonal(q_point); + for (unsigned int comp=0; comp +inline +void +FEEvaluationBase:: +submit_dof_value (Tensor<1,n_components,VectorizedArray > val_in, + unsigned int dof) +{ +#ifdef DEBUG + this->dof_values_initialized = true; +#endif + AssertIndexRange (dof, dofs_per_cell); + for (unsigned int comp=0;compvalues_dofs[comp][dof] = val_in[comp]; +} + + + +template +inline +void +FEEvaluationBase:: +submit_value (Tensor<1,n_components,VectorizedArray > val_in, + unsigned int q_point) +{ +#ifdef DEBUG + Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + AssertIndexRange (q_point, n_q_points); + this->values_quad_submitted = true; +#endif + if (this->cell_type == 2) + { + const vector_t JxW = J_value[q_point]; + for (unsigned int comp=0; compvalues_quad[comp][q_point] = val_in[comp] * JxW; + } + else //if (this->cell_type < 2) + { + const vector_t JxW = J_value[0] * quadrature_weights[q_point]; + for (unsigned int comp=0; compvalues_quad[comp][q_point] = val_in[comp] * JxW; + } +} + + + +template +inline +void +FEEvaluationBase:: +submit_gradient (Tensor<1,n_components, + Tensor<1,dim,VectorizedArray > > grad_in, + unsigned int q_point) +{ +#ifdef DEBUG + Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + AssertIndexRange (q_point, n_q_points); + this->gradients_quad_submitted = true; +#endif + if (this->cell_type == 0) + { + const vector_t JxW = J_value[0] * quadrature_weights[q_point]; + for (unsigned int comp=0;compgradients_quad[comp][d][q_point] = (grad_in[comp][d] * + cartesian[0][d] * JxW); + } + else if (this->cell_type == 2) + { + for (unsigned int comp=0; compgradients_quad[comp][d][q_point] = new_val * J_value[q_point]; + } + } + else //if (this->cell_type == 1) + { + const vector_t JxW = J_value[0] * quadrature_weights[q_point]; + for (unsigned int comp=0; compgradients_quad[comp][d][q_point] = new_val * JxW; + } + } +} + + + +template +inline +Tensor<1,n_components,VectorizedArray > +FEEvaluationBase:: +integrate_value () +{ +#ifdef DEBUG + Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + Assert (this->values_quad_submitted == true, + internal::ExcAccessToUninitializedField()); +#endif + Tensor<1,n_components,vector_t> return_value (false); + for (unsigned int comp=0; compvalues_quad[comp][0]; + for (unsigned int q=0; qvalues_quad[comp][q]; + return (return_value); +} + + + +/*----------------------- FEEvaluationAccess -------------------------------*/ + + +template +inline +FEEvaluationAccess:: +FEEvaluationAccess (const MatrixFree &data_in, + const unsigned int fe_no, + const unsigned int quad_no_in) + : + FEEvaluationBase + (data_in, fe_no, quad_no_in) +{} + + + + +/*-------------------- FEEvaluationAccess scalar --------------------------*/ + + +template +inline +FEEvaluationAccess:: +FEEvaluationAccess (const MatrixFree &data_in, + const unsigned int fe_no, + const unsigned int quad_no_in) + : + FEEvaluationBase + (data_in, fe_no, quad_no_in) +{} + + + +template +inline +VectorizedArray +FEEvaluationAccess:: +get_dof_value (unsigned int dof) const +{ + AssertIndexRange (dof, dofs_per_cell); + return this->values_dofs[0][dof]; +} + + + +template +inline +VectorizedArray +FEEvaluationAccess:: +get_value (unsigned int q_point) const +{ + Assert (this->values_quad_initialized==true, + internal::ExcAccessToUninitializedField()); + AssertIndexRange (q_point, n_q_points); + return this->values_quad[0][q_point]; +} + + + +template +inline +Tensor<1,dim,VectorizedArray > +FEEvaluationAccess:: +get_gradient (unsigned int q_point) const +{ + // could use the base class gradient, but that + // involves too many inefficient + // initialization + + Assert (this->gradients_quad_initialized==true, + internal::ExcAccessToUninitializedField()); + AssertIndexRange (q_point, n_q_points); + + Tensor<1,dim,vector_t> grad_out (false); + + // Cartesian cell + if (this->cell_type == 0) + { + for (unsigned int d=0; dgradients_quad[0][d][q_point] * + this->cartesian[0][d]); + } + // cell with general Jacobian + else if (this->cell_type == 2) + { + for (unsigned int d=0; djacobian[q_point][d][0] * + this->gradients_quad[0][0][q_point]); + for (unsigned e=1; ejacobian[q_point][d][e] * + this->gradients_quad[0][e][q_point]); + } + } + // cell with general Jacobian, but constant + // within the cell + else // if (this->cell_type == 1) + { + for (unsigned int d=0; djacobian[0][d][0] * + this->gradients_quad[0][0][q_point]); + for (unsigned e=1; ejacobian[0][d][e] * + this->gradients_quad[0][e][q_point]); + } + } + return grad_out; +} + + + +template +inline +Tensor<2,dim,VectorizedArray > +FEEvaluationAccess:: +get_hessian (unsigned int q_point) const +{ + return BaseClass::get_hessian(q_point)[0]; +} + + + +template +inline +Tensor<1,dim,VectorizedArray > +FEEvaluationAccess:: +get_hessian_diagonal (unsigned int q_point) const +{ + return BaseClass::get_hessian_diagonal(q_point)[0]; +} + + + +template +inline +VectorizedArray +FEEvaluationAccess:: +get_laplacian (unsigned int q_point) const +{ + return BaseClass::get_laplacian(q_point)[0]; +} + + + +template +inline +void +FEEvaluationAccess:: +submit_dof_value (VectorizedArray val_in, + unsigned int dof) +{ +#ifdef DEBUG + this->dof_values_initialized = true; + AssertIndexRange (dof, dofs_per_cell); +#endif + this->values_dofs[0][dof] = val_in; +} + + + +template +inline +void +FEEvaluationAccess:: +submit_value (VectorizedArray val_in, + unsigned int q_point) +{ +#ifdef DEBUG + Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + AssertIndexRange (q_point, n_q_points); + this->values_quad_submitted = true; +#endif + if (this->cell_type == 2) + { + const vector_t JxW = this->J_value[q_point]; + this->values_quad[0][q_point] = val_in * JxW; + } + else //if (this->cell_type < 2) + { + const vector_t JxW = this->J_value[0] * this->quadrature_weights[q_point]; + this->values_quad[0][q_point] = val_in * JxW; + } +} + + + +template +inline +void +FEEvaluationAccess:: +submit_gradient (Tensor<1,dim,VectorizedArray > grad_in, + unsigned int q_point) +{ +#ifdef DEBUG + Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + AssertIndexRange (q_point, n_q_points); + this->gradients_quad_submitted = true; +#endif + if (this->cell_type == 0) + { + const vector_t JxW = this->J_value[0] * this->quadrature_weights[q_point]; + for (unsigned int d=0; dgradients_quad[0][d][q_point] = (grad_in[d] * + this->cartesian[0][d] * JxW); + } + else if (this->cell_type == 2) + { + for (unsigned int d=0; djacobian[q_point][0][d] * grad_in[0]; + for (unsigned e=1; ejacobian[q_point][e][d] * grad_in[e]; + this->gradients_quad[0][d][q_point] = new_val * this->J_value[q_point]; + } + } + else //if (this->cell_type == 1) + { + const vector_t JxW = this->J_value[0] * this->quadrature_weights[q_point]; + for (unsigned int d=0; djacobian[0][0][d] * grad_in[0]; + for (unsigned e=1; ejacobian[0][e][d] * grad_in[e]; + this->gradients_quad[0][d][q_point] = new_val * JxW; + } + } +} + + + +template +inline +VectorizedArray +FEEvaluationAccess:: +integrate_value () +{ + return BaseClass::integrate_value()[0]; +} + + + + +/*----------------- FEEvaluationAccess vector-valued ----------------------*/ + + +template +inline +FEEvaluationAccess:: +FEEvaluationAccess (const MatrixFree &data_in, + const unsigned int fe_no, + const unsigned int quad_no_in) + : + FEEvaluationBase + (data_in, fe_no, quad_no_in) +{} + + + +template +inline +Tensor<2,dim,VectorizedArray > +FEEvaluationAccess:: +get_gradient (unsigned int q_point) const +{ + return BaseClass::get_gradient (q_point); +} + + + +template +inline +VectorizedArray +FEEvaluationAccess:: +get_divergence (unsigned int q_point) const +{ + Assert (this->gradients_quad_initialized==true, + internal::ExcAccessToUninitializedField()); + AssertIndexRange (q_point, n_q_points); + + vector_t divergence; + + // Cartesian cell + if (this->cell_type == 0) + { + divergence = (this->gradients_quad[0][0][q_point] * + this->cartesian[0][0]); + for (unsigned int d=1; dgradients_quad[d][d][q_point] * + this->cartesian[0][d]); + } + // cell with general Jacobian + else if (this->cell_type == 2) + { + divergence = (this->jacobian[q_point][0][0] * + this->gradients_quad[0][0][q_point]); + for (unsigned e=1; ejacobian[q_point][0][e] * + this->gradients_quad[0][e][q_point]); + for (unsigned int d=1; djacobian[q_point][d][e] * + this->gradients_quad[d][e][q_point]); + } + // cell with general Jacobian, but constant + // within the cell + else // if (this->cell_type == 1) + { + divergence = (this->jacobian[0][0][0] * + this->gradients_quad[0][0][q_point]); + for (unsigned e=1; ejacobian[0][0][e] * + this->gradients_quad[0][e][q_point]); + for (unsigned int d=1; djacobian[0][d][e] * + this->gradients_quad[d][e][q_point]); + } + return divergence; +} + + + +template +inline +SymmetricTensor<2,dim,VectorizedArray > +FEEvaluationAccess:: +get_symmetric_gradient (unsigned int q_point) const +{ + // copy from generic function into + // dim-specialization function + const Tensor<2,dim,vector_t> grad = get_gradient(q_point); + vector_t symmetrized [(dim*dim+dim)/2]; + vector_t half = make_vectorized_array (0.5); + for (unsigned int d=0; d (symmetrized); +} + + + +template +inline +typename FEEvaluationAccess::curl_type +FEEvaluationAccess:: +get_curl (unsigned int q_point) const +{ + // copy from generic function into + // dim-specialization function + const Tensor<2,dim,vector_t> grad = get_gradient(q_point); + curl_type curl; + switch (dim) + { + case 1: + Assert (false, + ExcMessage("Computing the curl in 1d is not a useful operation")); + break; + case 2: + curl[0] = grad[1][0] - grad[0][1]; + break; + case 3: + curl[0] = grad[2][1] - grad[1][2]; + curl[1] = grad[0][2] - grad[2][0]; + curl[2] = grad[1][0] - grad[0][1]; + break; + default: + Assert (false, ExcNotImplemented()); + } + return curl; +} + + + +template +inline +Tensor<2,dim,VectorizedArray > +FEEvaluationAccess:: +get_hessian_diagonal (unsigned int q_point) const +{ + Assert (this->hessians_quad_initialized==true, + internal::ExcAccessToUninitializedField()); + AssertIndexRange (q_point, n_q_points); + + return BaseClass::get_hessian_diagonal (q_point); +} + + + +template +inline +Tensor<3,dim,VectorizedArray > +FEEvaluationAccess:: +get_hessian (unsigned int q_point) const +{ + Assert (this->hessians_quad_initialized==true, + internal::ExcAccessToUninitializedField()); + AssertIndexRange (q_point, n_q_points); + return BaseClass::get_hessian(q_point); +} + + + +template +inline +void +FEEvaluationAccess:: +submit_gradient (Tensor<2,dim,VectorizedArray > grad_in, + unsigned int q_point) +{ + BaseClass::submit_gradient (grad_in, q_point); +} + + + +template +inline +void +FEEvaluationAccess:: +submit_gradient (Tensor<1,dim,Tensor<1,dim,VectorizedArray > > grad_in, + unsigned int q_point) +{ + BaseClass::submit_gradient(grad_in, q_point); +} + + + +template +inline +void +FEEvaluationAccess:: +submit_symmetric_gradient (SymmetricTensor<2,dim,VectorizedArray > + sym_grad, + unsigned int q_point) +{ + // could have used base class operator, but + // that involves some overhead which is + // inefficient. it is nice to have the + // symmetric tensor because that saves some + // operations +#ifdef DEBUG + Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + AssertIndexRange (q_point, n_q_points); + this->gradients_quad_submitted = true; +#endif + if (this->cell_type == 0) + { + const vector_t JxW = this->J_value[0] * this->quadrature_weights[q_point]; + for (unsigned int d=0; dgradients_quad[d][d][q_point] = (sym_grad.access_raw_entry(d) * + JxW * + this->cartesian[0][d]); + for (unsigned int e=0, counter=dim; egradients_quad[e][d][q_point] = (value * + this->cartesian[0][d]); + this->gradients_quad[d][e][q_point] = (value * + this->cartesian[0][e]); + } + } + else if (this->cell_type == 2) + { + vector_t weighted [dim][dim]; + { + const vector_t JxW = this->J_value[q_point]; + for (unsigned int i=0; ijacobian[q_point][0][d] * weighted[comp][0]; + for (unsigned e=1; ejacobian[q_point][e][d] * weighted[comp][e]; + this->gradients_quad[comp][d][q_point] = new_val; + } + } + else //if (this->cell_type == 1) + { + vector_t weighted [dim][dim]; + { + const vector_t JxW = (this->J_value[0] * + this->quadrature_weights[q_point]); + for (unsigned int i=0; ijacobian[q_point][0][d] * weighted[comp][0]; + for (unsigned e=1; ejacobian[q_point][e][d] * weighted[comp][e]; + this->gradients_quad[comp][d][q_point] = new_val; + } + } +} + + + +template +inline +void +FEEvaluationAccess:: +submit_curl (curl_type curl, + unsigned int q_point) +{ + Tensor<2,dim,vector_t> grad; + switch (dim) + { + case 1: + Assert (false, + ExcMessage("Testing by the curl in 1d is not a useful operation")); + break; + case 2: + grad[1][0] = curl[0]; + grad[0][1] = -curl[0]; + break; + case 3: + grad[2][1] = curl[0]; + grad[1][2] = -curl[0]; + grad[0][2] = curl[1]; + grad[2][0] = -curl[1]; + grad[1][0] = curl[2]; + grad[0][1] = -curl[2]; + break; + default: + Assert (false, ExcNotImplemented()); + } + submit_gradient (grad, q_point); +} + + + +/*----------------------- FEEvaluationGeneral -------------------------------*/ + +template +inline +FEEvaluationGeneral:: +FEEvaluationGeneral (const MatrixFree &data_in, + const unsigned int fe_no, + const unsigned int quad_no_in) + : + BaseClass (data_in, fe_no, quad_no_in) +{ +#ifdef DEBUG + // print error message when the dimensions do + // not match. Propose a possible fix + if (dofs_per_cell != this->data.dofs_per_cell || + n_q_points != this->data.n_q_points) + { + std::string message = + "-------------------------------------------------------\n"; + message += "Illegal arguments in constructor/wrong template arguments!\n"; + message += " Called --> FEEvaluation(data, "; + message += Utilities::int_to_string(fe_no) + ", "; + message += Utilities::int_to_string(quad_no_in) + ")\n"; + + // check whether some other vector component + // has the correct number of points + unsigned int proposed_dof_comp = numbers::invalid_unsigned_int, + proposed_quad_comp = numbers::invalid_unsigned_int; + for (unsigned int no=0; nomatrix_info.n_components(); ++no) + if (this->matrix_info.get_dof_info(no).dofs_per_cell[0] == + dofs_per_cell) + { + proposed_dof_comp = no; + break; + } + for (unsigned int no=0; nomapping_info.mapping_data_gen.size(); ++no) + if (this->mapping_info.mapping_data_gen[no].n_q_points[this->active_quad_index] == n_q_points) + { + proposed_quad_comp = no; + break; + } + if (proposed_dof_comp != numbers::invalid_unsigned_int && + proposed_quad_comp != numbers::invalid_unsigned_int) + { + message += "Wrong vector component selection:\n"; + message += " Did you mean FEEvaluation(data, "; + message += Utilities::int_to_string(proposed_dof_comp) + ", "; + message += Utilities::int_to_string(proposed_quad_comp) + ")?\n"; + std::string correct_pos; + if (proposed_dof_comp != fe_no) + correct_pos = " ^ "; + else + correct_pos = " "; + if (proposed_quad_comp != quad_no_in) + correct_pos += " ^\n"; + else + correct_pos += " \n"; + message += " " + correct_pos; + } + // ok, did not find the numbers specified by + // the template arguments in the given + // list. Suggest correct template arguments + const unsigned int proposed_n_dofs_1d = static_cast(std::pow(1.001*this->data.dofs_per_cell,1./dim)); + const unsigned int proposed_n_q_points_1d = static_cast(std::pow(1.001*this->data.n_q_points,1./dim)); + message += "Wrong template arguments:\n"; + message += " Did you mean FEEvaluationdata.dofs_per_cell && + n_q_points == this->data.n_q_points, + ExcMessage(message)); + } + AssertDimension (n_q_points, + this->mapping_info.mapping_data_gen[this->quad_no]. + n_q_points[this->active_quad_index]); + AssertDimension (dofs_per_cell * this->n_fe_components, + this->dof_info.dofs_per_cell[this->active_fe_index]); +#endif +} + + + +template +inline +void +FEEvaluationGeneral:: +evaluate (bool evaluate_val, bool evaluate_grad, bool evaluate_lapl) +{ + Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + Assert (this->dof_values_initialized == true, + internal::ExcAccessToUninitializedField()); + + const vector_t * val = this->data.shape_values.begin(); + const vector_t * grad = this->data.shape_gradients.begin(); + const vector_t * hess = this->data.shape_hessians.begin(); + + for(unsigned int comp=0;comp n_q_points_1d ? dofs_per_cell : n_q_points]; + vector_t temp2[n_dofs_1d > n_q_points_1d ? dofs_per_cell : n_q_points]; + + if (dim == 3) + { + if (evaluate_grad == true) + { + // grad x + apply_tensor_prod<0,true,false> (grad, this->values_dofs[comp], temp1); + apply_tensor_prod<1,true,false> (val, temp1, temp2); + apply_tensor_prod<2,true,false> (val, temp2, this->gradients_quad[comp][0]); + } + + if (evaluate_lapl == true) + { + // grad xz + if (evaluate_grad == false) + { + apply_tensor_prod<0,true,false> (grad, this->values_dofs[comp], temp1); + apply_tensor_prod<1,true,false> (val, temp1, temp2); + } + apply_tensor_prod<2,true,false>(grad, temp2, this->hessians_quad[comp][4]); + + // grad xy + apply_tensor_prod<1,true,false>(grad, temp1, temp2); + apply_tensor_prod<2,true,false> (val, temp2, this->hessians_quad[comp][3]); + + // grad xx + apply_tensor_prod<0,true,false> (hess, this->values_dofs[comp], temp1); + apply_tensor_prod<1,true,false> (val, temp1, temp2); + apply_tensor_prod<2,true,false> (val, temp2, this->hessians_quad[comp][0]); + } + + // grad y + apply_tensor_prod<0,true,false> (val, this->values_dofs[comp], temp1); + if (evaluate_grad == true) + { + apply_tensor_prod<1,true,false> (grad, temp1, temp2); + apply_tensor_prod<2,true,false> (val, temp2, this->gradients_quad[comp][1]); + } + + if (evaluate_lapl == true) + { + // grad yz + if (evaluate_grad == false) + apply_tensor_prod<1,true,false> (grad, temp1, temp2); + apply_tensor_prod<2,true,false> (grad, temp2, this->hessians_quad[comp][5]); + + // grad yy + apply_tensor_prod<1,true,false> (hess, temp1, temp2); + apply_tensor_prod<2,true,false> (val, temp2, this->hessians_quad[comp][1]); + } + + // grad z: can use the values applied in x direction stored in temp1 + apply_tensor_prod<1,true,false> (val, temp1, temp2); + if (evaluate_grad == true) + apply_tensor_prod<2,true,false> (grad, temp2, this->gradients_quad[comp][2]); + + // grad zz: can use the values applied in x and y direction stored in temp2 + if (evaluate_lapl == true) + apply_tensor_prod<2,true,false> (hess, temp2, this->hessians_quad[comp][2]); + + // val: can use the values applied in x & y direction stored in temp2 + if (evaluate_val == true) + apply_tensor_prod<2,true,false> (val, temp2, this->values_quad[comp]); + } + else if (dim == 2) + { + // grad x + if (evaluate_grad == true) + { + apply_tensor_prod<0,true,false> (grad, this->values_dofs[comp], temp1); + apply_tensor_prod<1,true,false> (val, temp1, this->gradients_quad[comp][0]); + } + if (evaluate_lapl == true) + { + // grad xy + if (evaluate_grad == false) + apply_tensor_prod<0,true,false> (grad, this->values_dofs[comp], temp1); + apply_tensor_prod<1,true,false> (grad, temp1, this->hessians_quad[comp][2]); + + // grad xx + apply_tensor_prod<0,true,false> (hess, this->values_dofs[comp], temp1); + apply_tensor_prod<1,true,false> (val, temp1, this->hessians_quad[comp][0]); + } + + // grad y + apply_tensor_prod<0,true,false> (val, this->values_dofs[comp], temp1); + if (evaluate_grad == true) + apply_tensor_prod<1,true,false> (grad, temp1, this->gradients_quad[comp][1]); + + // grad yy + if (evaluate_lapl == true) + apply_tensor_prod<1,true,false> (hess, temp1, this->hessians_quad[comp][1]); + + // val: can use values applied in x + if (evaluate_val == true) + apply_tensor_prod<1,true,false> (val, temp1, this->values_quad[comp]); + } + else if (dim == 1) + { + if (evaluate_val == true) + apply_tensor_prod<0,true,false> (val, this->values_dofs[comp], + this->values_quad[comp]); + if (evaluate_grad == true) + apply_tensor_prod<0,true,false> (grad, this->values_dofs[comp], + this->gradients_quad[comp][0]); + if (evaluate_lapl == true) + apply_tensor_prod<0,true,false> (hess, this->values_dofs[comp], + this->hessians_quad[comp][0]); + } + } + +#ifdef DEBUG + if (evaluate_val == true) + this->values_quad_initialized = true; + if (evaluate_grad == true) + this->gradients_quad_initialized = true; + if (evaluate_lapl == true) + this->hessians_quad_initialized = true; +#endif +} + + + +template +inline +void +FEEvaluationGeneral:: +integrate (bool integrate_val,bool integrate_grad) +{ +#ifdef DEBUG + Assert (this->cell != numbers::invalid_unsigned_int, ExcNotInitialized()); + if (integrate_val == true) + Assert (this->values_quad_submitted == true, + internal::ExcAccessToUninitializedField()); + if (integrate_grad == true) + Assert (this->gradients_quad_submitted == true, + internal::ExcAccessToUninitializedField()); +#endif + + const vector_t * val = this->data.shape_values.begin(); + const vector_t * grad = this->data.shape_gradients.begin(); + + for(unsigned int comp=0;comp n_q_points_1d ? dofs_per_cell : n_q_points]; + vector_t temp2[n_dofs_1d > n_q_points_1d ? dofs_per_cell : n_q_points]; + + if (dim == 3) + { + if (integrate_val == true) + { + // val + apply_tensor_prod<0,false,false> (val, this->values_quad[comp], temp1); + } + if (integrate_grad == true) + { + // grad x: can sum to temporary value in temp1 + if (integrate_val == true) + apply_tensor_prod<0,false,true> + (grad, this->gradients_quad[comp][0],temp1); + else + apply_tensor_prod<0,false,false> + (grad, this->gradients_quad[comp][0],temp1); + } + apply_tensor_prod<1,false,false> (val, temp1, temp2); + if (integrate_grad == true) + { + // grad y: can sum to temporary x value in temp2 + apply_tensor_prod<0,false,false> (val, this->gradients_quad[comp][1], temp1); + apply_tensor_prod<1,false,true> (grad, temp1, temp2); + } + apply_tensor_prod<2,false,false> (val, temp2, this->values_dofs[comp]); + if (integrate_grad == true) + { + // grad z: can sum to temporary x and y value in output + apply_tensor_prod<0,false,false> (val, this->gradients_quad[comp][2], temp1); + apply_tensor_prod<1,false,false> (val, temp1, temp2); + apply_tensor_prod<2,false,true> (grad, temp2, this->values_dofs[comp]); + } + } + else if (dim == 2) + { + // val + if (integrate_val == true) + apply_tensor_prod<0,false,false> (val, this->values_quad[comp], temp1); + if (integrate_grad == true) + { + //grad x + if (integrate_val == true) + apply_tensor_prod<0,false,true> + (grad, this->gradients_quad[comp][0],temp1); + else + apply_tensor_prod<0,false,false> + (grad, this->gradients_quad[comp][0],temp1); + } + apply_tensor_prod<1,false,false> (val, temp1, this->values_dofs[comp]); + if (integrate_grad == true) + { + // grad y + apply_tensor_prod<0,false,false> (grad, this->gradients_quad[comp][1], temp1); + apply_tensor_prod<1,false,true> (val, temp1, this->values_dofs[comp]); + } + } + else if (dim == 1) + { + if (integrate_grad == true) + apply_tensor_prod<0,false,false> (grad, this->gradients_quad[comp][0], + this->values_dofs[comp]); + if (integrate_val == true) + { + if (integrate_grad == true) + apply_tensor_prod<0,false,true> (val, this->values_quad[comp], + this->values_dofs[comp]); + else + apply_tensor_prod<0,false,false> (val, this->values_quad[comp], + this->values_dofs[comp]); + } + } + } + +#ifdef DEBUG + this->dof_values_initialized = true; +#endif +} + + + +template +inline +Point > +FEEvaluationGeneral:: +quadrature_point (const unsigned int q) const +{ + Assert (this->mapping_info.quadrature_points_initialized == true, + ExcNotInitialized()); + AssertIndexRange (q, n_q_points); + + // Cartesian mesh: not all quadrature points + // are stored, only the diagonal. Hence, need + // to find the tensor product index and + // retrieve the value from that + if (this->cell_type == 0) + { + Point point (false); + switch (dim) + { + case 1: + return this->quadrature_points[q]; + case 2: + point[0] = this->quadrature_points[q%n_q_points_1d][0]; + point[1] = this->quadrature_points[q/n_q_points_1d][1]; + return point; + case 3: + point[0] = this->quadrature_points[q%n_q_points_1d][0]; + point[1] = this->quadrature_points[(q/n_q_points_1d)%n_q_points_1d][1]; + point[2] = this->quadrature_points[q/(n_q_points_1d*n_q_points_1d)][2]; + return point; + default: + Assert (false, ExcNotImplemented()); + return point; + } + } + // all other cases: just return the respective + // data as it is fully stored + else + return this->quadrature_points[q]; +} + + + // General tensor product application for up + // to three spatial dimensions. Does not + // assume any symmetry in the shape values + // field +template +template +inline +void +FEEvaluationGeneral:: +apply_tensor_prod (const VectorizedArray*shape_data, + const VectorizedArray input [], + VectorizedArray output []) +{ + AssertIndexRange (direction, dim); + const int mm = dof_to_quad ? n_dofs_1d : n_q_points_1d, + nn = dof_to_quad ? n_q_points_1d : n_dofs_1d; + + const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1); + const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1); + const int stride = ((direction > 0 ? nn : 1 ) * + (direction > 1 ? nn : 1)); + + const vector_t * in = &input[0]; + vector_t * out = &output[0]; + for (int i2=0; i2 +inline +FEEvaluation:: +FEEvaluation (const MatrixFree &data_in, + const unsigned int fe_no, + const unsigned int quad_no) + : + BaseClass (data_in, fe_no, quad_no) +{ + // check whether element is appropriate +#ifdef DEBUG + const double zero_tol = + types_are_equal::value==true?1e-8:1e-7; + std::string error_message = "FEEvaluation not appropriate.\n"; + error_message += " It assumes symmetry of quadrature points w.r.t. 0.5 \n"; + error_message += " and the basis functions starting from left and right.\n"; + error_message += "Try FEEvaluationGeneral<...> instead!"; + + // symmetry for values + for (unsigned int i=0; i<(n_dofs_1d+1)/2; ++i) + for (unsigned int j=0; jdata.shape_values[i*n_q_points_1d+j][0] - + this->data.shape_values[(n_dofs_1d-i)*n_q_points_1d + -j-1][0]) < zero_tol, + ExcMessage(error_message)); + + // shape values should be zero at for all + // basis functions except for one where they + // are one in the middle + if (n_q_points_1d%2 == 1 && n_dofs_1d%2 == 1) + { + for (int i=0; i(n_dofs_1d/2); ++i) + Assert (std::fabs(this->data.shape_values[i*n_q_points_1d+ + n_q_points_1d/2][0]) < zero_tol, + ExcMessage(error_message)); + Assert (std::fabs(this->data.shape_values[(n_dofs_1d/2)*n_q_points_1d+ + n_q_points_1d/2][0]-1.)< zero_tol, + ExcMessage(error_message)); + } + + // skew-symmetry for gradient, zero of middle + // basis function in middle quadrature point + for (unsigned int i=0; i<(n_dofs_1d+1)/2; ++i) + for (unsigned int j=0; jdata.shape_gradients[i*n_q_points_1d+j][0] + + this->data.shape_gradients[(n_dofs_1d-i)*n_q_points_1d- + j-1][0]) < zero_tol, + ExcMessage(error_message)); + if (n_dofs_1d%2 == 1 && n_q_points_1d%2 == 1) + Assert (std::fabs(this->data.shape_gradients[(n_dofs_1d/2)*n_q_points_1d+ + (n_q_points_1d/2)][0]) < zero_tol, + ExcMessage(error_message)); + + + // symmetry for Laplacian + for (unsigned int i=0; i<(n_dofs_1d+1)/2; ++i) + for (unsigned int j=0; jdata.shape_hessians[i*n_q_points_1d+j][0] - + this->data.shape_hessians[(n_dofs_1d-i)*n_q_points_1d- + j-1][0]) < zero_tol, + ExcMessage(error_message)); +#endif +} + + + +template +inline +void +FEEvaluation:: +evaluate (bool evaluate_val, bool evaluate_grad, bool evaluate_lapl) +{ + Assert (this->cell != numbers::invalid_unsigned_int, + ExcNotInitialized()); + Assert (this->dof_values_initialized == true, + internal::ExcAccessToUninitializedField()); + + for(unsigned int comp=0;comp n_q_points_1d ? dofs_per_cell : n_q_points]; + vector_t temp2[n_dofs_1d > n_q_points_1d ? dofs_per_cell : n_q_points]; + + if (dim == 3) + { + if (evaluate_grad == true) + { + // grad x + apply_gradients<0,true,false> (this->values_dofs[comp], temp1); + apply_values<1,true,false> (temp1, temp2); + apply_values<2,true,false> (temp2, this->gradients_quad[comp][0]); + } + + if (evaluate_lapl == true) + { + // grad xz + if (evaluate_grad == false) + { + apply_gradients<0,true,false> (this->values_dofs[comp], temp1); + apply_values<1,true,false> (temp1, temp2); + } + apply_gradients<2,true,false>(temp2, this->hessians_quad[comp][4]); + + // grad xy + apply_gradients<1,true,false>(temp1, temp2); + apply_values<2,true,false> (temp2, this->hessians_quad[comp][3]); + + // grad xx + apply_hessians<0,true,false> (this->values_dofs[comp], temp1); + apply_values<1,true,false> (temp1, temp2); + apply_values<2,true,false> (temp2, this->hessians_quad[comp][0]); + } + + // grad y + apply_values<0,true,false> (this->values_dofs[comp], temp1); + if (evaluate_grad == true) + { + apply_gradients<1,true,false> (temp1, temp2); + apply_values<2,true,false> (temp2, this->gradients_quad[comp][1]); + } + + if (evaluate_lapl == true) + { + // grad yz + if (evaluate_grad == false) + apply_gradients<1,true,false> (temp1, temp2); + apply_gradients<2,true,false> (temp2, this->hessians_quad[comp][5]); + + // grad yy + apply_hessians<1,true,false> (temp1, temp2); + apply_values<2,true,false> (temp2, this->hessians_quad[comp][1]); + } + + // grad z: can use the values applied in x direction stored in temp1 + apply_values<1,true,false> (temp1, temp2); + if (evaluate_grad == true) + apply_gradients<2,true,false> (temp2, this->gradients_quad[comp][2]); + + // grad zz: can use the values applied in x and y direction stored in temp2 + if (evaluate_lapl == true) + apply_hessians<2,true,false> (temp2, this->hessians_quad[comp][2]); + + // val: can use the values applied in x & y direction stored in temp2 + if (evaluate_val == true) + apply_values<2,true,false> (temp2, this->values_quad[comp]); + } + else if (dim == 2) + { + // grad x + if (evaluate_grad == true) + { + apply_gradients<0,true,false> (this->values_dofs[comp], temp1); + apply_values<1,true,false> (temp1, this->gradients_quad[comp][0]); + } + if (evaluate_lapl == true) + { + // grad xy + if (evaluate_grad == false) + apply_gradients<0,true,false> (this->values_dofs[comp], temp1); + apply_gradients<1,true,false> (temp1, this->hessians_quad[comp][2]); + + // grad xx + apply_hessians<0,true,false> (this->values_dofs[comp], temp1); + apply_values<1,true,false> (temp1, this->hessians_quad[comp][0]); + } + + // grad y + apply_values<0,true,false> (this->values_dofs[comp], temp1); + if (evaluate_grad == true) + apply_gradients<1,true,false> (temp1, this->gradients_quad[comp][1]); + + // grad yy + if (evaluate_lapl == true) + apply_hessians<1,true,false> (temp1, this->hessians_quad[comp][1]); + + // val: can use values applied in x + if (evaluate_val == true) + apply_values<1,true,false> (temp1, this->values_quad[comp]); + } + else if (dim == 1) + { + if (evaluate_val == true) + apply_values<0,true,false> (this->values_dofs[comp], + this->values_quad[comp]); + if (evaluate_grad == true) + apply_gradients<0,true,false> (this->values_dofs[comp], + this->gradients_quad[comp][0]); + if (evaluate_lapl == true) + apply_hessians<0,true,false> (this->values_dofs[comp], + this->hessians_quad[comp][0]); + } + } + +#ifdef DEBUG + if (evaluate_val == true) + this->values_quad_initialized = true; + if (evaluate_grad == true) + this->gradients_quad_initialized = true; + if (evaluate_lapl == true) + this->hessians_quad_initialized = true; +#endif +} + + + +template +inline +void +FEEvaluation:: +integrate (bool integrate_val,bool integrate_grad) +{ +#ifdef DEBUG + Assert (this->cell != numbers::invalid_unsigned_int, + ExcNotInitialized()); + if (integrate_val == true) + Assert (this->values_quad_submitted == true, + internal::ExcAccessToUninitializedField()); + if (integrate_grad == true) + Assert (this->gradients_quad_submitted == true, + internal::ExcAccessToUninitializedField()); +#endif + + for(unsigned int comp=0;comp n_q_points_1d ? dofs_per_cell : n_q_points]; + vector_t temp2[n_dofs_1d > n_q_points_1d ? dofs_per_cell : n_q_points]; + + if (dim == 3) + { + if (integrate_val == true) + { + // val + apply_values<0,false,false> (this->values_quad[comp], temp1); + } + if (integrate_grad == true) + { + // grad x: can sum to temporary value in temp1 + if (integrate_val == true) + apply_gradients<0,false,true> (this->gradients_quad[comp][0], + temp1); + else + apply_gradients<0,false,false> (this->gradients_quad[comp][0], + temp1); + } + apply_values<1,false,false> (temp1, temp2); + if (integrate_grad == true) + { + // grad y: can sum to temporary x value in temp2 + apply_values<0,false,false> (this->gradients_quad[comp][1], temp1); + apply_gradients<1,false,true> (temp1, temp2); + } + apply_values<2,false,false> (temp2, this->values_dofs[comp]); + if (integrate_grad == true) + { + // grad z: can sum to temporary x and y value in output + apply_values<0,false,false> (this->gradients_quad[comp][2], temp1); + apply_values<1,false,false> (temp1, temp2); + apply_gradients<2,false,true> (temp2, this->values_dofs[comp]); + } + } + else if (dim == 2) + { + // val + if (integrate_val == true) + apply_values<0,false,false> (this->values_quad[comp], temp1); + if (integrate_grad == true) + { + //grad x + if (integrate_val == true) + apply_gradients<0,false,true> (this->gradients_quad[comp][0], + temp1); + else + apply_gradients<0,false,false> (this->gradients_quad[comp][0], + temp1); + } + apply_values<1,false,false> (temp1, this->values_dofs[comp]); + if (integrate_grad == true) + { + // grad y + apply_values<0,false,false> (this->gradients_quad[comp][1], temp1); + apply_gradients<1,false,true> (temp1, this->values_dofs[comp]); + } + } + else if (dim == 1) + { + if (integrate_grad == true) + apply_gradients<0,false,false> (this->gradients_quad[comp][0], + this->values_dofs[comp]); + if (integrate_val == true) + { + if (integrate_grad == true) + apply_values<0,false,true> (this->values_quad[comp], + this->values_dofs[comp]); + else + apply_values<0,false,false> (this->values_quad[comp], + this->values_dofs[comp]); + } + } + } +#ifdef DEBUG + this->dof_values_initialized = true; +#endif +} + + + +// ----------------- optimized implementation tensor product symmetric case + +template +template +inline +void +FEEvaluation:: +apply_values (const VectorizedArray input [], + VectorizedArray output []) +{ + AssertIndexRange (direction, dim); + const int mm = dof_to_quad ? n_dofs_1d : n_q_points_1d, + nn = dof_to_quad ? n_q_points_1d : n_dofs_1d; + const int n_cols = nn / 2; + const int mid = mm / 2; + + const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1); + const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1); + const int stride = ((direction > 0 ? nn : 1 ) * + (direction > 1 ? nn : 1)); + + const vector_t * in = &input[0]; + vector_t * out = &output[0]; + for (int i2=0; i2data.shape_values[col]; + val1 = this->data.shape_values[nn-1-col]; + } + else + { + val0 = this->data.shape_values[col*n_q_points_1d]; + val1 = this->data.shape_values[(col+1)*n_q_points_1d-1]; + } + if (mid > 0) + { + res0 = val0 * in[0]; + res1 = val1 * in[0]; + res0 += val1 * in[stride*(mm-1)]; + res1 += val0 * in[stride*(mm-1)]; + for (int ind=1; inddata.shape_values[ind*n_q_points_1d+col]; + val1 = this->data.shape_values[ind*n_q_points_1d+nn-1-col]; + } + else + { + val0 = this->data.shape_values[col*n_q_points_1d+ind]; + val1 = this->data.shape_values[(col+1)*n_q_points_1d-1-ind]; + } + res0 += val0 * in[stride*ind]; + res1 += val1 * in[stride*ind]; + res0 += val1 * in[stride*(mm-1-ind)]; + res1 += val0 * in[stride*(mm-1-ind)]; + } + } + else + res0 = res1 = vector_t(); + if (dof_to_quad == true) + { + if (mm % 2 == 1) + { + val0 = this->data.shape_values[mid*n_q_points_1d+col]; + val1 = val0 * in[stride*mid]; + res0 += val1; + res1 += val1; + } + } + else + { + if (mm % 2 == 1 && nn % 2 == 0) + { + val0 = this->data.shape_values[col*n_q_points_1d+mid]; + val1 = val0 * in[stride*mid]; + res0 += val1; + res1 += val1; + } + } + if (add == false) + { + out[stride*col] = res0; + out[stride*(nn-1-col)] = res1; + } + else + { + out[stride*col] += res0; + out[stride*(nn-1-col)] += res1; + } + } + if ( dof_to_quad == true && nn%2==1 && mm%2==1 ) + { + if (add==false) + out[stride*n_cols] = in[stride*mid]; + else + out[stride*n_cols] += in[stride*mid]; + } + else if (dof_to_quad == true && nn%2==1) + { + vector_t res0; + vector_t val0 = this->data.shape_values[n_cols]; + if (mid > 0) + { + res0 = in[0] + in[stride*(mm-1)]; + res0 *= val0; + for (int ind=1; inddata.shape_values[ind*n_q_points_1d+n_cols]; + vector_t val1 = in[stride*ind] + in[stride*(mm-1-ind)]; + val1 *= val0; + res0 += val1; + } + } + else + res0 = vector_t(); + if (mm % 2 == 1) + { + val0 = this->data.shape_values[mid*n_q_points_1d+n_cols]; + res0 += val0 * in[stride*mid]; + } + if (add == false) + out[stride*n_cols] = res0; + else + out[stride*n_cols] += res0; + } + else if (dof_to_quad == false && nn%2 == 1) + { + vector_t res0; + if (mid > 0) + { + vector_t val0 = this->data.shape_values[n_cols*n_q_points_1d]; + res0 = in[0] + in[stride*(mm-1)]; + res0 *= val0; + for (int ind=1; inddata.shape_values[n_cols*n_q_points_1d+ind]; + vector_t val1 = in[stride*ind] + in[stride*(mm-1-ind)]; + val1 *= val0; + res0 += val1; + } + if (mm % 2) + res0 += in[stride*mid]; + } + else + res0 = in[0]; + if (add == false) + out[stride*n_cols] = res0; + else + out[stride*n_cols] += res0; + } + + // increment: in regular case, just go to the + // next point in x-direction. If we are at the + // end of one chunk in x-dir, need to jump + // over to the next layer in z-direction + switch (direction) + { + case 0: + in += mm; + out += nn; + break; + case 1: + case 2: + ++in; + ++out; + break; + default: + Assert (false, ExcNotImplemented()); + } + } + if (direction == 1) + { + in += nn*(mm-1); + out += nn*(nn-1); + } + } +} + + + +template +template +inline +void +FEEvaluation:: +apply_gradients (const VectorizedArray input [], + VectorizedArray output []) +{ + AssertIndexRange (direction, dim); + const int mm = dof_to_quad ? n_dofs_1d : n_q_points_1d, + nn = dof_to_quad ? n_q_points_1d : n_dofs_1d; + const int n_cols = nn / 2; + const int mid = mm / 2; + + const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1); + const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1); + const int stride = ((direction > 0 ? nn : 1 ) * + (direction > 1 ? nn : 1)); + + const vector_t * in = &input[0]; + vector_t * out = &output[0]; + for (int i2=0; i2data.shape_gradients[col]; + val1 = this->data.shape_gradients[nn-1-col]; + } + else + { + val0 = this->data.shape_gradients[col*n_q_points_1d]; + val1 = this->data.shape_gradients[(nn-col-1)*n_q_points_1d]; + } + if (mid > 0) + { + res0 = val0 * in[0]; + res1 = val1 * in[0]; + res0 -= val1 * in[stride*(mm-1)]; + res1 -= val0 * in[stride*(mm-1)]; + for (int ind=1; inddata.shape_gradients[ind*n_q_points_1d+col]; + val1 = this->data.shape_gradients[ind*n_q_points_1d+nn-1-col]; + } + else + { + val0 = this->data.shape_gradients[col*n_q_points_1d+ind]; + val1 = this->data.shape_gradients[(nn-col-1)*n_q_points_1d+ind]; + } + res0 += val0 * in[stride*ind]; + res1 += val1 * in[stride*ind]; + res0 -= val1 * in[stride*(mm-1-ind)]; + res1 -= val0 * in[stride*(mm-1-ind)]; + } + } + else + res0 = res1 = vector_t(); + if (mm % 2 == 1) + { + if (dof_to_quad == true) + val0 = this->data.shape_gradients[mid*n_q_points_1d+col]; + else + val0 = this->data.shape_gradients[col*n_q_points_1d+mid]; + val1 = val0 * in[stride*mid]; + res0 += val1; + res1 -= val1; + } + if (add == false) + { + out[stride*col] = res0; + out[stride*(nn-1-col)] = res1; + } + else + { + out[stride*col] += res0; + out[stride*(nn-1-col)] += res1; + } + } + if ( nn%2 == 1 ) + { + vector_t val0, res0; + if (dof_to_quad == true) + val0 = this->data.shape_gradients[n_cols]; + else + val0 = this->data.shape_gradients[n_cols*n_q_points_1d]; + res0 = in[0] - in[stride*(mm-1)]; + res0 *= val0; + for (int ind=1; inddata.shape_gradients[ind*n_q_points_1d+n_cols]; + else + val0 = this->data.shape_gradients[n_cols*n_q_points_1d+ind]; + vector_t val1 = in[stride*ind] - in[stride*(mm-1-ind)]; + val1 *= val0; + res0 += val1; + } + if (add == false) + out[stride*n_cols] = res0; + else + out[stride*n_cols] += res0; + } + + // increment: in regular case, just go to the + // next point in x-direction. for y-part in 3D + // and if we are at the end of one chunk in + // x-dir, need to jump over to the next layer + // in z-direction + switch (direction) + { + case 0: + in += mm; + out += nn; + break; + case 1: + case 2: + ++in; + ++out; + break; + default: + Assert (false, ExcNotImplemented()); + } + } + + if (direction == 1) + { + in += nn * (mm-1); + out += nn * (nn-1); + } + } +} + + + + // Laplacian operator application. Very + // similar to value application because the + // same symmetry relations hold. However, it + // is not possible to omit some values that + // are zero for the values +template +template +inline +void +FEEvaluation:: +apply_hessians (const VectorizedArray input [], + VectorizedArray output []) +{ + AssertIndexRange (direction, dim); + const int mm = dof_to_quad ? n_dofs_1d : n_q_points_1d, + nn = dof_to_quad ? n_q_points_1d : n_dofs_1d; + const int n_cols = nn / 2; + const int mid = mm / 2; + + const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1); + const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1); + const int stride = ((direction > 0 ? nn : 1 ) * + (direction > 1 ? nn : 1)); + + const vector_t * in = &input[0]; + vector_t * out = &output[0]; + for (int i2=0; i2data.shape_hessians[col]; + val1 = this->data.shape_hessians[nn-1-col]; + } + else + { + val0 = this->data.shape_hessians[col*n_q_points_1d]; + val1 = this->data.shape_hessians[(col+1)*n_q_points_1d-1]; + } + if (mid > 0) + { + res0 = val0 * in[0]; + res1 = val1 * in[0]; + res0 += val1 * in[stride*(mm-1)]; + res1 += val0 * in[stride*(mm-1)]; + for (int ind=1; inddata.shape_hessians[ind*n_q_points_1d+col]; + val1 = this->data.shape_hessians[ind*n_q_points_1d+nn-1-col]; + } + else + { + val0 = this->data.shape_hessians[col*n_q_points_1d+ind]; + val1 = this->data.shape_hessians[(col+1)*n_q_points_1d-1-ind]; + } + res0 += val0 * in[stride*ind]; + res1 += val1 * in[stride*ind]; + res0 += val1 * in[stride*(mm-1-ind)]; + res1 += val0 * in[stride*(mm-1-ind)]; + } + } + else + res0 = res1 = vector_t(); + if (mm % 2 == 1) + { + if (dof_to_quad == true) + val0 = this->data.shape_hessians[mid*n_q_points_1d+col]; + else + val0 = this->data.shape_hessians[col*n_q_points_1d+mid]; + val1 = val0 * in[stride*mid]; + res0 += val1; + res1 += val1; + } + if (add == false) + { + out[stride*col] = res0; + out[stride*(nn-1-col)] = res1; + } + else + { + out[stride*col] += res0; + out[stride*(nn-1-col)] += res1; + } + } + if ( nn%2 == 1 ) + { + vector_t val0, res0; + if (dof_to_quad == true) + val0 = this->data.shape_hessians[n_cols]; + else + val0 = this->data.shape_hessians[n_cols*n_q_points_1d]; + if (mid > 0) + { + res0 = in[0] + in[stride*(mm-1)]; + res0 *= val0; + for (int ind=1; inddata.shape_hessians[ind*n_q_points_1d+n_cols]; + else + val0 = this->data.shape_hessians[n_cols*n_q_points_1d+ind]; + vector_t val1 = in[stride*ind] + in[stride*(mm-1-ind)]; + val1 *= val0; + res0 += val1; + } + } + else + res0 = vector_t(); + if (mm % 2 == 1) + { + if (dof_to_quad == true) + val0 = this->data.shape_hessians[mid*n_q_points_1d+n_cols]; + else + val0 = this->data.shape_hessians[n_cols*n_q_points_1d+mid]; + res0 += val0 * in[stride*mid]; + } + if (add == false) + out[stride*n_cols] = res0; + else + out[stride*n_cols] += res0; + } + + // increment: in regular case, just go to the + // next point in x-direction. If we are at the + // end of one chunk in x-dir, need to jump + // over to the next layer in z-direction + switch (direction) + { + case 0: + in += mm; + out += nn; + break; + case 1: + case 2: + ++in; + ++out; + break; + default: + Assert (false, ExcNotImplemented()); + } + } + if (direction == 1) + { + in += nn*(mm-1); + out += nn*(nn-1); + } + } +} + + +/*----------------------- FEEvaluationGL -------------------------------*/ + + +template +inline +FEEvaluationGL:: +FEEvaluationGL (const MatrixFree &data_in, + const unsigned int fe_no, + const unsigned int quad_no) + : + BaseClass (data_in, fe_no, quad_no) +{ +#ifdef DEBUG + std::string error_mess = "FEEvaluationGL not appropriate. It assumes:\n"; + error_mess += " - identity operation for shape values\n"; + error_mess += " - zero diagonal at interior points for gradients\n"; + error_mess += "Try FEEvaluation<...> instead!"; + + const double zero_tol = + types_are_equal::value==true?1e-9:1e-7; + + for (unsigned int i=0; idata.shape_values[i*n_points_1d+j][0])data.shape_values[i*n_points_1d+ + j][0]-1.)data.shape_gradients[i*n_points_1d+i][0]) +inline +void +FEEvaluationGL:: +evaluate (bool evaluate_val,bool evaluate_grad,bool evaluate_lapl) +{ + Assert (this->cell != numbers::invalid_unsigned_int, + ExcNotInitialized()); + Assert (this->dof_values_initialized == true, + internal::ExcAccessToUninitializedField()); + + if (evaluate_val == true) + { + std::memcpy (&this->values_quad[0][0], &this->values_dofs[0][0], + dofs_per_cell * n_components * + sizeof (this->values_dofs[0][0])); +#ifdef DEBUG + this->values_quad_initialized = true; +#endif + } + if (evaluate_grad == true) + { + for(unsigned int comp=0;comp (this->values_dofs[comp], + this->gradients_quad[comp][0]); + // grad y + apply_gradients<1,true,false> (this->values_dofs[comp], + this->gradients_quad[comp][1]); + // grad y + apply_gradients<2,true,false> (this->values_dofs[comp], + this->gradients_quad[comp][2]); + } + else if (dim == 2) + { + // grad x + apply_gradients<0,true,false> (this->values_dofs[comp], + this->gradients_quad[comp][0]); + // grad y + apply_gradients<1,true,false> (this->values_dofs[comp], + this->gradients_quad[comp][1]); + } + else if (dim == 1) + apply_gradients<0,true,false> (this->values_dofs[comp], + this->gradients_quad[comp][0]); + } +#ifdef DEBUG + this->gradients_quad_initialized = true; +#endif + } + if (evaluate_lapl == true) + { + for(unsigned int comp=0;comptemplate apply_hessians<0,true,false> (this->values_dofs[comp], + this->hessians_quad[comp][0]); + // grad y + this->template apply_hessians<1,true,false> (this->values_dofs[comp], + this->hessians_quad[comp][1]); + // grad y + this->template apply_hessians<2,true,false> (this->values_dofs[comp], + this->hessians_quad[comp][2]); + + vector_t temp1[n_q_points]; + // grad xy + apply_gradients<0,true,false> (this->values_dofs[comp], temp1); + apply_gradients<1,true,false> (temp1, this->hessians_quad[comp][3]); + // grad xz + apply_gradients<2,true,false> (temp1, this->hessians_quad[comp][4]); + // grad yz + apply_gradients<1,true,false> (this->values_dofs[comp], temp1); + apply_gradients<2,true,false> (temp1, this->hessians_quad[comp][5]); + } + else if (dim == 2) + { + // grad x + this->template apply_hessians<0,true,false> (this->values_dofs[comp], + this->hessians_quad[comp][0]); + // grad y + this->template apply_hessians<1,true,false> (this->values_dofs[comp], + this->hessians_quad[comp][1]); + vector_t temp1[n_q_points]; + // grad xy + apply_gradients<0,true,false> (this->values_dofs[comp], temp1); + apply_gradients<1,true,false> (temp1, this->hessians_quad[comp][2]); + } + else if (dim == 1) + this->template apply_hessians<0,true,false> (this->values_dofs[comp], + this->hessians_quad[comp][0]); + } +#ifdef DEBUG + this->hessians_quad_initialized = true; +#endif + } +} + + + +template +inline +void +FEEvaluationGL:: +integrate (bool integrate_val, bool integrate_grad) +{ + Assert (this->cell != numbers::invalid_unsigned_int, + ExcNotInitialized()); + if (integrate_val == true) + Assert (this->values_quad_submitted == true, + internal::ExcAccessToUninitializedField()); + if (integrate_grad == true) + Assert (this->gradients_quad_submitted == true, + internal::ExcAccessToUninitializedField()); + if (integrate_val == true) + std::memcpy (&this->values_dofs[0][0], &this->values_quad[0][0], + dofs_per_cell * n_components * + sizeof (this->values_dofs[0][0])); + if (integrate_grad == true) + { + for(unsigned int comp=0;comp (this->gradients_quad[comp][0], + this->values_dofs[comp]); + else + apply_gradients<0, false, false> (this->gradients_quad[comp][0], + this->values_dofs[comp]); + + // grad y: can sum to temporary x value in temp2 + apply_gradients<1, false, true> (this->gradients_quad[comp][1], + this->values_dofs[comp]); + + // grad z: can sum to temporary x and y value in output + apply_gradients<2, false, true> (this->gradients_quad[comp][2], + this->values_dofs[comp]); + } + else if (dim == 2) + { + // grad x: If integrate_val == true we have to add to the previous output + if (integrate_val == true) + apply_gradients<0, false, true> (this->gradients_quad[comp][0], + this->values_dofs[comp]); + else + apply_gradients<0, false, false> (this->gradients_quad[comp][0], + this->values_dofs[comp]); + + // grad y: can sum to temporary x value in temp2 + apply_gradients<1, false, true> (this->gradients_quad[comp][1], + this->values_dofs[comp]); + } + else if (dim == 1) + { + if (integrate_val == true) + apply_gradients<0, false, true> (this->gradients_quad[comp][0], + this->values_dofs[comp]); + else + apply_gradients<0, false, false> (this->gradients_quad[comp][0], + this->values_dofs[comp]); + + } + } + } + +#ifdef DEBUG + this->dof_values_initialized = true; +#endif +} + + + +template +template +inline +void +FEEvaluationGL:: +apply_gradients (const VectorizedArray input [], + VectorizedArray output []) +{ + AssertIndexRange (direction, dim); + const int mm = n_points_1d; + const int nn = n_points_1d; + const int n_cols = nn / 2; + const int mid = mm / 2; + + const int n_blocks1 = (dim > 1 ? (direction > 0 ? nn : mm) : 1); + const int n_blocks2 = (dim > 2 ? (direction > 1 ? nn : mm) : 1); + const int stride = ((direction > 0 ? nn : 1 ) * + (direction > 1 ? nn : 1)); + + const vector_t * in = &input[0]; + vector_t * out = &output[0]; + for (int i2=0; i2data.shape_gradients[col]; + val1 = this->data.shape_gradients[nn-1-col]; + } + else + { + val0 = this->data.shape_gradients[col*n_points_1d]; + val1 = this->data.shape_gradients[(nn-col-1)*n_points_1d]; + } + if (mid > 0) + { + res0 = val0 * in[0]; + res1 = val1 * in[0]; + res0 -= val1 * in[stride*(mm-1)]; + res1 -= val0 * in[stride*(mm-1)]; + for (int ind=1; inddata.shape_gradients[ind*n_points_1d+col]; + val1 = this->data.shape_gradients[ind*n_points_1d+nn-1-col]; + } + else + { + val0 = this->data.shape_gradients[col*n_points_1d+ind]; + val1 = this->data.shape_gradients[(nn-col-1)*n_points_1d+ind]; + } + + // at inner points, the gradient is zero for + // ind==col + if (ind == col) + { + res1 += val1 * in[stride*ind]; + res0 -= val1 * in[stride*(mm-1-ind)]; + } + else + { + res0 += val0 * in[stride*ind]; + res1 += val1 * in[stride*ind]; + res0 -= val1 * in[stride*(mm-1-ind)]; + res1 -= val0 * in[stride*(mm-1-ind)]; + } + } + } + else + res0 = res1 = vector_t(); + if (mm % 2 == 1) + { + if (dof_to_quad == true) + val0 = this->data.shape_gradients[mid*n_points_1d+col]; + else + val0 = this->data.shape_gradients[col*n_points_1d+mid]; + val1 = val0 * in[stride*mid]; + res0 += val1; + res1 -= val1; + } + if (add == false) + { + out[stride*col] = res0; + out[stride*(nn-1-col)] = res1; + } + else + { + out[stride*col] += res0; + out[stride*(nn-1-col)] += res1; + } + } + if ( nn%2 == 1 ) + { + vector_t val0, res0; + if (dof_to_quad == true) + val0 = this->data.shape_gradients[n_cols]; + else + val0 = this->data.shape_gradients[n_cols*n_points_1d]; + if (mid > 0) + { + res0 = in[0] - in[stride*(mm-1)]; + res0 *= val0; + for (int ind=1; inddata.shape_gradients[ind*n_points_1d+n_cols]; + else + val0 = this->data.shape_gradients[n_cols*n_points_1d+ind]; + vector_t val1 = in[stride*ind] - in[stride*(mm-1-ind)]; + val1 *= val0; + res0 += val1; + } + } + else + res0 = vector_t(); + if (add == false) + out[stride*n_cols] = res0; + else + out[stride*n_cols] += res0; + } + + // increment: in regular case, just go to the + // next point in x-direction. for y-part in 3D + // and if we are at the end of one chunk in + // x-dir, need to jump over to the next layer + // in z-direction + switch (direction) + { + case 0: + in += mm; + out += nn; + break; + case 1: + case 2: + ++in; + ++out; + break; + default: + Assert (false, ExcNotImplemented()); + } + } + + if (direction == 1) + { + in += nn * (mm-1); + out += nn * (nn-1); + } + } + +#endif // ifndef DOXYGEN + +} + + +DEAL_II_NAMESPACE_CLOSE + +#endif diff --git a/deal.II/include/deal.II/matrix_free/fe_evaluation_data.h b/deal.II/include/deal.II/matrix_free/fe_evaluation_data.h new file mode 100644 index 0000000000..8fce46b7a3 --- /dev/null +++ b/deal.II/include/deal.II/matrix_free/fe_evaluation_data.h @@ -0,0 +1,188 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2011 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__matrix_free_fe_evaluation_data_h +#define __deal2__matrix_free_fe_evaluation_data_h + + +#include +#include +#include +#include + +#include + + +DEAL_II_NAMESPACE_OPEN + +namespace internal +{ +namespace MatrixFreeFunctions +{ + /** + * The class that stores the shape functions, gradients and Hessians + * evaluated for a tensor product finite element and tensor product + * quadrature formula on the unit cell. Because of this structure, only + * one-dimensional data is stored. + * + * @author Katharina Kormann and Martin Kronbichler, 2010, 2011 + */ + template + struct FEEvaluationData + { + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + /** + * Empty constructor. Does nothing. + */ + FEEvaluationData (); + + /** + * Initializes the data fields. Takes a + * one-dimensional quadrature formula and a + * finite element as arguments and evaluates + * the shape functions, gradients and Hessians + * on the one-dimensional unit cell. This + * function assumes that the finite element is + * derived from a one-dimensional element by a + * tensor product. It uses FETools::get_name() + * and FETools::get_fe_from_name() to find the + * one-dimensional element corresponding to + * the input element in @p dim dimensions. + */ + template + void reinit (const Quadrature<1> &quad, + const FiniteElement &fe_dim); + + /** + * Internal helper function for initialization + * that does the main work. + */ + void do_initialize (const Quadrature<1> &quad, + const FiniteElement<1> &fe, + const unsigned int dim); + + /** + * Returns the memory consumption of this + * class in bytes. + */ + std::size_t memory_consumption () const; + + /** + * Stores the shape values of the 1D finite + * element evaluated on all 1D quadrature + * points in vectorized format, i.e., as an + * array of + * VectorizedArray::n_array_elements + * equal elements. The length of this array is + * n_dofs_1d * n_q_points_1d and + * quadrature points are the index running + * fastest. + */ + AlignedVector shape_values; + + /** + * Stores the shape gradients of the 1D finite + * element evaluated on all 1D quadrature + * points in vectorized format, i.e., as an + * array of + * VectorizedArray::n_array_elements + * equal elements. The length of this array is + * n_dofs_1d * n_q_points_1d and + * quadrature points are the index running + * fastest. + */ + AlignedVector shape_gradients; + + /** + * Stores the shape Hessians of the 1D finite + * element evaluated on all 1D quadrature + * points in vectorized format, i.e., as an + * array of + * VectorizedArray::n_array_elements + * equal elements. The length of this array is + * n_dofs_1d * n_q_points_1d and + * quadrature points are the index running + * fastest. + */ + AlignedVector shape_hessians; + + /** + * Stores the indices from cell DoFs to face + * DoFs. The rows go through the + * 2*dim faces, and the columns the + * DoFs on the faces. + */ + Table<2,unsigned int> face_indices; + + /** + * Stores one-dimensional values of shape + * functions on subface. Since there are two + * subfaces, store two variants. Not + * vectorized. + */ + std::vector face_value[2]; + + /** + * Stores one-dimensional gradients of shape + * functions on subface. Since there are two + * subfaces, store two variants. Not + * vectorized. + */ + std::vector face_gradient[2]; + + /** + * Non-vectorized version of shape + * values. Needed when evaluating face info. + */ + std::vector shape_values_number; + + /** + * Non-vectorized version of shape + * gradients. Needed when evaluating face + * info. + */ + std::vector shape_gradient_number; + + /** + * Stores the number of quadrature points in + * @p dim dimensions for a cell. + */ + unsigned int n_q_points; + + /** + * Stores the number of DoFs per cell in @p + * dim dimensions. + */ + unsigned int dofs_per_cell; + + /** + * Stores the number of quadrature points per + * face in @p dim dimensions. + */ + unsigned int n_q_points_face; + + /** + * Stores the number of DoFs per face in @p + * dim dimensions. + */ + unsigned int dofs_per_face; + }; + +} // end of namespace MatrixFreeFunctions +} // end of namespace internal + +DEAL_II_NAMESPACE_CLOSE + +#endif diff --git a/deal.II/include/deal.II/matrix_free/fe_evaluation_data.templates.h b/deal.II/include/deal.II/matrix_free/fe_evaluation_data.templates.h new file mode 100644 index 0000000000..4401cc644e --- /dev/null +++ b/deal.II/include/deal.II/matrix_free/fe_evaluation_data.templates.h @@ -0,0 +1,265 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2011 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 + + +DEAL_II_NAMESPACE_OPEN + + +namespace internal +{ +namespace MatrixFreeFunctions +{ + namespace internal + { + + // ----------------- FE_PolyAccess ----------------------------------- + + // in order to read out the 1D info from a + // finite element and use the tensor product + // structure easily, we need to be able to + // access the numbering in the polynomial + // space of the finite element. that + // information is not public, but we can get + // access to that information by creating a + // new dummy class that is based on + // FE_Poly,dim,spacedim> + template + class FE_PolyAccess : public FE_Poly, dim, spacedim> + { + public: + FE_PolyAccess (const FE_Poly,dim,spacedim> &fe) + : + FE_Poly,dim,spacedim>(fe) + {} + + virtual std::string get_name() const + { + Assert (false, ExcNotImplemented()); + return 0; + } + virtual FiniteElement * clone() const + { + Assert (false, ExcNotImplemented()); + return 0; + } + + const std::vector get_numbering () const + { + return this->poly_space.get_numbering(); + } + + const std::vector get_numbering_inverse () const + { + return this->poly_space.get_numbering_inverse(); + } + }; + + } // end of namespace internal + + + + // ----------------- actual FEEvaluationData functions -------------------- + + template + FEEvaluationData::FEEvaluationData () + : + n_q_points (0), + dofs_per_cell (0) + {} + + + + template + template + void + FEEvaluationData::reinit (const Quadrature<1> &quad, + const FiniteElement &fe_dim) + { + Assert (fe_dim.n_components() == 1, + ExcMessage("FEEvaluation only works for scalar finite elements.")); + + // take the name of the finite element + // and generate a 1d element. read the + // name, change the template argument + // to one and construct an element + std::string fe_name = fe_dim.get_name(); + const std::size_t template_starts = fe_name.find_first_of('<'); + Assert (fe_name[template_starts+1] == (dim==1?'1':(dim==2?'2':'3')), + ExcInternalError()); + fe_name[template_starts+1] = '1'; + std_cxx1x::shared_ptr > fe_1d + (FETools::get_fe_from_name<1>(fe_name)); + const FiniteElement<1> & fe = *fe_1d; + do_initialize (quad, fe, dim); + } + + + template + void + FEEvaluationData::do_initialize (const Quadrature<1> &quad, + const FiniteElement<1> &fe, + const unsigned int dim) + { + const unsigned int n_dofs_1d = fe.dofs_per_cell, + n_q_points_1d = quad.size(); + std::vector lexicographic (n_dofs_1d); + + // renumber (this is necessary for FE_Q, for + // example, since there the vertex DoFs come + // first, which is incompatible with the + // lexicographic ordering necessary to apply + // tensor products efficiently) + { + const FE_Poly,1,1> *cast_fe = + dynamic_cast,1,1>*>(&fe); + Assert (cast_fe != 0, ExcNotImplemented()); + const internal::FE_PolyAccess<1,1> & fe_acc = + static_cast< const internal::FE_PolyAccess<1,1> &>(*cast_fe); + lexicographic = fe_acc.get_numbering(); + } + + n_q_points = 1; + dofs_per_cell = 1; + n_q_points_face = 1; + dofs_per_face = 1; + for (unsigned int d=0; d(dim)-1; ++d) + { + n_q_points_face *= n_q_points_1d; + dofs_per_face *= n_dofs_1d; + } + + const unsigned int array_size = n_dofs_1d*n_q_points_1d; + this->shape_gradients.resize_fast (array_size); + this->shape_values.resize_fast (array_size); + this->shape_hessians.resize_fast (array_size); + + this->face_gradient[0].resize(n_dofs_1d); + this->face_value[0].resize(array_size); + this->face_gradient[1].resize(n_dofs_1d); + this->face_value[1].resize(array_size); + this->shape_values_number.resize (array_size); + this->shape_gradient_number.resize (array_size); + + for (unsigned int i=0; i q_point = quad.get_points()[q]; + shape_values_number[my_i*n_q_points_1d+q] = fe.shape_value(i,q_point); + shape_gradient_number[my_i*n_q_points_1d+q] = fe.shape_grad (i,q_point)[0]; + shape_values [my_i*n_q_points_1d+q] = + shape_values_number [my_i*n_q_points_1d+q]; + shape_gradients[my_i*n_q_points_1d+q] = + shape_gradient_number[my_i*n_q_points_1d+q]; + shape_hessians[my_i*n_q_points_1d+q] = + fe.shape_grad_grad(i,q_point)[0][0]; + face_value[0][my_i*n_q_points_1d+q] = fe.shape_value(i,q_point*0.5); + face_value[1][my_i*n_q_points_1d+q] = fe.shape_value(i,Point<1>(0.5)+q_point*0.5); + } + this->face_gradient[0][my_i] = fe.shape_grad(i,Point<1>(0.))[0]; + this->face_gradient[1][my_i] = fe.shape_grad(i,Point<1>(1.))[0]; + } + + // face information + unsigned int n_faces = 1; + for (unsigned int d=0; dface_indices.reinit(n_faces, this->dofs_per_face); + switch (dim) + { + case 3: + { + for (unsigned int i=0; idofs_per_face; i++) + { + const unsigned int jump_term = + this->dofs_per_face*((i*n_dofs_1d)/this->dofs_per_face); + this->face_indices(0,i) = i*n_dofs_1d; + this->face_indices(1,i) = i*n_dofs_1d + n_dofs_1d-1; + this->face_indices(2,i) = i%n_dofs_1d + jump_term; + this->face_indices(3,i) = (i%n_dofs_1d + jump_term + + (n_dofs_1d-1)*n_dofs_1d); + this->face_indices(4,i) = i; + this->face_indices(5,i) = (n_dofs_1d-1)*this->dofs_per_face+i; + } + break; + } + case 2: + { + for (unsigned int i=0; iface_indices(0,i) = n_dofs_1d*i; + this->face_indices(1,i) = n_dofs_1d*i + n_dofs_1d-1; + this->face_indices(2,i) = i; + this->face_indices(3,i) = (n_dofs_1d-1)*n_dofs_1d+i; + } + break; + } + case 1: + { + this->face_indices(0,0) = 0; + this->face_indices(1,0) = n_dofs_1d-1; + break; + } + default: + Assert (false, ExcNotImplemented()); + } + } + + + + template + std::size_t + FEEvaluationData::memory_consumption () const + { + std::size_t memory = sizeof(*this); + memory += MemoryConsumption::memory_consumption(shape_values); + memory += MemoryConsumption::memory_consumption(shape_gradients); + memory += MemoryConsumption::memory_consumption(shape_hessians); + memory += face_indices.memory_consumption(); + for (unsigned int i=0; i<2; ++i) + { + memory += MemoryConsumption::memory_consumption(face_value[i]); + memory += MemoryConsumption::memory_consumption(face_gradient[i]); + } + memory += MemoryConsumption::memory_consumption(shape_values_number); + memory += MemoryConsumption::memory_consumption(shape_gradient_number); + return memory; + } + + // end of functions for FEEvaluationData + +} // end of namespace MatrixFreeFunctions +} // end of namespace internal + + +DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/include/deal.II/matrix_free/helper_functions.h b/deal.II/include/deal.II/matrix_free/helper_functions.h new file mode 100644 index 0000000000..71cc7263f5 --- /dev/null +++ b/deal.II/include/deal.II/matrix_free/helper_functions.h @@ -0,0 +1,393 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2011 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__matrix_free_helper_functions_h +#define __deal2__matrix_free_helper_functions_h + + +#include +#include +#include +#include +#include +#include + +#include + +DEAL_II_NAMESPACE_OPEN + + + +namespace internal +{ +namespace MatrixFreeFunctions +{ + // forward declaration of internal data structure + namespace internal + { + template struct ConstraintValues; + } + + + // set minimum grain size for parallel + // computations + namespace internal + { + const unsigned int minimum_parallel_grain_size = 500; + } + + + /* + * Compressed data type to store a two + * dimensional array. The data is stored in + * a single standard vector. In a second + * vector, the first element belonging to + * each row is stored. + */ + template + struct CompressedMatrix + { + AlignedVector data; + std::vector row_index; + T* operator[] (const unsigned int row) { + return begin(row); + }; + const T* operator[] (const unsigned int row) const { + return begin(row); + }; + const T* begin(const unsigned int row) const { + AssertIndexRange (row, row_index.size()-1); + return data.begin() + row_index[row]; + }; + const T* end(const unsigned int row) const { + AssertIndexRange (row, row_index.size()-1); + return data.begin() + row_index[row+1]; + }; + unsigned int row_length (const unsigned int row) const { + AssertIndexRange (row, row_index.size()-1); + return row_index[row+1] - row_index[row]; + }; + T* begin(const unsigned int row) { + AssertIndexRange (row, row_index.size()-1); + return data.begin() + row_index[row]; + }; + T* end(const unsigned int row) { + AssertIndexRange (row, row_index.size()-1); + return data.begin() + row_index[row+1]; + }; + void complete_last_row() { + row_index.push_back (data.size()); + } + void swap (CompressedMatrix &other) { + data.swap (other.data); + row_index.swap (other.row_index); + } + void print (std::ostream &out) const + { + for (unsigned int row=0; row partition_color_blocks; + unsigned int evens; + unsigned int odds; + unsigned int n_blocked_workers; + unsigned int n_workers; + + std::vector partition_evens; + std::vector partition_odds; + std::vector partition_n_blocked_workers; + std::vector partition_n_workers; + }; + + + + /** + * A struct that collects all information + * related to the size of the problem and MPI + * parallelization. + */ + struct SizeInfo + { + /** + * Constructor. + */ + SizeInfo () + { + clear(); + } + + /** + * Clears all data fields and resets the sizes + * to zero. + */ + void clear() + { + n_active_cells = 0; + n_macro_cells = 0; + boundary_cells_start = 0; + boundary_cells_end = 0; + n_vectors = 0; + locally_owned_cells = IndexSet(); + ghost_cells = IndexSet(); + communicator = MPI_COMM_SELF; + my_pid = 0; + n_procs = 0; + } + + template + void print_mem (STREAM &out, + std::size_t data_length) const + { + Utilities::MPI::MinMaxAvg memory_c; + if (Utilities::System::job_supports_mpi() == true) + { + memory_c = Utilities::MPI::min_max_avg (1e-6*data_length, + communicator); + } + else + { + memory_c.sum = 1e-6*data_length; + memory_c.min = memory_c.sum; + memory_c.max = memory_c.sum; + memory_c.avg = memory_c.sum; + memory_c.min_index = 0; + memory_c.max_index = 0; + } + if (n_procs < 2) + out << memory_c.min; + else + out << memory_c.min << "/" << memory_c.avg << "/" << memory_c.max; + out << " MB" << std::endl; + } + + void make_layout (const unsigned int n_active_cells_in, + const unsigned int n_boundary_cells, + const unsigned int n_vectors_in, + std::vector &irregular_cells) + { + n_vectors = n_vectors_in; + n_active_cells = n_active_cells_in; + + // check that number of boundary cells is + // divisible by n_vectors or that it contains + // all cells + Assert (n_boundary_cells % n_vectors == 0 || + n_boundary_cells == n_active_cells, ExcInternalError()); + n_macro_cells = (n_active_cells+n_vectors-1)/n_vectors; + irregular_cells.resize (n_macro_cells); + if (n_macro_cells*n_vectors > n_active_cells) + { + irregular_cells[n_macro_cells-1] = + n_vectors - (n_macro_cells*n_vectors - n_active_cells); + } + if (n_procs > 1) + { + const unsigned int n_macro_boundary_cells = + (n_boundary_cells+n_vectors-1)/n_vectors; + boundary_cells_start = (n_macro_cells-n_macro_boundary_cells)/2; + boundary_cells_end = boundary_cells_start + n_macro_boundary_cells; + } + else + boundary_cells_start = boundary_cells_end = n_macro_cells; + } + + unsigned int n_active_cells; + unsigned int n_macro_cells; + unsigned int boundary_cells_start; + unsigned int boundary_cells_end; + unsigned int n_vectors; + + /** + * index sets to describe the layout of cells: + * locally owned cells and locally active + * cells + */ + IndexSet locally_owned_cells; + IndexSet ghost_cells; + + /** + * MPI communicator + */ + MPI_Comm communicator; + unsigned int my_pid; + unsigned int n_procs; + }; + + + + namespace internal + { + // ----------------- hash structure -------------------------------- + + /** + * A class that is + * used to quickly find out whether two + * vectors of floating point numbers are the + * same without going through all the + * elements: store a hash value for each + * vector. Generate the + * hash value by a sum of all values + * multiplied by random numbers (cast to + * int). Of course, this is not a sure + * criterion and one must manually check for + * equality before this hash is telling + * something useful. However, inequalities are + * easily detected (unless roundoff spoils the + * hash function) + */ + struct HashValue + { + // Constructor: sets the size of Number values + // with the typical magnitude that is to be + // expected. + HashValue (const double element_size = 1.) + : + scaling (element_size * std::numeric_limits::epsilon() * + 1024.) + {}; + + // get hash value for a vector of floating + // point numbers (which are assumed to be of + // order of magnitude one). Do this by first + // truncating everything that is smaller than + // the scaling (in order to eliminate noise + // from roundoff errors) and then calling the + // boost hash function + unsigned int operator ()(const std::vector &vec) + { + std::vector mod_vec(vec); + for (unsigned int i=0; i(boost::hash_range (mod_vec.begin(), mod_vec.end())); + }; + + // get hash value for a tensor of rank + // two where the magnitude of the + // entries is given by the parameter + // weight + template + unsigned int operator ()(const Tensor<2,dim,VectorizedArray > &input, + const bool is_diagonal) + { + const unsigned int n_vectors = VectorizedArray::n_array_elements; + + if (is_diagonal) + { + number mod_tensor [dim][n_vectors]; + for (unsigned int i=0; i(boost::hash_range + (&mod_tensor[0][0], + &mod_tensor[0][0]+dim*n_vectors)); + } + else + { + number mod_tensor [dim][dim][n_vectors]; + for (unsigned int i=0; i(boost::hash_range + (&mod_tensor[0][0][0], + &mod_tensor[0][0][0]+ + dim*dim*n_vectors)); + } + }; + + const double scaling; + }; + + } // end of namespace internal + +} // end of namespace MatrixFreeFunctions +} // end of namespace internal + +DEAL_II_NAMESPACE_CLOSE + +#endif diff --git a/deal.II/include/deal.II/matrix_free/mapping_info.h b/deal.II/include/deal.II/matrix_free/mapping_info.h new file mode 100644 index 0000000000..3dc07eca94 --- /dev/null +++ b/deal.II/include/deal.II/matrix_free/mapping_info.h @@ -0,0 +1,420 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2011 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__matrix_free_mapping_info_h +#define __deal2__matrix_free_mapping_info_h + + +#include +#include +#include +#include +#include +#include + +#include + + +DEAL_II_NAMESPACE_OPEN + + +namespace internal +{ +namespace MatrixFreeFunctions +{ + /** + * The class that stores all geometry-dependent data related with cell + * interiors for use in the matrix-free class. + * + * @author Katharina Kormann and Martin Kronbichler, 2010, 2011 + */ + template + struct MappingInfo + { + typedef VectorizedArray vector_t; + typedef Point point; + typedef Tensor<1,dim,vector_t> tensor1; + typedef Tensor<2,dim,vector_t> tensor2; + typedef Tensor<3,dim,vector_t> tensor3; + typedef Tensor<1,(dim>1?dim*(dim-1)/2:1),Tensor<1,dim,vector_t> > tensorUT; + static const std::size_t n_vectors + = VectorizedArray::n_array_elements; + + /** + * Determines how many bits of an unsigned int + * are used to distinguish the cell types + * (Cartesian, with constant Jacobian, or + * general) + */ + static const std::size_t n_cell_type_bits = 2; + + /** + * Determines how many types of different + * cells can be detected at most. Corresponds + * to the number of bits we reserved for it. + */ + static const unsigned int n_cell_types = 1U< &tria, + const std::vector > &cells, + const std::vector &active_fe_index, + const Mapping &mapping, + const std::vector > &quad, + const UpdateFlags update_flags); + + /** + * Helper function to determine which update + * flags must be set in the internal functions + * to initialize all data as requested by the + * user. + */ + UpdateFlags + compute_update_flags (const UpdateFlags update_flags, + const std::vector > &quad) const; + + /** + * Returns the type of a given cell as + * detected during initialization. + */ + unsigned int get_cell_type (const unsigned int cell_chunk_no) const + { + AssertIndexRange (cell_chunk_no, cell_type.size()); + return cell_type[cell_chunk_no] % n_cell_types; + }; + + /** + * Returns the type of a given cell as + * detected during initialization. + */ + unsigned int get_cell_data_index (const unsigned int cell_chunk_no) const + { + AssertIndexRange (cell_chunk_no, cell_type.size()); + return cell_type[cell_chunk_no] >> n_cell_type_bits; + }; + + /** + * Clears all data fields in this class. + */ + void clear (); + + /** + * Returns the memory consumption of this + * class in bytes. + */ + std::size_t memory_consumption() const; + + /** + * Prints a detailed summary of memory + * consumption in the different structures of + * this class to the given output stream. + */ + template + void print_memory_consumption(STREAM &out, + const SizeInfo &size_info) const; + + /** + * Stores whether a cell is Cartesian, has + * constant transform data (Jacobians) or is + * general. cell_type % 4 gives this + * information (0: Cartesian, 1: constant + * Jacobian throughout cell, 2: general cell), + * and cell_type / 4 gives the index in the + * data field of where to find the information + * in the fields Jacobian and JxW values + * (except for quadrature points, for which + * the index runs as usual). + */ + std::vector cell_type; + + /** + * The first field stores the inverse Jacobian + * for Cartesian cells: There, it is a + * diagonal rank-2 tensor, so we actually just + * store a rank-1 tensor. It is the same on + * all cells, therefore we only store it once + * per cell, and use similarities from one + * cell to another, too (on structured meshes, + * there are usually many cells with the same + * Jacobian). + * + * The second field stores the Jacobian + * determinant for Cartesian cells (without + * the quadrature weight, which depends on the + * quadrature point, whereas the determinant + * is the same on each quadrature point). + */ + AlignedVector > cartesian; + + /** + * The first field stores the Jacobian for + * non-Cartesian cells where all the Jacobians + * on the cell are the same (i.e., constant, + * which comes from a linear transformation + * from unit to real cell). Also use + * similarities from one cell to another (on + * structured meshes, there are usually many + * cells with the same Jacobian). + * + * The second field stores the Jacobian + * determinant for non-Cartesian cells with + * constant Jacobian throughout the cell + * (without the quadrature weight, which + * depends on the quadrature point, whereas + * the determinant is the same on each + * quadrature point). + */ + AlignedVector > linear; + + /** + * Definition of a structure that stores data + * that depends on the quadrature formula (if + * we have more than one quadrature formula on + * a given problem, these fields will be + * different) + */ + struct MappingInfoDependent + { + /** + * This field stores the row starts for the + * inverse Jacobian transformations, + * quadrature weights and second derivatives. + */ + std::vector rowstart_jacobians; + + /** + * This field stores the inverse Jacobian + * transformation from unit to real cell, + * which is needed for most gradient + * transformations (corresponds to + * FEValues::inverse_jacobian) for general + * cells. + */ + AlignedVector jacobians; + + /** + * This field stores the Jacobian + * determinant times the quadrature weights + * (JxW in deal.II speak) for general cells. + */ + AlignedVector JxW_values; + + /** + * Stores the diagonal part of the gradient of + * the inverse Jacobian transformation. The + * first index runs over the derivatives + * $\partial^2/\partial x_i^2$, the second + * over the space coordinate. Needed for + * computing the Laplacian of FE functions on + * the real cell. Uses a separate storage from + * the off-diagonal part $\partial^2/\partial + * x_i \partial x_j, i\neq j$ because that is + * only needed for computing a full Hessian. + */ + AlignedVector jacobians_grad_diag; + + /** + * Stores the off-diagonal part of the + * gradient of the inverse Jacobian + * transformation. Because of symmetry, only + * the upper diagonal part is needed. The + * first index runs through the derivatives + * row-wise, i.e., $\partial^2/\partial x_1 + * \partial x_2$ first, then + * $\partial^2/\partial x_1 \partial x_3$, and + * so on. The second index is the spatial + * coordinate. Not filled currently. + */ + AlignedVector jacobians_grad_upper; + + /** + * Stores the row start for quadrature points + * in real coordinates for both types of + * cells. Note that Cartesian cells will have + * shorter fields (length is @p n_q_points_1d) + * than non-Cartesian cells (length is @p + * n_q_points). + */ + std::vector rowstart_q_points; + + /** + * Stores the quadrature points in real + * coordinates for Cartesian cells (does not + * need to store the full data on all points) + */ + AlignedVector quadrature_points; + + /** + * The dim-dimensional quadrature formula + * underlying the problem (constructed from a + * 1D tensor product quadrature formula). + */ + dealii::hp::QCollection quadrature_formula; + + /** + * The (dim-1)-dimensional quadrature formula + * underlying the problem (constructed from a + * 1D tensor product quadrature formula). + */ + dealii::hp::QCollection quadrature_formula_faces; + + /** + * The number of quadrature points for the + * current quadrature formula. + */ + std::vector n_q_points; + + /** + * The number of quadrature points for the + * current quadrature formula when applied to + * a face. Only set if the quadrature formula + * is derived from a tensor product, since it + * is not defined from the full quadrature + * formula otherwise. + */ + std::vector n_q_points_face; + + /** + * The quadrature weights (vectorized data + * format) on the unit cell. + */ + std::vector > quadrature_weights; + + /** + * This variable stores the number of + * quadrature points for all quadrature + * indices in the underlying element for + * easier access to data in the hp case. + */ + std::vector quad_index_conversion; + + /** + * Returns the quadrature index for a given + * number of quadrature points. If not in hp + * mode or if the index is not found, this + * function always returns index 0. Hence, + * this function does not check whether the + * given degree is actually present. + */ + unsigned int + quad_index_from_n_q_points (const unsigned int n_q_points) const + { + for (unsigned int i=0; i + void print_memory_consumption(STREAM &out, + const SizeInfo &size_info) const; + + /** + * Returns the memory consumption in bytes. + */ + std::size_t memory_consumption () const; + }; + + /** + * Contains all the stuff that depends on the + * quadrature formula + + */ + std::vector mapping_data_gen; + + /** + * Stores whether JxW values have been + * initialized + */ + bool JxW_values_initialized; + + /** + * Stores whether we computed second + * derivatives. + */ + bool second_derivatives_initialized; + + /** + * Stores whether we computed quadrature + * points. + */ + bool quadrature_points_initialized; + + /** + * Internal temporary data used for the + * initialization. + */ + struct CellData + { + CellData (const double jac_size_in) : + jac_size (jac_size_in) {} + + void resize (const unsigned int size) + { + if (general_jac.size() != size) + { + quadrature_points.resize(size); + general_jac.resize(size); + general_jac_grad.resize(size); + } + } + + AlignedVector quadrature_points; + AlignedVector general_jac; + AlignedVector general_jac_grad; + tensor2 const_jac; + const double jac_size; + }; + + /** + * Helper function called internally during + * the initialize function. + */ + void evaluate_on_cell (const dealii::Triangulation &tria, + const std::pair *cells, + const unsigned int cell, + const unsigned int my_q, + unsigned int (&cell_t_prev)[n_vectors], + unsigned int (&cell_t)[n_vectors], + FEValues &fe_values, + CellData &cell_data) const; + }; + +} // end of namespace MatrixFreeFunctions +} // end of namespace internal + +DEAL_II_NAMESPACE_CLOSE + +#endif diff --git a/deal.II/include/deal.II/matrix_free/mapping_info.templates.h b/deal.II/include/deal.II/matrix_free/mapping_info.templates.h new file mode 100644 index 0000000000..2e3e1f6154 --- /dev/null +++ b/deal.II/include/deal.II/matrix_free/mapping_info.templates.h @@ -0,0 +1,978 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2011 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 + + +DEAL_II_NAMESPACE_OPEN + + +namespace internal +{ +namespace MatrixFreeFunctions +{ + // ----------------- actual MappingInfo functions ------------------------- + + template + MappingInfo::MappingInfo() + : + JxW_values_initialized (false), + second_derivatives_initialized (false), + quadrature_points_initialized (false) + {} + + + + template + void + MappingInfo::clear () + { + JxW_values_initialized = false; + quadrature_points_initialized = false; + second_derivatives_initialized = false; + mapping_data_gen.clear(); + cell_type.clear(); + cartesian.clear(); + linear.clear(); + } + + + + template + UpdateFlags + MappingInfo:: + compute_update_flags (const UpdateFlags update_flags, + const std::vector > &quad) const + { + // this class is build around the evaluation + // this class is build around the evaluation + // of inverse gradients, so compute them in + // any case + UpdateFlags new_flags = update_inverse_jacobians; + + // if the user requested gradients, need + // inverse Jacobians + if (update_flags & update_gradients || update_flags & update_inverse_jacobians) + new_flags |= update_inverse_jacobians; + + // for JxW, would only need JxW values. + if (update_flags & update_JxW_values) + new_flags |= update_JxW_values; + + // for Hessian information, need inverse + // Jacobians and the derivative of Jacobians + // (these two together will give use the + // gradients of the inverse Jacobians, which + // is what we need) + if (update_flags & update_hessians || update_flags & update_jacobian_grads) + new_flags |= update_jacobian_grads; + + if (update_flags & update_quadrature_points) + new_flags |= update_quadrature_points; + + // there is one more thing: if we have a + // quadrature formula with only one quadrature + // point on the first component, but more + // points on later components, we need to have + // Jacobian gradients anyway in order to + // determine whether the Jacobian is constant + // throughout a cell + bool formula_with_one_point = false; + for (unsigned int i=0; i 1) + { + new_flags |= update_jacobian_grads; + goto end_set; + } + end_set: + return new_flags; + } + + + + namespace internal + { + template + double get_jacobian_size (const dealii::Triangulation &tria) + { + if (tria.n_cells() == 0) + return 1; + else return tria.begin()->diameter(); + } + } + + + + template + void + MappingInfo::initialize + (const dealii::Triangulation &tria, + const std::vector > &cells, + const std::vector &active_fe_index, + const Mapping &mapping, + const std::vector > &quad, + const UpdateFlags update_flags_input) + { + clear(); + const unsigned int n_quads = quad.size(); + const unsigned int n_cells = cells.size(); + Assert (n_cells%n_vectors == 0, ExcInternalError()); + const unsigned int n_macro_cells = n_cells/n_vectors; + mapping_data_gen.resize (n_quads); + cell_type.resize (n_macro_cells); + + // dummy FE that is used to set up an FEValues + // object. Do not need the actual finite + // element because we will only evaluate + // quantities for the mapping that are + // independent of the FE + FE_Nothing dummy_fe; + UpdateFlags update_flags = compute_update_flags (update_flags_input, quad); + + if (update_flags & update_JxW_values) + JxW_values_initialized = true; + if (update_flags & update_jacobian_grads) + second_derivatives_initialized = true; + if (update_flags & update_quadrature_points) + quadrature_points_initialized = true; + + // when we make comparisons about the size of + // Jacobians we need to know the approximate + // size of typical entries in Jacobians. We + // need to fix the Jacobian size once and for + // all. We choose the diameter of the first + // cell (on level zero, which is the best + // accuracy we can hope for, since diameters + // on finer levels are computed by differences + // of nearby cells). If the mesh extends over + // a certain domain, the precision of double + // values is essentially limited by this + // precision. + const double jacobian_size = internal::get_jacobian_size(tria); + + // objects that hold the data for up to + // n_vectors cells while we fill them up. Only + // after all n_vectors cells have been + // processed, we can insert the data into the + // data structures of this class + CellData data (jacobian_size); + + for (unsigned int my_q=0; my_q n_q_points_1d (n_hp_quads), + step_size_cartesian (n_hp_quads); + if (n_hp_quads > 1) + current_data.quad_index_conversion.resize(n_hp_quads); + for (unsigned int q=0; q(n_q_points_1d[q]); + current_data.n_q_points.push_back (n_q_points); + + current_data.n_q_points_face.push_back + (Utilities::fixed_power(n_q_points_1d[q])); + current_data.quadrature_formula.push_back + (Quadrature(quad[my_q][q])); + current_data.quadrature_formula_faces.push_back + (Quadrature(quad[my_q][q])); + + // set quadrature weights in vectorized form + current_data.quadrature_weights[q].resize(n_q_points); + for (unsigned int i=0; i 1) + current_data.quad_index_conversion[q] = n_q_points; + + // To walk on the diagonal for lexicographic + // ordering, we have to jump one index ahead + // in each direction. For direction 0, this is + // just the next point, for direction 1, it + // means adding n_q_points_1d, and so on. + step_size_cartesian[q] = 0; + unsigned int factor = 1; + for (unsigned int d=0; d::active_cell_iterator, + // we need to manually select the correct + // finite element, so just hold a vector of + // FEValues + std::vector > > + fe_values (current_data.quadrature_formula.size()); + UpdateFlags update_flags_feval = + (update_flags & update_inverse_jacobians ? update_jacobians : update_default) | + (update_flags & update_jacobian_grads ? update_jacobian_grads : update_default) | + (update_flags & update_quadrature_points ? update_quadrature_points : update_default); + + // resize the fields that have fixed size or + // for which we know something from an earlier + // loop + current_data.rowstart_q_points.resize (n_macro_cells+1); + if (my_q > 0) + { + const unsigned int n_cells_var = + mapping_data_gen[0].rowstart_jacobians.size()-1; + current_data.rowstart_jacobians.reserve (n_cells_var+1); + const unsigned int reserve_size = n_cells_var * + current_data.n_q_points[0]; + if (mapping_data_gen[0].jacobians.size() > 0) + current_data.jacobians.reserve (reserve_size); + if (mapping_data_gen[0].JxW_values.size() > 0) + current_data.jacobians.reserve (reserve_size); + if (mapping_data_gen[0].jacobians_grad_diag.size() > 0) + current_data.jacobians_grad_diag.reserve (reserve_size); + if (mapping_data_gen[0].jacobians_grad_upper.size() > 0) + current_data.jacobians_grad_upper.reserve (reserve_size); + } + + // a hash structure that is used to detect + // similarities between mapping data from one + // cell to the next. + std::vector > hash_collection; + internal::HashValue hash_value (jacobian_size); + + // loop over all cells + for (unsigned int cell=0; cell 0 ? + active_fe_index[cell] : 0; + const unsigned int n_q_points = current_data.n_q_points[fe_index]; + if (fe_values[fe_index].get() == 0) + fe_values[fe_index].reset + (new FEValues (mapping, dummy_fe, + current_data.quadrature_formula[fe_index], + update_flags_feval)); + FEValues &fe_val = *fe_values[fe_index]; + data.resize (n_q_points); + + // if the fe index has changed from the + // previous cell, set the old cell type to + // invalid (otherwise, we might detect + // similarity due to some cells further ahead) + if (cell > 0 && active_fe_index.size() > 0 && + active_fe_index[cell] != active_fe_index[cell-1]) + cell_t_prev[n_vectors-1] = numbers::invalid_unsigned_int; + evaluate_on_cell (tria, &cells[cell*n_vectors], + cell, my_q, cell_t_prev, cell_t, fe_val, data); + + // now reorder the data into vectorized + // types. if we are here for the first time, + // we need to find out whether the Jacobian + // allows for some simplification (Cartesian, + // linear) taking n_vectors cell together and + // we have to insert that data into the + // respective fields. Also, we have to + // compress different cell indicators into one + // structure. + + if (my_q == 0) + { + // find the most general cell type (most + // general type is 2 (general cell)) + unsigned int most_general_type = 0; + for (unsigned int j=0; j most_general_type) + most_general_type = cell_t[j]; + AssertIndexRange (most_general_type, 3); + + // Cartesian cell with diagonal Jacobian: only + // insert the diagonal of the inverse and the + // Jacobian determinant + unsigned int insert_position = numbers::invalid_unsigned_int; + typedef std::vector >::iterator iter; + if (most_general_type == 0) + { + std::pair new_entry; + for (unsigned int d=0; d (data.const_jac, true); + std::pair insertion (hash, -insert_position); + iter pos = std::lower_bound (hash_collection.begin(), + hash_collection.end(), + insertion); + + // ok, found a data field with the same + // key. check whether we really hit a + // duplicate, i.e., whether the hash really + // was effective + bool duplicate = true; + if (pos != hash_collection.end() && + pos->first == hash) + { + for (unsigned int d=0; dsecond].first[d][j])> + hash_value.scaling) + duplicate = false; + } + else + duplicate = false; + + // if no duplicate, insert the data + if (duplicate == false) + { + hash_collection.insert (pos, insertion); + cartesian.push_back (new_entry); + } + // else, remember the position + else + insert_position = -pos->second; + } + + // Constant Jacobian case. same strategy as + // before, but with other data fields + else if (most_general_type == 1) + { + insert_position = linear.size(); + + // check whether everything is the same as on + // the previous cell + const unsigned int hash = hash_value.template operator() (data.const_jac, false); + std::pair insertion (hash, -insert_position); + iter pos = std::lower_bound (hash_collection.begin(), + hash_collection.end(), + insertion); + + // ok, found a data field with the same + // key. check whether we really hit a + // duplicate + bool duplicate = true; + if (pos != hash_collection.end() && + pos->first == hash) + { + for (unsigned int d=0; dsecond].first[d][e][j])> + hash_value.scaling) + duplicate = false; + } + else + duplicate = false; + + if (duplicate == false) + { + hash_collection.insert (pos, insertion); + linear.push_back (std::pair(data.const_jac, + make_vectorized_array (Number(0.)))); + } + else + insert_position = -pos->second; + } + + // general cell case: first resize the data + // field to fit the new data. if we are here + // the first time, assume that there are many + // general cells to come, so reserve some + // memory in order to not have too many + // reallocations and memcpy's. The scheme used + // here involves at most one reallocation. + else + { + Assert (most_general_type == 2, ExcInternalError()); + insert_position = current_data.rowstart_jacobians.size(); + if (current_data.rowstart_jacobians.size() == 0) + { + unsigned int reserve_size = (n_macro_cells-cell+1)/2; + current_data.rowstart_jacobians.reserve + (reserve_size); + reserve_size *= n_q_points; + current_data.jacobians.reserve (reserve_size); + if (update_flags & update_JxW_values) + current_data.JxW_values.reserve (reserve_size); + if (update_flags & update_jacobian_grads) + current_data.jacobians_grad_diag.reserve (reserve_size); + if (update_flags & update_jacobian_grads) + current_data.jacobians_grad_upper.reserve (reserve_size); + } + } + + cell_type[cell] = ((insert_position << n_cell_type_bits) + + most_general_type); + + } // end if (my_q == 0) + + // general cell case: now go through all + // quadrature points and collect the + // data. done for all different quadrature + // formulas, so do it outside the above loop. + if (get_cell_type(cell) == 2) + { + const unsigned int previous_size = + current_data.jacobians.size(); + current_data.rowstart_jacobians.push_back (previous_size); + if (update_flags & update_JxW_values) + { + AssertDimension (previous_size, + current_data.JxW_values.size()); + } + if (update_flags & update_jacobian_grads) + { + AssertDimension (previous_size, + current_data.jacobians_grad_diag.size()); + AssertDimension (previous_size, + current_data.jacobians_grad_upper.size()); + } + for (unsigned int q=0; q(grad_diag)); + } + + // sets upper-diagonal part of Jacobian + tensorUT grad_upper; + for (unsigned int d=0, count=0; d + void + MappingInfo::evaluate_on_cell (const dealii::Triangulation &tria, + const std::pair *cells, + const unsigned int cell, + const unsigned int my_q, + unsigned int (&cell_t_prev)[n_vectors], + unsigned int (&cell_t)[n_vectors], + FEValues &fe_val, + CellData &data) const + { + const unsigned int n_q_points = fe_val.n_quadrature_points; + const UpdateFlags update_flags = fe_val.get_update_flags(); + + // this should be the same value as + // used in HashValue::scaling (but we + // not have that field here) + const double zero_tolerance_double = data.jac_size * + std::numeric_limits::epsilon() * 1024.; + for (unsigned int j=0; j::cell_iterator + cell_it (&tria, cells[j].first, cells[j].second); + fe_val.reinit(cell_it); + cell_t[j] = numbers::invalid_unsigned_int; + + // extract quadrature points and store them + // temporarily. if we have Cartesian cells, we + // can compress the indices + if (update_flags & update_quadrature_points) + for (unsigned int q=0; q &point = fe_val.quadrature_point(q); + for (unsigned int d=0; d 0 && get_cell_type(cell) < 2) + continue; + + // first round: if the transformation is + // detected to be the same as on the old cell, + // we only need to copy over the data. + if (fe_val.get_cell_similarity() == CellSimilarity::translation + && + my_q == 0) + { + if (j==0) + { + Assert (cell>0, ExcInternalError()); + cell_t[j] = cell_t_prev[n_vectors-1]; + } + else + cell_t[j] = cell_t[j-1]; + } + + const DerivativeForm<1,dim,dim> & jac_0 = fe_val.jacobian(0); + + if (my_q == 0) + { + // check whether the Jacobian is constant on + // this cell the first time we come around + // here + if (cell_t[j] == numbers::invalid_unsigned_int) + { + bool jacobian_constant = true; + for (unsigned int q=1; q &jac = fe_val.jacobian(q); + for (unsigned int d=0; d + zero_tolerance_double) + jacobian_constant = false; + if (jacobian_constant == false) + break; + } + + // check whether the Jacobian is diagonal to + // machine accuracy + bool cell_cartesian = jacobian_constant; + for (unsigned int d=0; d + zero_tolerance_double) + { + cell_cartesian=false; + break; + } + + // in case we have only one quadrature point, + // we can have non-constant Jacobians, but we + // cannot detect it by comparison from one + // quadrature point to the next: in that case, + // need to look at second derivatives and see + // whether there are some non-zero entries + // (this is necessary since we determine the + // constness of the Jacobian for the first + // quadrature formula and might not look at + // them any more for the second, third + // quadrature formula + if (cell_cartesian == false && n_q_points == 1) + { + Assert (update_flags & update_jacobian_grads, + ExcInternalError()); + const DerivativeForm<1,dim,dim> &jac = fe_val.jacobian(0); + const DerivativeForm<2,dim,dim> &jacobian_grad = + fe_val.jacobian_grad(0); + for (unsigned int d=0; d + zero_tolerance_double) + jacobian_constant = false; + } + } + // set cell type + if (cell_cartesian == true) + cell_t[j] = 0; + else if (jacobian_constant == true) + cell_t[j] = 1; + else + cell_t[j] = 2; + } + + // Cartesian cell + if (cell_t[j] == 0) + { + // set Jacobian into diagonal and clear + // off-diagonal part + for (unsigned int d=0; d &jac = fe_val.jacobian(q); + for (unsigned int d=0; d &jacobian_grad = fe_val.jacobian_grad(q); + for (unsigned int d=0; d + std::size_t MappingInfo::MappingInfoDependent::memory_consumption() const + { + std::size_t + memory = MemoryConsumption::memory_consumption (jacobians); + memory += MemoryConsumption::memory_consumption (JxW_values); + memory += MemoryConsumption::memory_consumption (jacobians_grad_diag); + memory += MemoryConsumption::memory_consumption (jacobians_grad_upper); + memory += MemoryConsumption::memory_consumption (rowstart_q_points); + memory += MemoryConsumption::memory_consumption (quadrature_points); + memory += MemoryConsumption::memory_consumption (quadrature_formula); + memory += MemoryConsumption::memory_consumption (quadrature_formula_faces); + memory += MemoryConsumption::memory_consumption (quadrature_weights); + memory += MemoryConsumption::memory_consumption (n_q_points); + memory += MemoryConsumption::memory_consumption (n_q_points_face); + memory += MemoryConsumption::memory_consumption (quad_index_conversion); + return memory; + } + + + + template + std::size_t MappingInfo::memory_consumption() const + { + std::size_t + memory= MemoryConsumption::memory_consumption (mapping_data_gen); + memory += MemoryConsumption::memory_consumption (linear); + memory += MemoryConsumption::memory_consumption (cartesian); + memory += MemoryConsumption::memory_consumption (cell_type); + memory += sizeof (this); + return memory; + } + + + + template + template + void MappingInfo::MappingInfoDependent::print_memory_consumption + (STREAM &out, + const SizeInfo &size_info) const + { + // print_mem involves global communication, so + // we can disable the check here only if no + // processor has any such data +#if DEAL_II_COMPILER_SUPPORTS_MPI + unsigned int general_size_glob = 0, general_size_loc = jacobians.size(); + MPI_Allreduce (&general_size_loc, &general_size_glob, 1, MPI_UNSIGNED, + MPI_MAX, size_info.communicator); +#else + unsigned int general_size_glob = jacobians.size(); +#endif + if (general_size_glob > 0) + { + out << " Memory Jacobian data: "; + size_info.print_mem (out, + MemoryConsumption::memory_consumption (jacobians) + + + MemoryConsumption::memory_consumption (JxW_values)); + out << " Memory second derivative data: "; + size_info.print_mem (out, + MemoryConsumption::memory_consumption (jacobians_grad_diag) + + + MemoryConsumption::memory_consumption (jacobians_grad_upper)); + } + +#if DEAL_II_COMPILER_SUPPORTS_MPI + unsigned int quad_size_glob = 0, quad_size_loc = quadrature_points.size(); + MPI_Allreduce (&quad_size_loc, &quad_size_glob, 1, MPI_UNSIGNED, + MPI_MAX, size_info.communicator); +#else + unsigned int quad_size_glob = quadrature_points.size(); +#endif + if (quad_size_glob > 0) + { + out << " Memory quadrature points: "; + size_info.print_mem (out, + MemoryConsumption::memory_consumption (rowstart_q_points) + + + MemoryConsumption::memory_consumption (quadrature_points)); + } + } + + + + template + template + void MappingInfo::print_memory_consumption(STREAM &out, + const SizeInfo &size_info) const + { + out << " Cell types: "; + size_info.print_mem (out, MemoryConsumption::memory_consumption (cell_type)); + out << " Memory transformations compr: "; + size_info.print_mem (out, MemoryConsumption::memory_consumption (linear) + + MemoryConsumption::memory_consumption (cartesian)); + for (unsigned int j=0; j +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if DEAL_II_USE_MT==1 +#include +#include +#include +#include +#endif + +#include +#include +#include + + +DEAL_II_NAMESPACE_OPEN + + + +/** + * This class collects all the data that is stored for the matrix free + * implementation. The storage scheme is tailored towards several + * loops performed with the same data, i.e., typically doing many + * matrix-vector products or residual computations on the same + * mesh. The class is used in step-37 and step-48. + * + * The stored data can be subdivided into three main components: + * + * - DoFInfo: It stores how local degrees of freedom relate to global degrees + * of freedom. It includes a description of constraints that are evaluated + * as going through all local degrees of freedom on a cell. + * + * - MappingInfo: It stores the transformations from real to unit cells that + * are necessary in order to build derivatives of finite element functions + * and find location of quadrature weights in physical space. + * + * - FEEvaluationData: It contains the shape functions of the finite element, + * evaluated on the unit cell. + * + * Besides the initialization routines, this class implements only a + * single operation, namely a loop over all cells (cell_loop()). This + * loop is scheduled in such a way that cells that share degrees of + * freedom are not worked on simultaneously, which implies that it is + * possible to write to vectors (or matrices) in parallel without + * having to explicitly synchronize access to these vectors and + * matrices. This class does not implement any shape values, all it + * does is to cache the respective data. To implement finite element + * operations, use the class FEEvaluation (or some of the related + * classes). + * + * This class traverses the cells in a different order than the usual + * Triangulation class in deal.II, in order to be flexible with respect to + * parallelization in shared memory and vectorization. + * + * Vectorization is implemented by merging several topological cells into one + * so-called macro cell. This enables the application of all cell-related + * operations for several cells with one CPU instruction and is one of the + * main features of this framework. + * + * @author Katharina Kormann, Martin Kronbichler, 2010, 2011 + */ + +template +class MatrixFree +{ +public: + + /** + * Collects the options for initialization of + * the MatrixFree class. The first + * parameter specifies the MPI communicator to + * be used, the second the parallelization + * options in shared memory (task-based + * parallelism, where one can choose between + * no parallelism and three schemes that avoid + * that cells with access to the same vector + * entries are accessed simultaneously), the + * third with the block size for task parallel + * scheduling, the fourth the update flags + * that should be stored by this class. + * + * The fifth parameter specifies the level in + * the triangulation from which the indices + * are to be used. If the level is set to + * numbers::invalid_unsigned_int, the active + * cells are traversed, and otherwise the + * cells in the given level. This option has + * no effect in case a DoFHandler or + * hp::DoFHandler is given. + * + * The parameter @p initialize_plain_indices + * indicates whether the DoFInfo class should + * also allow for access to vectors without + * resolving constraints. + * + * The last two parameters allow the user to + * disable some of the initialization + * processes. For example, if only the + * scheduling that avoids touching the same + * vector/matrix indices simultaneously is to + * be found, the mapping needs not be + * initialized. Likewise, if the mapping has + * changed from one iteration to the next but + * the topology has not (like when using a + * deforming mesh with MappingQEulerian), it + * suffices to initialize the mapping only. + */ + struct AdditionalData + { + /** + * Collects options for task parallelism. + */ + enum TasksParallelScheme {none, partition_partition, partition_color, color}; + + /** + * Constructor for AdditionalData. + */ + AdditionalData (const MPI_Comm mpi_communicator = MPI_COMM_SELF, + const TasksParallelScheme tasks_parallel_scheme = partition_partition, + const unsigned int tasks_block_size = 0, + const UpdateFlags mapping_update_flags = update_gradients | update_JxW_values, + const unsigned int level_mg_handler = numbers::invalid_unsigned_int, + const bool store_plain_indices = true, + const bool initialize_indices = true, + const bool initialize_mapping = true) + : + mpi_communicator (mpi_communicator), + tasks_parallel_scheme (tasks_parallel_scheme), + tasks_block_size (tasks_block_size), + mapping_update_flags (mapping_update_flags), + level_mg_handler (level_mg_handler), + store_plain_indices (store_plain_indices), + initialize_indices (initialize_indices), + initialize_mapping (initialize_mapping) + {}; + + /** + * Sets the MPI communicator that the parallel + * layout of the operator should be based + * upon. Defaults to MPI_COMM_SELF, but should + * be set to a communicator similar to the one + * used for a distributed triangulation in + * order to inform this class over all cells + * that are present. + */ + MPI_Comm mpi_communicator; + + /** + * Sets the scheme for task parallelism. There + * are four options available. If set to @p + * none, the operator application is done in + * serial without shared memory + * parallelism. If this class is used together + * with MPI and MPI is also used for + * parallelism within the nodes, this flag + * should be set to @p none. The default value + * is @p partition_partition, i.e. we actually + * use multithreading with the first option + * described below. + * + * The first option @p partition_partition is + * to partition the cells on two levels in + * onion-skin-like partitions and forming + * chunks of tasks_block_size after the + * partitioning. The partitioning finds sets + * of independent cells that enable working in + * parallel without accessing the same vector + * entries at the same time. + * + * The second option @p partition_color is to + * use a partition on the global level and + * color cells within the partitions (where + * all chunks within a color are + * independent). Here, the subdivision into + * chunks of cells is done before the + * partitioning, which might give worse + * partitions but better cache performance if + * degrees of freedom are not renumbered. + * + * The third option @p color is to use a + * traditional algorithm of coloring on the + * global level. This scheme is a special case + * of the second option where only one + * partition is present. Note that for + * problems with hanging nodes, there are + * quite many colors (50 or more in 3D), which + * might degrade parallel performance (bad + * cache behavior, many synchronization + * points). + */ + TasksParallelScheme tasks_parallel_scheme; + + /** + * Sets the number of so-called macro cells + * that should form one partition. If zero + * size is given, the class tries to find a + * good size for the blocks based on the + * number of threads stored in + * multithread_info.n_default_threads and the + * number of cells present. Otherwise, the + * given number is used. If the given number + * is larger than one third of the number of + * total cells, this means no + * parallelism. Note that in the case + * vectorization is used, a macro cell + * consists of more than one physical cell. + */ + unsigned int tasks_block_size; + + /** + * This flag is used to determine which + * quantities should be cached. This class can + * cache data needed for gradient computations + * (inverse Jacobians), Jacobian determinants + * (JxW), quadrature points as well as data + * for Hessians (derivative of Jacobians). By + * default, only data for gradients and + * Jacobian determinants times quadrature + * weights, JxW, are cached. If quadrature + * points or second derivatives are needed, + * they must be specified by this field (even + * though second derivatives might still be + * evaluated on Cartesian cells without this + * option set here, since there the Jacobian + * describes the mapping completely). + */ + UpdateFlags mapping_update_flags; + + /** + * If working on a MGDoFHandler, this option + * can be used to define whether we work on a + * certain level of the mesh, and not the + * active cells. If set to + * invalid_unsigned_int (which is the default + * value), the active cells are gone through, + * otherwise the level given by this + * parameter. + */ + unsigned int level_mg_handler; + + /** + * Controls whether to allow reading from + * vectors without resolving constraints, + * i.e., just read the local values of the + * vector. By default, this option is + * disabled, so if you want to use + * FEEvaluationBase::read_dof_values_plain, + * this flag needs to be set. + */ + bool store_plain_indices; + + /** + * Option to control whether the indices + * stored in the DoFHandler should be read and + * the pattern for task parallelism should be + * set up in the initialize method of + * MatrixFree. Defaults to true. Can be + * disabled in case the mapping should be + * recomputed (e.g. when using a deforming + * mesh described through MappingEulerian) but + * the topology of cells has remained the + * same. + */ + bool initialize_indices; + + /** + * Option to control whether the mapping + * information should be computed in the + * initialize method of + * MatrixFree. Defaults to true. Can be + * disabled when only some indices should be + * set up (e.g. when only a set of independent + * cells should be computed). + */ + bool initialize_mapping; + }; + + /** + * Default empty constructor. Does + * nothing. + */ + MatrixFree (); + + /** + * Destructor. + */ + ~MatrixFree(); + + /** + * Extracts the information needed to + * perform loops over cells. The + * DoFHandler and ConstraintMatrix + * describe the layout of degrees of + * freedom, the DoFHandler and the + * mapping describe the transformations + * from unit to real cell, and the finite + * element underlying the DoFHandler + * together with the quadrature formula + * describe the local operations. Note + * that the finite element underlying the + * DoFHandler must either be scalar or + * contain several copies of the same + * element. Mixing several different + * elements into one FESystem is not + * allowed. In that case, use the + * initialization function with several + * DoFHandler arguments. + * + * The @p IndexSet @p locally_owned_dofs + * is used to specify the parallel + * partitioning with MPI. Usually, this + * needs not be specified, and the other + * initialization function without and @p + * IndexSet description can be used, + * which gets the partitioning + * information builtin into the + * DoFHandler. + */ + template + void reinit (const Mapping &mapping, + const DH &dof_handler, + const ConstraintMatrix &constraint, + const IndexSet &locally_owned_dofs, + const Quadrature &quad, + const AdditionalData additional_data = AdditionalData()); + + /** + * Initializes the data structures. Same + * as above, but with index set stored in + * the DoFHandler for describing the + * locally owned degrees of freedom. + */ + template + void reinit (const Mapping &mapping, + const DH &dof_handler, + const ConstraintMatrix &constraint, + const Quadrature &quad, + const AdditionalData additional_data = AdditionalData()); + + /** + * Initializes the data structures. Same + * as above, but with mapping @p + * MappingQ1. + */ + template + void reinit (const DH &dof_handler, + const ConstraintMatrix &constraint, + const Quadrature &quad, + const AdditionalData additional_data = AdditionalData()); + + /** + * Extracts the information needed to + * perform loops over cells. The + * DoFHandler and ConstraintMatrix + * describe the layout of degrees of + * freedom, the DoFHandler and the + * mapping describe the transformations + * from unit to real cell, and the finite + * element underlying the DoFHandler + * together with the quadrature formula + * describe the local operations. As + * opposed to the scalar case treated + * with the other initialization + * functions, this function allows for + * problems with two or more different + * finite elements. The DoFHandlers to + * each element must be passed as + * pointers to the initialization + * function. Note that the finite element + * underlying an DoFHandler must either + * be scalar or contain several copies of + * the same element. Mixing several + * different elements into one @p + * FE_System is not allowed. + * + * This function also allows for using + * several quadrature formulas, e.g. when + * the description contains independent + * integrations of elements of different + * degrees. However, the number of + * different quadrature formulas can be + * sets independently from the number of + * DoFHandlers, when several elements are + * always integrated with the same + * quadrature formula. + * + * The @p IndexSet @p locally_owned_dofs + * is used to specify the parallel + * partitioning with MPI. Usually, this + * needs not be specified, and the other + * initialization function without and @p + * IndexSet description can be used, + * which gets the partitioning + * information from the DoFHandler. This + * is the most general initialization + * function. + */ + template + void reinit (const Mapping &mapping, + const std::vector &dof_handler, + const std::vector &constraint, + const std::vector &locally_owned_set, + const std::vector &quad, + const AdditionalData additional_data = AdditionalData()); + + /** + * Initializes the data structures. Same + * as before, but now the index set + * description of the locally owned range + * of degrees of freedom is taken from + * the DoFHandler. + */ + template + void reinit (const Mapping &mapping, + const std::vector &dof_handler, + const std::vector &constraint, + const std::vector &quad, + const AdditionalData additional_data = AdditionalData()); + + /** + * Initializes the data structures. Same + * as above, but with mapping @p + * MappingQ1. + */ + template + void reinit (const std::vector &dof_handler, + const std::vector &constraint, + const std::vector &quad, + const AdditionalData additional_data = AdditionalData()); + + /** + * Initializes the data structures. Same + * as before, but now the index set + * description of the locally owned range + * of degrees of freedom is taken from + * the DoFHandler. Moreover, only a + * single quadrature formula is used, as + * might be necessary when several + * components in a vector-valued problem + * are integrated together based on the + * same quadrature formula. + */ + template + void reinit (const Mapping &mapping, + const std::vector &dof_handler, + const std::vector &constraint, + const Quadrature &quad, + const AdditionalData additional_data = AdditionalData()); + + /** + * Initializes the data structures. Same + * as above, but with mapping @p + * MappingQ1. + */ + template + void reinit (const std::vector &dof_handler, + const std::vector &constraint, + const Quadrature &quad, + const AdditionalData additional_data = AdditionalData()); + + /** + * Copy function. Creates a deep copy of all + * data structures. It is usually enough to + * keep the data for different operations + * once, so this function should not be needed + * very often. + */ + void copy_from (const MatrixFree &matrix_free_base); + + /** + * Clears all data fields and brings the class + * into a condition similar to after having + * called the default constructor. + */ + void clear(); + + /** + * This method runs the loop over all + * cells (in parallel) and performs + * the MPI data exchange on the source + * vector and destination vector. The + * first argument indicates a function + * object that has the following + * signature: cell_operation + * (const MatrixFree + * &, OutVector &, InVector &, + * std::pair &), where the first + * argument passes the data of the + * calling class and the last argument + * defines the range of cells which + * should be worked on (typically more + * than one cell should be worked on + * in order to reduce overheads). One + * can pass a pointer to an object in + * this place if it has an + * operator() with the + * correct set of arguments since such + * a pointer can be converted to the + * function object. + */ + template + void cell_loop (const std_cxx1x::function &, + OutVector &, + const InVector&, + const std::pair &)> &cell_operation, + OutVector &dst, + const InVector &src) const; + + /** + * This is the second variant to run the loop + * over all cells, now providing a function + * pointer to a member function of class @p + * CLASS with the signature + * cell_operation (const + * MatrixFree &, OutVector &, + * InVector &, std::pair&)const. This method obviates the need + * to call std_cxx1x::bind to bind the class + * into the given function in case the local + * function needs to access data in the class + * (i.e., it is a non-static member function). + */ + template + void cell_loop (void (CLASS::*function_pointer)(const MatrixFree &, + OutVector&, + const InVector&, + const std::pair &)const, + const CLASS *owning_class, + OutVector &dst, + const InVector &src) const; + + /** + * Same as above, but for class member + * functions which are non-const. + */ + template + void cell_loop (void (CLASS::*function_pointer)(const MatrixFree &, + OutVector&, + const InVector&, + const std::pair &), + CLASS *owning_class, + OutVector &dst, + const InVector &src) const; + + /** + * Returns an approximation of the memory + * consumption of this class in bytes. + */ + std::size_t memory_consumption() const; + + /** + * Prints a detailed summary of memory + * consumption in the different structures of + * this class to the given output stream. + */ + template + void print_memory_consumption(STREAM &out) const; + + /** + * Prints a summary of this class to the given + * output stream. It is focused on the + * indices, and does not print all the data + * stored. + */ + void print (std::ostream &out) const; + + /** + * Initialize function for a general + * vector. The length of the vector is equal + * to the total number of degrees in the + * DoFHandler. If the vector is of class + * parallel::distributed::Vector, the ghost + * entries are set accordingly. For + * vector-valued problems with several + * DoFHandlers underlying this class, the + * parameter @p vector_component defines which + * component is to be used. + */ + template + void initialize_dof_vector(VectorType &vec, + const unsigned int vector_component=0) const; + + /** + * Initialize function for a distributed + * vector. The length of the vector is equal + * to the total number of degrees in the + * DoFHandler. If the vector is of class + * parallel::distributed::Vector, the ghost + * entries are set accordingly. For + * vector-valued problems with several + * DoFHandlers underlying this class, the + * parameter @p vector_component defines which + * component is to be used. + */ + template + void initialize_dof_vector(parallel::distributed::Vector &vec, + const unsigned int vector_component=0) const; + + /** + * Returns the partitioner that represents the + * locally owned data and the ghost indices + * where access is needed to for the cell + * loop. + */ + const std_cxx1x::shared_ptr& + get_vector_partitioner (const unsigned int vector_component=0) const; + + /** + * Returns a list of all degrees of freedom + * that are constrained. The list is returned + * in local index space for the locally owned + * range of the vector, not in global + * numbers. In addition, it only returns the + * indices for degrees of freedom that are + * owned locally, not for ghosts. + */ + const std::vector & + get_constrained_dofs (const unsigned int fe_component = 0) const; + + /** + * In the hp adaptive case, a subrange of + * cells as computed during the cell loop + * might contain elements of different + * degrees. Use this function to compute what + * the subrange for an individual finite + * element degree is. The finite element + * degree is associated to the vector + * component given in the function call. + */ + std::pair + create_cell_subrange_hp (const std::pair &range, + const unsigned int fe_degree, + const unsigned int vector_component = 0) const; + + /** + * In the hp adaptive case, a subrange of + * cells as computed during the cell loop + * might contain elements of different + * degrees. Use this function to compute what + * the subrange for a given index the hp + * finite element, as opposed to the finite + * element degree in the other function. + */ + std::pair + create_cell_subrange_hp_by_index (const std::pair &range, + const unsigned int fe_index, + const unsigned int vector_component = 0) const; + /** + * Calls renumber_dofs function in dof + * info which renumbers the the + * degrees of freedom according to the + * ordering for parallelization. + */ + void renumber_dofs (std::vector &renumbering, + const unsigned int vector_component = 0); + + unsigned int n_components () const; + + /** + * Returns information on task graph. + */ + const internal::MatrixFreeFunctions::TaskInfo & + get_task_info () const; + + /** + * Returns information on system size. + */ + const internal::MatrixFreeFunctions::SizeInfo & + get_size_info () const; + + /** + * Returns the number of cells this structure + * is based on. If you are using a usual + * DoFHandler, it corresponds to the number of + * (locally owned) active cells. Note that + * most data structures in this class do not + * directly act on this number but rather on + * n_macro_cells() which gives the number of + * cells as seen when lumping several cells + * together with vectorization. + */ + unsigned int n_physical_cells () const; + + /** + * Returns the number of macro cells that this + * structure works on, i.e., the number of + * cell chunks that are worked on after the + * application of vectorization which in + * general works on several cells at once. The + * cell range in @p cell_loop runs from zero + * to n_macro_cells() (exclusive), so this is + * the appropriate size if you want to store + * arrays of data for all cells to be worked + * on. This number is approximately + * n_physical_cells()/VectorizedArray::n_array_elements + * (depending on how many cells are not filled + * up completely). + */ + unsigned int n_macro_cells () const; + + /* + * Returns geometry-dependent + * information on the cells. + */ + + const internal::MatrixFreeFunctions::MappingInfo & + get_mapping_info () const; + + /** + * Returns information on indexation + * degrees of freedom. + */ + + const internal::MatrixFreeFunctions::DoFInfo & + get_dof_info (const unsigned int fe_component = 0) const; + + /* + * Returns the constraint pool holding + * all the constraints in the mesh. + */ + + const internal::MatrixFreeFunctions::CompressedMatrix & + get_constraint_pool () const; + + /** + * In case this structure was built based on a + * DoFHandler, this returns the + * DoFHandler. Note that this function returns + * an exception in case the structure was + * based on MGDoFHandler and a level has been + * specified in InitializationOption. + */ + const DoFHandler & + get_dof_handler (const unsigned int fe_component = 0) const; + + /** + * In case this structure was built based on a + * DoFHandler, this returns the + * DoFHandler. Note that this function returns + * an exception in case the structure was + * based on MGDoFHandler and a level has been + * specified in InitializationOption. + */ + const MGDoFHandler & + get_mg_dof_handler (const unsigned int fe_component = 0) const; + + /** + * This returns the cell iterator in deal.II + * speak to a given cell in the renumbering of + * this structure. This function returns an + * exception in case the structure was + * constructed based on an MGDoFHandler with + * level specified, as these cells are in + * general not active. + * + * Note that the cell iterators in deal.II go + * through cells differently to what the cell + * loop of this class does. This is because + * several cells are worked on together + * (vectorization), and since cells with + * neighbors on different MPI processors need + * to be accessed at a certain time when + * accessing remote data and overlapping + * communication with computation. + */ + typename DoFHandler::active_cell_iterator + get_cell_iterator (const unsigned int macro_cell_number, + const unsigned int vector_number, + const unsigned int fe_component = 0) const; + + /** + * This returns the cell iterator in deal.II + * speak to a given cell in the renumbering of + * this structure. This function returns an + * exception in case the structure was + * constructed based on a DoFHandler and not + * MGDoFHandler. + * + * Note that the cell iterators in deal.II go + * through cells differently to what the cell + * loop of this class does. This is because + * several cells are worked on together + * (vectorization), and since cells with + * neighbors on different MPI processors need + * to be accessed at a certain time when + * accessing remote data and overlapping + * communication with computation. + */ + typename MGDoFHandler::cell_iterator + get_mg_cell_iterator (const unsigned int macro_cell_number, + const unsigned int vector_number, + const unsigned int fe_component = 0) const; + + /** + * This returns the cell iterator in deal.II + * speak to a given cell in the renumbering of + * this structure. This function returns an + * exception in case the structure was + * not constructed based on an hp::DoFHandler. + * + * Note that the cell iterators in deal.II go + * through cells differently to what the cell + * loop of this class does. This is because + * several cells are worked on together + * (vectorization), and since cells with + * neighbors on different MPI processors need + * to be accessed at a certain time when + * accessing remote data and overlapping + * communication with computation. + */ + typename hp::DoFHandler::active_cell_iterator + get_hp_cell_iterator (const unsigned int macro_cell_number, + const unsigned int vector_number, + const unsigned int fe_component = 0) const; + + /** + * Since this class uses vectorized + * data types with usually more than + * one value in the data field, a + * situation might occur when some + * components of the vector type do + * not correspond to an actual cell in + * the mesh. When using only this + * class, one usually does not need to + * bother about that fact since the + * values are padded with + * zeros. However, when this class is + * mixed with deal.II access to cells, + * care needs to be taken. This + * function returns @p true if not all + * @p n_vectors cells for the given @p + * macro_cell are real cells. To find + * out how many cells are actually + * used, use the function @p + * n_components_filled. + */ + bool + at_irregular_cell (const unsigned int macro_cell_number) const; + + /** + * Use this function to find out how + * many cells over the length of + * vectorization data types correspond + * to real cells in the mesh. For most + * given @p macro_cells, this is just + * @p n_vectors many, but there might + * be one or a few meshes (where the + * numbers do not add up) where there + * are less such components filled, + * indicated by the function @p + * at_irregular_cell. + */ + unsigned int + n_components_filled (const unsigned int macro_cell_number) const; + + /** + * Returns the number of degrees of + * freedom per cell for a given hp + * index. + */ + unsigned int + get_dofs_per_cell (const unsigned int fe_component = 0, + const unsigned int hp_active_fe_index = 0) const; + + /** + * Returns the number of quadrature + * points per cell for a given hp + * index. + */ + unsigned int + get_n_q_points (const unsigned int quad_index = 0, + const unsigned int hp_active_fe_index = 0) const; + + /** + * Returns the number of degrees of + * freedom on each face of the cell + * for given hp index. + */ + unsigned int + get_dofs_per_face (const unsigned int fe_component = 0, + const unsigned int hp_active_fe_index = 0) const; + + /** + * Returns the number of quadrature + * points on each face of the cell for + * given hp index. + */ + unsigned int + get_n_q_points_face (const unsigned int quad_index = 0, + const unsigned int hp_active_fe_index = 0) const; + + /** + * Returns the set of cells that are + * oned by the processor. + */ + const IndexSet & + get_locally_owned_set (const unsigned int fe_component = 0) const; + + /** + * Returns the set of ghost cells + * needed but not owned by the + * processor. + */ + const IndexSet & + get_ghost_set (const unsigned int fe_component = 0) const; + + /** + * Returns the unit cell information + * for given hp index. + */ + const internal::MatrixFreeFunctions::FEEvaluationData & + get_fe_evaluation (const unsigned int fe_component = 0, + const unsigned int quad_index = 0, + const unsigned int hp_active_fe_index = 0, + const unsigned int hp_active_quad_index = 0) const; + + /** + * Returns the quadrature rule for + * given hp index. + */ + const Quadrature & + get_quad (const unsigned int quad_index = 0, + const unsigned int hp_active_fe_index = 0) const; + + /** + * Queries whether or not the + * indexation has been set. + */ + bool indices_initialized () const; + + /** + * Queries whether or not the + * geometry-related information for + * the cells has been set. + */ + + bool mapping_initialized () const; + +private: + + /** + * This is the actual reinit function + * that sets up the indices for the + * DoFHandler and MGDoFHandler case. + */ + template + void internal_reinit (const Mapping &mapping, + const std::vector &dof_handler, + const std::vector &constraint, + const std::vector &locally_owned_set, + const std::vector > &quad, + const AdditionalData additional_data); + + /** + * Same as before but for hp::DoFHandler + * instead of generic DoFHandler type. + */ + void internal_reinit (const Mapping &mapping, + const std::vector*> &dof_handler, + const std::vector &constraint, + const std::vector &locally_owned_set, + const std::vector > &quad, + const AdditionalData additional_data); + + /** + * Initializes the fields in DoFInfo together + * with @p constraint_pool. + */ + void + initialize_indices (const std::vector &constraint, + const std::vector &locally_owned_set); + + /** + * Initializes the DoFHandlers based on a + * DoFHandler argument. + */ + void initialize_dof_handlers (const std::vector*> &dof_handlers, + const unsigned int level); + + /** + * Initializes the DoFHandlers based on a + * DoFHandler argument. + */ + void initialize_dof_handlers (const std::vector*> &dof_handlers, + const unsigned int level); + + /** + * Initializes the DoFHandlers based on a + * DoFHandler argument. + */ + void initialize_dof_handlers (const std::vector*> &dof_handlers, + const unsigned int level); + + /** + * This struct defines which DoFHandler has + * actually been given at construction, in + * order to define the correct behavior when + * querying the underlying DoFHandler. + */ + struct DoFHandlers + { + DoFHandlers () : n_dof_handlers (0), level (numbers::invalid_unsigned_int) {}; + std::vector > > dof_handler; + std::vector > > mg_dof_handler; + std::vector > > hp_dof_handler; + enum ActiveDoFHandler { usual, multigrid, hp } active_dof_handler; + unsigned int n_dof_handlers; + unsigned int level; + }; + + /** + * Pointers to the DoFHandlers underlying the + * current problem. + */ + DoFHandlers dof_handlers; + + /** + * Contains the information about degrees of + * freedom on the individual cells and + * constraints. + */ + std::vector dof_info; + + /** + * Contains the weights for constraints stored + * in DoFInfo. Filled into a separate field + * since several vector components might share + * similar weights, which reduces memory + * consumption. Moreover, it obviates template + * arguments on DoFInfo and keeps it a plain + * field of indices only. + */ + internal::MatrixFreeFunctions::CompressedMatrix constraint_pool; + + /** + * Holds information on transformation of + * cells from reference cell to real cell that + * is needed for evaluating integrals. + */ + internal::MatrixFreeFunctions::MappingInfo mapping_info; + + /** + * Contains shape value information on the + * unit cell. + */ + Table<4,internal::MatrixFreeFunctions::FEEvaluationData > fe_evaluation_data; + + /** + * Describes how the cells are gone + * through. With the cell level (first index + * in this field) and the index within the + * level, one can reconstruct a deal.II + * cell iterator and use all the traditional + * things deal.II offers to do with cell + * iterators. + */ + std::vector > cell_level_index; + + /** + * Stores how many cells we have, how many + * cells that we see after applying + * vectorization (i.e., the number of macro + * cells), and MPI-related stuff. + */ + internal::MatrixFreeFunctions::SizeInfo size_info; + + /** + * Information regarding the shared memory + * parallelization. + */ + internal::MatrixFreeFunctions::TaskInfo task_info; + + /** + * Stores whether indices have been + * initialized. + */ + bool indices_are_initialized; + + /** + * Stores whether indices have been + * initialized. + */ + bool mapping_is_initialized; +}; + + + +/*----------------------- Inline functions ----------------------------------*/ + +#ifndef DOXYGEN + + +template +template +inline +void +MatrixFree::initialize_dof_vector(VectorType &vec, + const unsigned int comp) const +{ + AssertIndexRange (comp, n_components()); + vec.reinit(dof_info[comp].vector_partitioner->global_size); +} + + + +template +template +inline +void +MatrixFree::initialize_dof_vector(parallel::distributed::Vector &vec, + const unsigned int comp) const +{ + AssertIndexRange (comp, n_components()); + vec.reinit(dof_info[comp].vector_partitioner); +} + + + +template +inline +const std_cxx1x::shared_ptr& +MatrixFree::get_vector_partitioner (const unsigned int comp) const +{ + AssertIndexRange (comp, n_components()); + return dof_info[comp].vector_partitioner; +} + + + +template +inline +const std::vector & +MatrixFree::get_constrained_dofs (const unsigned int comp) const +{ + AssertIndexRange (comp, n_components()); + return dof_info[comp].constrained_dofs; +} + + + +template +inline +unsigned int +MatrixFree::n_components () const +{ + AssertDimension (dof_handlers.n_dof_handlers, dof_info.size()); + return dof_handlers.n_dof_handlers; +} + + + +template +inline +const internal::MatrixFreeFunctions::TaskInfo & +MatrixFree::get_task_info () const +{ + return task_info; +} + + + +template +inline +const internal::MatrixFreeFunctions::SizeInfo & +MatrixFree::get_size_info () const +{ + return size_info; +} + + + +template +inline +unsigned int +MatrixFree::n_macro_cells () const +{ + return size_info.n_macro_cells; +} + + + +template +inline +unsigned int +MatrixFree::n_physical_cells () const +{ + return size_info.n_active_cells; +} + + + +template +inline +const internal::MatrixFreeFunctions::MappingInfo & +MatrixFree::get_mapping_info () const +{ + return mapping_info; +} + + + +template +inline +const internal::MatrixFreeFunctions::DoFInfo & +MatrixFree::get_dof_info (unsigned int dof_index) const +{ + AssertIndexRange (dof_index, n_components()); + return dof_info[dof_index]; +} + + + +template +inline +const internal::MatrixFreeFunctions::CompressedMatrix & +MatrixFree::get_constraint_pool () const +{ + return constraint_pool; +} + + + +template +inline +std::pair +MatrixFree::create_cell_subrange_hp +(const std::pair &range, + const unsigned int degree, + const unsigned int vector_component) const +{ + if (dof_info[vector_component].cell_active_fe_index.size() == 0) + return range; + + const unsigned int fe_index = + dof_info[vector_component].fe_index_from_degree(degree); + if (fe_index >= dof_info[vector_component].max_fe_index) + return std::pair(range.second, range.second); + else + return create_cell_subrange_hp_by_index (range, fe_index, vector_component); +} + + + +template +inline +std::pair +MatrixFree::create_cell_subrange_hp_by_index +(const std::pair &range, + const unsigned int fe_index, + const unsigned int vector_component) const +{ + AssertIndexRange (fe_index, dof_info[vector_component].max_fe_index); + const std::vector &fe_indices = + dof_info[vector_component].cell_active_fe_index; + if (fe_indices.size() == 0) + return range; + else + { + // the range over which we are searching must be ordered, + // otherwise we got a range that spans over too many cells +#ifdef DEBUG + for (unsigned int i=range.first+1; i= fe_indices[i-1], + ExcMessage ("Cell range must be over sorted range of fe indices in hp case!")); + AssertIndexRange(range.first,fe_indices.size()+1); + AssertIndexRange(range.second,fe_indices.size()+1); +#endif + std::pair return_range; + return_range.first = + std::lower_bound(&fe_indices[range.first], &fe_indices[range.second], + fe_index) + -&fe_indices[0] ; + return_range.second = + std::lower_bound(&fe_indices[return_range.first], + &fe_indices[range.second], + fe_index+1)-&fe_indices[0]; + Assert(return_range.first >= range.first && + return_range.second <= range.second, ExcInternalError()); + return return_range; + } +} + + + +template +inline +void +MatrixFree::renumber_dofs (std::vector &renumbering, + const unsigned int vector_component) +{ + AssertIndexRange(vector_component, dof_info.size()); + dof_info[vector_component].renumber_dofs (renumbering); +} + + + +template +inline +const DoFHandler & +MatrixFree::get_dof_handler (const unsigned int dof_index) const +{ + AssertIndexRange (dof_index, n_components()); + if (dof_handlers.active_dof_handler == DoFHandlers::usual) + { + AssertDimension (dof_handlers.dof_handler.size(), + dof_handlers.n_dof_handlers); + return *dof_handlers.dof_handler[dof_index]; + } + else if (dof_handlers.active_dof_handler == DoFHandlers::multigrid) + { + Assert (dof_handlers.level == numbers::invalid_unsigned_int, + ExcMessage("Cannot return DoFHandler when MGDoFHandler and " + "level are specified.")); + AssertDimension (dof_handlers.mg_dof_handler.size(), + dof_handlers.n_dof_handlers); + return *dof_handlers.mg_dof_handler[dof_index]; + } + else + { + Assert (false, ExcNotImplemented()); + // put pseudo return argument to avoid + // compiler error, but trigger a segfault in + // case this is only run in optimized mode + return *dof_handlers.dof_handler[numbers::invalid_unsigned_int]; + } +} + + + +template +inline +const MGDoFHandler & +MatrixFree::get_mg_dof_handler (const unsigned int dof_index) const +{ + AssertIndexRange (dof_index, dof_handlers.n_dof_handlers); + Assert (dof_handlers.active_dof_handler == DoFHandlers::multigrid, + ExcNotImplemented()); + return *dof_handlers.mg_dof_handler[dof_index]; +} + + + +template +inline +typename DoFHandler::active_cell_iterator +MatrixFree::get_cell_iterator(const unsigned int macro_cell_number, + const unsigned int vector_number, + const unsigned int dof_index) const +{ + const unsigned int n_vectors=VectorizedArray::n_array_elements; +#ifdef DEBUG + AssertIndexRange (dof_index, dof_handlers.n_dof_handlers); + AssertIndexRange (macro_cell_number, size_info.n_macro_cells); + AssertIndexRange (vector_number, n_vectors); + const unsigned int irreg_filled = + std_cxx1x::get<2>(dof_info[dof_index].row_starts[macro_cell_number]); + if (irreg_filled > 0) + AssertIndexRange (vector_number, irreg_filled); +#endif + + const DoFHandler* dofh = 0; + if (dof_handlers.active_dof_handler == DoFHandlers::usual) + { + AssertDimension (dof_handlers.dof_handler.size(), + dof_handlers.n_dof_handlers); + dofh = dof_handlers.dof_handler[dof_index]; + } + else if (dof_handlers.active_dof_handler == DoFHandlers::multigrid) + { + Assert (dof_handlers.level == numbers::invalid_unsigned_int, + ExcMessage("Cannot return DoFHandler when MGDoFHandler and " + "level are specified.")); + AssertDimension (dof_handlers.mg_dof_handler.size(), + dof_handlers.n_dof_handlers); + dofh = dof_handlers.mg_dof_handler[dof_index]; + } + else + { + Assert (false, ExcMessage ("Cannot return DoFHandler::cell_iterator " + "for underlying DoFHandler!")); + } + + std::pair index = + cell_level_index[macro_cell_number*n_vectors+vector_number]; + return typename DoFHandler::active_cell_iterator + (&dofh->get_tria(), index.first, index.second, dofh); +} + + + +template +inline +typename MGDoFHandler::cell_iterator +MatrixFree::get_mg_cell_iterator(const unsigned int macro_cell_number, + const unsigned int vector_number, + const unsigned int dof_index) const +{ + const unsigned int n_vectors=VectorizedArray::n_array_elements; +#ifdef DEBUG + AssertIndexRange (dof_index, dof_handlers.n_dof_handlers); + AssertIndexRange (macro_cell_number, size_info.n_macro_cells); + AssertIndexRange (vector_number, n_vectors); + const unsigned int irreg_filled = + std_cxx1x::get<2>(dof_info[dof_index].row_starts[macro_cell_number]); + if (irreg_filled > 0) + AssertIndexRange (vector_number, irreg_filled); +#endif + + Assert (dof_handlers.active_dof_handler == DoFHandlers::multigrid, + ExcNotImplemented()); + const MGDoFHandler * dofh = dof_handlers.mg_dof_handler[dof_index]; + + std::pair index = + cell_level_index[macro_cell_number*n_vectors+vector_number]; + return typename MGDoFHandler::cell_iterator + (&dofh->get_tria(), index.first, index.second, dofh); +} + + + +template +inline +typename hp::DoFHandler::active_cell_iterator +MatrixFree::get_hp_cell_iterator(const unsigned int macro_cell_number, + const unsigned int vector_number, + const unsigned int dof_index) const +{ + const unsigned int n_vectors=VectorizedArray::n_array_elements; +#ifdef DEBUG + AssertIndexRange (dof_index, dof_handlers.n_dof_handlers); + AssertIndexRange (macro_cell_number, size_info.n_macro_cells); + AssertIndexRange (vector_number, n_vectors); + const unsigned int irreg_filled = + std_cxx1x::get<2>(dof_info[dof_index].row_starts[macro_cell_number]); + if (irreg_filled > 0) + AssertIndexRange (vector_number, irreg_filled); +#endif + + Assert (dof_handlers.active_dof_handler == DoFHandlers::hp, + ExcNotImplemented()); + const hp::DoFHandler * dofh = dof_handlers.hp_dof_handler[dof_index]; + std::pair index = + cell_level_index[macro_cell_number*n_vectors+vector_number]; + return typename hp::DoFHandler::cell_iterator + (&dofh->get_tria(), index.first, index.second, dofh); +} + + + +template +inline +bool +MatrixFree::at_irregular_cell (const unsigned int macro_cell) const +{ + AssertIndexRange (macro_cell, size_info.n_macro_cells); + return std_cxx1x::get<2>(dof_info[0].row_starts[macro_cell]) > 0; +} + + + +template +inline +unsigned int +MatrixFree::n_components_filled (const unsigned int macro_cell) const +{ + AssertIndexRange (macro_cell, size_info.n_macro_cells); + const unsigned int n_filled = + std_cxx1x::get<2>(dof_info[0].row_starts[macro_cell]); + if (n_filled == 0) + return VectorizedArray::n_array_elements; + else + return n_filled; +} + + + +template +inline +unsigned int +MatrixFree::get_dofs_per_cell(const unsigned int dof_index, + const unsigned int active_fe_index) const +{ + AssertIndexRange (dof_index, dof_info.size()); + return dof_info[dof_index].dofs_per_cell[active_fe_index]; +} + + + +template +inline +unsigned int +MatrixFree::get_n_q_points(const unsigned int quad_index, + const unsigned int active_fe_index) const +{ + AssertIndexRange (quad_index, + mapping_info.mapping_data_gen.size()); + return mapping_info.mapping_data_gen[quad_index].n_q_points[active_fe_index]; +} + + + +template +inline +unsigned int +MatrixFree::get_dofs_per_face(const unsigned int dof_index, + const unsigned int active_fe_index) const +{ + AssertIndexRange (dof_index, dof_info.size()); + return dof_info[dof_index].dofs_per_face[active_fe_index]; +} + + + +template +inline +unsigned int +MatrixFree::get_n_q_points_face(const unsigned int quad_index, + const unsigned int active_fe_index) const +{ + AssertIndexRange (quad_index, + mapping_info.mapping_data_gen.size()); + return mapping_info.mapping_data_gen[quad_index].n_q_points_face[active_fe_index]; +} + + + +template +inline +const IndexSet & +MatrixFree::get_locally_owned_set(const unsigned int dof_index) const +{ + AssertIndexRange (dof_index, dof_info.size()); + return dof_info[dof_index].vector_partitioner->locally_owned_range(); +} + + + +template +inline +const IndexSet & +MatrixFree::get_ghost_set(const unsigned int dof_index) const +{ + AssertIndexRange (dof_index, dof_info.size()); + return dof_info[dof_index].vector_partitioner->ghost_indices(); +} + + + +template +inline +const internal::MatrixFreeFunctions::FEEvaluationData & +MatrixFree::get_fe_evaluation(const unsigned int index_fe, + const unsigned int index_quad, + const unsigned int active_fe_index, + const unsigned int active_quad_index) const +{ + AssertIndexRange (index_fe, fe_evaluation_data.size(0)); + AssertIndexRange (index_quad, fe_evaluation_data.size(1)); + AssertIndexRange (active_fe_index, fe_evaluation_data.size(2)); + AssertIndexRange (active_quad_index, fe_evaluation_data.size(3)); + return fe_evaluation_data(index_fe, index_quad, + active_fe_index, active_quad_index); +} + + + +template +inline +const Quadrature & +MatrixFree::get_quad (const unsigned int quad_index, + const unsigned int active_fe_index) const +{ + AssertIndexRange (quad_index, mapping_info.mapping_data_gen.size()); + return mapping_info.mapping_data_gen[quad_index]. + quadrature_formula[active_fe_index]; +} + + + +template +inline +bool +MatrixFree::indices_initialized () const +{ + return indices_are_initialized; +} + + + +template +inline +bool +MatrixFree::mapping_initialized () const +{ + return mapping_is_initialized; +} + + + +// ------------------------------ reinit functions --------------------------- + +namespace internal +{ + template + std::vector + extract_locally_owned_index_sets (const std::vector &dofh, + const unsigned int) + { + std::vector locally_owned_set; + locally_owned_set.reserve (dofh.size()); + for(unsigned int j=0;jlocally_owned_dofs()); + return locally_owned_set; + } + + + + template + std::vector + extract_locally_owned_index_sets (const std::vector*> &dofh, + const unsigned int level) + { + std::vector locally_owned_set; + locally_owned_set.reserve (dofh.size()); + for(unsigned int j=0;jlocally_owned_dofs()); + else + { + IndexSet new_set (dofh[j]->n_dofs(level)); + new_set.add_range (0, dofh[j]->n_dofs(level)); + locally_owned_set.push_back(new_set); + } + return locally_owned_set; + } +} + + + +template +template +void MatrixFree:: +reinit(const DH &dof_handler, + const ConstraintMatrix &constraints_in, + const Quad &quad, + const MatrixFree::AdditionalData additional_data) +{ + MappingQ1 mapping; + std::vector dof_handlers; + std::vector constraints; + std::vector quads; + + dof_handlers.push_back(&dof_handler); + constraints.push_back (&constraints_in); + quads.push_back (quad); + + std::vector locally_owned_sets = + internal::extract_locally_owned_index_sets (dof_handlers, + additional_data.level_mg_handler); + reinit(mapping, dof_handlers,constraints, locally_owned_sets, quads, + additional_data); +} + + + +template +template +void MatrixFree:: +reinit(const Mapping &mapping, + const DH &dof_handler, + const ConstraintMatrix &constraints_in, + const Quad &quad, + const MatrixFree::AdditionalData additional_data) +{ + std::vector dof_handlers; + std::vector constraints; + std::vector quads; + + dof_handlers.push_back(&dof_handler); + constraints.push_back (&constraints_in); + quads.push_back (quad); + + std::vector locally_owned_sets = + internal::extract_locally_owned_index_sets (dof_handlers, + additional_data.level_mg_handler); + reinit(mapping, dof_handlers,constraints,locally_owned_sets, quads, + additional_data); +} + + + +template +template +void MatrixFree:: +reinit(const std::vector &dof_handler, + const std::vector &constraint, + const std::vector &quad, + const MatrixFree::AdditionalData additional_data) +{ + MappingQ1 mapping; + std::vector locally_owned_set = + internal::extract_locally_owned_index_sets (dof_handler, + additional_data.level_mg_handler); + reinit(mapping, dof_handler,constraint,locally_owned_set, + static_cast >&>(quad), + additional_data); +} + + + +template +template +void MatrixFree:: +reinit(const std::vector &dof_handler, + const std::vector &constraint, + const Quad &quad, + const MatrixFree::AdditionalData additional_data) +{ + MappingQ1 mapping; + std::vector quads; + quads.push_back(quad); + std::vector locally_owned_set = + internal::extract_locally_owned_index_sets (dof_handler, + additional_data.level_mg_handler); + reinit(mapping, dof_handler,constraint,locally_owned_set, quads, + additional_data); +} + + + +template +template +void MatrixFree:: +reinit(const Mapping &mapping, + const std::vector &dof_handler, + const std::vector &constraint, + const Quad &quad, + const MatrixFree::AdditionalData additional_data) +{ + std::vector quads; + quads.push_back(quad); + std::vector locally_owned_set = + internal::extract_locally_owned_index_sets (dof_handler, + additional_data.level_mg_handler); + reinit(mapping, dof_handler,constraint,locally_owned_set, quads, + additional_data); +} + + + +template +template +void MatrixFree:: +reinit(const Mapping &mapping, + const std::vector &dof_handler, + const std::vector &constraint, + const std::vector &quad, + const MatrixFree::AdditionalData additional_data) +{ + std::vector locally_owned_set = + internal::extract_locally_owned_index_sets (dof_handler, + additional_data.level_mg_handler); + reinit(mapping, dof_handler,constraint,locally_owned_set, + quad, additional_data); +} + + + +template +template +void MatrixFree:: +reinit(const Mapping &mapping, + const std::vector &dof_handler, + const std::vector &constraint, + const std::vector &locally_owned_set, + const std::vector &quad, + const MatrixFree::AdditionalData additional_data) +{ + // find out whether we use a hp Quadrature or + // a standard quadrature + std::vector > quad_hp; + for (unsigned int q=0; q(quad[q])); + internal_reinit (mapping, dof_handler, constraint, locally_owned_set, + quad_hp, additional_data); +} + + + +// ------------------------------ implementation of cell_loop --------------- + + // internal helper functions that define how + // to call MPI data exchange functions: for + // generic vectors, do nothing at all. For + // distributed vectors, can call + // update_ghost_values_start function and so on. If we + // have collections of vectors, just do the + // individual functions of the components +namespace internal +{ + template + inline + void update_ghost_values_start (const VectorStruct &, + const unsigned int channel = 0) + { + (void)channel; + } + + + + template + inline + void update_ghost_values_start (const parallel::distributed::Vector &src, + const unsigned int channel = 0) + { + src.update_ghost_values_start(channel); + } + + + + template + inline + void update_ghost_values_start (const std::vector &src) + { + for(unsigned int comp=0;comp + inline + void update_ghost_values_start (const std::vector &src) + { + for(unsigned int comp=0;comp + inline + void update_ghost_values_finish (const VectorStruct &) + {} + + + + template + inline + void update_ghost_values_finish (const parallel::distributed::Vector &src) + { + src.update_ghost_values_finish(); + } + + + + template + inline + void update_ghost_values_finish (const std::vector &src) + { + for(unsigned int comp=0;comp + inline + void update_ghost_values_finish (const std::vector &src) + { + for(unsigned int comp=0;comp + inline + void compress_start (VectorStruct &, + const unsigned int channel = 0) + { + (void)channel; + } + + + + template + inline + void compress_start (parallel::distributed::Vector &dst, + const unsigned int channel = 0) + { + dst.compress_start(channel); + } + + + + template + inline + void compress_start (std::vector &dst) + { + for(unsigned int comp=0;comp + inline + void compress_start (std::vector &dst) + { + for(unsigned int comp=0;comp + inline + void compress_finish (VectorStruct &) + {} + + + + template + inline + void compress_finish (parallel::distributed::Vector &dst) + { + dst.compress_finish(); + } + + + + template + inline + void compress_finish (std::vector &dst) + { + for(unsigned int comp=0;comp + inline + void compress_finish (std::vector &dst) + { + for(unsigned int comp=0;comp + class CellWork : public tbb::task + { + public: + CellWork (const Worker &worker_in, + const unsigned int partition_in, + const internal::MatrixFreeFunctions::TaskInfo &task_info_in) + : + worker (worker_in), + partition (partition_in), + task_info (task_info_in) + {}; + tbb::task* execute () + { + std::pair cell_range + (task_info.partition_color_blocks.data[partition], + task_info.partition_color_blocks.data[partition+1]); + worker(cell_range); + if(blocked==true) + dummy->spawn (*dummy); + return NULL; + } + + tbb::empty_task* dummy; + + private: + const Worker &worker; + const unsigned int partition; + const internal::MatrixFreeFunctions::TaskInfo &task_info; + }; + + + + template + class PartitionWork : public tbb::task + { + public: + PartitionWork (const Worker &function_in, + const unsigned int partition_in, + const internal::MatrixFreeFunctions::TaskInfo &task_info_in) + : + function (function_in), + partition (partition_in), + task_info (task_info_in) + {}; + tbb::task* execute () + { + if(false) + { + std::pair cell_range + (task_info.partition_color_blocks.data + [task_info.partition_color_blocks.row_index[partition]], + task_info.partition_color_blocks.data + [task_info.partition_color_blocks.row_index[partition+1]]); + function(cell_range); + } + else + { + tbb::empty_task* root = new( tbb::task::allocate_root() ) + tbb::empty_task; + unsigned int evens = task_info.partition_evens[partition]; + unsigned int odds = task_info.partition_odds[partition]; + unsigned int n_blocked_workers = + task_info.partition_n_blocked_workers[partition]; + unsigned int n_workers = task_info.partition_n_workers[partition]; + std::vector*> worker(n_workers); + std::vector*> blocked_worker(n_blocked_workers); + + root->set_ref_count(evens+1); + for(unsigned int j=0;jallocate_child()) + CellWork(function,task_info. + partition_color_blocks. + row_index[partition]+ + 2*j,task_info); + if(j>0) + { + worker[j]->set_ref_count(2); + blocked_worker[j-1]->dummy = new(worker[j]->allocate_child()) + tbb::empty_task; + worker[j-1]->spawn(*blocked_worker[j-1]); + } + else + worker[j]->set_ref_count(1); + if(jallocate_child()) + CellWork(function,task_info. + partition_color_blocks. + row_index[partition]+ + 2*j+1,task_info); + } + else + { + if(odds==evens) + { + worker[evens] = new(worker[j]->allocate_child()) + CellWork(function, + task_info. + partition_color_blocks. + row_index[partition]+ + 2*j+1,task_info); + worker[j]->spawn(*worker[evens]); + } + else + { + tbb::empty_task *child = new(worker[j]->allocate_child()) + tbb::empty_task(); + worker[j]->spawn(*child); + } + } + } + + root->wait_for_all(); + root->destroy(*root); + } + if(blocked==true) + dummy->spawn (*dummy); + return NULL; + } + + tbb::empty_task* dummy; + + private: + const Worker &function; + const unsigned int partition; + const internal::MatrixFreeFunctions::TaskInfo &task_info; + }; + + } // end of namespace partition + + + + namespace color + { + template + class CellWork + { + public: + CellWork (const Worker &worker_in, + const internal::MatrixFreeFunctions::TaskInfo &task_info_in) + : + worker (worker_in), + task_info (task_info_in) + {}; + void operator()(const tbb::blocked_range &r) const + { + for(unsigned int block=r.begin();block cell_range; + if(task_info.position_short_block + class PartitionWork : public tbb::task + { + public: + PartitionWork (const Worker &worker_in, + const unsigned int partition_in, + const internal::MatrixFreeFunctions::TaskInfo &task_info_in) + : + worker (worker_in), + partition (partition_in), + task_info (task_info_in) + {}; + tbb::task* execute () + { + unsigned int lower = task_info.partition_color_blocks.data[partition], + upper = task_info.partition_color_blocks.data[partition+1]; + parallel_for(tbb::blocked_range(lower,upper,1), + CellWork (worker,task_info)); + if(blocked==true) + dummy->spawn (*dummy); + return NULL; + } + + tbb::empty_task* dummy; + + private: + const Worker &worker; + const unsigned int partition; + const internal::MatrixFreeFunctions::TaskInfo &task_info; + }; + + } // end of namespace color + + + template + class MPIComDistribute : public tbb::task + { + public: + MPIComDistribute (const VectorStruct &src_in) + : + src(src_in) + {}; + + tbb::task* execute () + { + internal::update_ghost_values_finish(src); + return 0; + } + + private: + const VectorStruct &src; + }; + + + + template + class MPIComCompress : public tbb::task + { + public: + MPIComCompress (VectorStruct &dst_in) + : + dst(dst_in) + {}; + + tbb::task* execute () + { + internal::compress_start(dst); + return 0; + } + + private: + VectorStruct &dst; + }; + +#endif // #if DEAL_II_USE_MT==1 + +} // end of namespace internal + + + +template +template +inline +void +MatrixFree::cell_loop +(const std_cxx1x::function &, + OutVector &, + const InVector&, + const std::pair &)> &cell_operation, + OutVector &dst, + const InVector &src) const +{ +#if DEAL_II_USE_MT==1 + + // Use multithreading if so requested and if + // there is enough work to do in parallel (the + // code might hang if there are less than two + // chunks!) + if (task_info.use_multithreading == true && task_info.n_blocks > 3) + { + // to simplify the function calls, bind away + // all arguments except the cell range + typedef + std_cxx1x::function &range)> + Worker; + + const Worker func = std_cxx1x::bind (std_cxx1x::ref(cell_operation), + std_cxx1x::cref(*this), + std_cxx1x::ref(dst), + std_cxx1x::cref(src), + std_cxx1x::_1); + + if(task_info.use_partition_partition == true) + { + internal::update_ghost_values_start(src); + tbb::empty_task* root = new( tbb::task::allocate_root() ) + tbb::empty_task; + unsigned int evens = task_info.evens; + unsigned int odds = task_info.odds; + root->set_ref_count(evens+1); + unsigned int n_blocked_workers = task_info.n_blocked_workers; + unsigned int n_workers = task_info.n_workers; + std::vector*> + worker(n_workers); + std::vector*> + blocked_worker(n_blocked_workers); + internal::MPIComCompress *worker_compr = + new(root->allocate_child()) + internal::MPIComCompress(dst); + worker_compr->set_ref_count(1); + for(unsigned int j=0;j0) + { + worker[j] = new(root->allocate_child()) + internal::partition::PartitionWork + (func,2*j,task_info); + worker[j]->set_ref_count(2); + blocked_worker[j-1]->dummy = new(worker[j]->allocate_child()) + tbb::empty_task; + if(j>1) + worker[j-1]->spawn(*blocked_worker[j-1]); + else + worker_compr->spawn(*blocked_worker[j-1]); + } + else + { + worker[j] = new(worker_compr->allocate_child()) + internal::partition::PartitionWork + (func,2*j,task_info); + worker[j]->set_ref_count(2); + internal::MPIComDistribute *worker_dist = + new (worker[j]->allocate_child()) + internal::MPIComDistribute(src); + if(odds == 0) + break; + else + worker_dist->spawn(*worker_dist); + } + if(jallocate_child()) + internal::partition::PartitionWork + (func,2*j+1,task_info); + } + else + { + if(odds==evens) + { + worker[evens] = new(worker[j]->allocate_child()) + internal::partition::PartitionWork + (func,2*j+1,task_info); + worker[j]->spawn(*worker[evens]); + } + else + { + tbb::empty_task *child = new(worker[j]->allocate_child()) + tbb::empty_task(); + worker[j]->spawn(*child); + } + } + } + + root->wait_for_all(); + root->destroy(*root); + internal::compress_finish(dst); + } + else // end of partition-partition, start of partition-color + { + internal::update_ghost_values_start(src); + unsigned int evens = task_info.evens; + unsigned int odds = task_info.odds; + + // check whether there is only one + // partition. if not, build up the tree of + // partitions + if (odds > 0) + { + tbb::empty_task* root = new( tbb::task::allocate_root() ) tbb::empty_task; + root->set_ref_count(evens+1); + unsigned int n_blocked_workers = odds-(odds+evens+1)%2; + unsigned int n_workers = task_info.partition_color_blocks.data.size()-1- + n_blocked_workers; + std::vector*> worker(n_workers); + std::vector*> blocked_worker(n_blocked_workers); + unsigned int worker_index = 0, slice_index = 0; + unsigned int spawn_index = 0, spawn_index_new = 0; + int spawn_index_child = -2; + internal::MPIComCompress *worker_compr = new(root->allocate_child()) + internal::MPIComCompress(dst); + worker_compr->set_ref_count(1); + for (unsigned int part=0; + partallocate_child()) + internal::color::PartitionWork(func,slice_index,task_info); + else + worker[worker_index] = new(root->allocate_child()) + internal::color::PartitionWork(func,slice_index,task_info); + slice_index++; + for(;slice_indexset_ref_count(1); + worker_index++; + worker[worker_index] = new (worker[worker_index-1]->allocate_child()) + internal::color::PartitionWork(func,slice_index,task_info); + } + worker[worker_index]->set_ref_count(2); + if(part>0) + { + blocked_worker[(part-1)/2]->dummy = + new (worker[worker_index]->allocate_child()) tbb::empty_task; + worker_index++; + if(spawn_index_child == -1) + worker[spawn_index]->spawn(*blocked_worker[(part-1)/2]); + else + worker[spawn_index]->spawn(*worker[spawn_index_child]); + spawn_index = spawn_index_new; + spawn_index_child = -2; + } + else + { + internal::MPIComDistribute *worker_dist = + new (worker[worker_index]->allocate_child()) + internal::MPIComDistribute(src); + worker_dist->spawn(*worker_dist); + worker_index++; + } + part += 1; + if(partallocate_child()) + internal::color::PartitionWork(func,slice_index,task_info); + slice_index++; + if(slice_index< + task_info.partition_color_blocks.row_index[part+1]) + { + blocked_worker[part/2]->set_ref_count(1); + worker[worker_index] = new(blocked_worker[part/2]->allocate_child()) + internal::color::PartitionWork(func,slice_index,task_info); + slice_index++; + } + else + { + spawn_index_child = -1; + continue; + } + } + for(;slice_index + task_info.partition_color_blocks.row_index[part]) + { + worker[worker_index]->set_ref_count(1); + worker_index++; + } + worker[worker_index] = new (worker[worker_index-1]->allocate_child()) + internal::color::PartitionWork(func,slice_index,task_info); + } + spawn_index_child = worker_index; + worker_index++; + } + else + { + tbb::empty_task *final = new (worker[worker_index-1]->allocate_child()) + tbb::empty_task; + worker[spawn_index]->spawn(*final); + spawn_index_child = worker_index-1; + } + } + if(evens==odds) + worker[spawn_index]->spawn(*worker[spawn_index_child]); + root->wait_for_all(); + root->destroy(*root); + } + // case when we only have one partition: this + // is the usual coloring scheme, and we just + // schedule a parallel for loop for each color + else + { + Assert(evens==1,ExcInternalError()); + internal::update_ghost_values_finish(src); + + for (unsigned int color=0; + color < task_info.partition_color_blocks.row_index[1]; + ++color) + { + unsigned int lower = task_info.partition_color_blocks.data[color], + upper = task_info.partition_color_blocks.data[color+1]; + parallel_for(tbb::blocked_range(lower,upper,1), + internal::color::CellWork + (func,task_info)); + } + + internal::compress_start(dst); + } + internal::compress_finish(dst); + } + } + else +#endif + // serial loop + { + std::pair cell_range; + + internal::update_ghost_values_start (src); + + // First operate on cells where no ghost data is needed (inner + // cells) + { + cell_range.first = 0; + cell_range.second = size_info.boundary_cells_start; + cell_operation (*this, dst, src, cell_range); + } + + // before starting operations on cells that contain ghost nodes + // (outer cells), wait for the MPI commands to finish + internal::update_ghost_values_finish(src); + + // For the outer cells, do the same procedure as for inner + // cells. + if (size_info.boundary_cells_end > size_info.boundary_cells_start) + { + cell_range.first = size_info.boundary_cells_start; + cell_range.second = size_info.boundary_cells_end; + cell_operation (*this, dst, src, cell_range); + } + + internal::compress_start(dst); + + // Finally operate on cells where no ghost data is needed (inner + // cells) + if (size_info.n_macro_cells > size_info.boundary_cells_end) + { + cell_range.first = size_info.boundary_cells_end; + cell_range.second = size_info.n_macro_cells; + cell_operation (*this, dst, src, cell_range); + } + + internal::compress_finish(dst); + } +} + + + +template +template +inline +void +MatrixFree::cell_loop +(void (CLASS::*function_pointer)(const MatrixFree &, + OutVector &, + const InVector&, + const std::pair &)const, + const CLASS *owning_class, + OutVector &dst, + const InVector &src) const +{ + // here, use std_cxx1x::bind to hand a + // function handler with the appropriate + // argument to the other loop function + std_cxx1x::function &, + OutVector &, + const InVector&, + const std::pair &)> + function = std_cxx1x::bind(function_pointer, + std_cxx1x::cref(*owning_class), + std_cxx1x::_1, + std_cxx1x::_2, + std_cxx1x::_3, + std_cxx1x::_4); + cell_loop (function, dst, src); +} + + + +template +template +inline +void +MatrixFree::cell_loop +(void(CLASS::*function_pointer)(const MatrixFree &, + OutVector &, + const InVector&, + const std::pair &), + CLASS *owning_class, + OutVector &dst, + const InVector &src) const +{ + // here, use std_cxx1x::bind to hand a + // function handler with the appropriate + // argument to the other loop function + std_cxx1x::function &, + OutVector &, + const InVector&, + const std::pair &)> + function = std_cxx1x::bind(function_pointer, + std_cxx1x::ref(*owning_class), + std_cxx1x::_1, + std_cxx1x::_2, + std_cxx1x::_3, + std_cxx1x::_4); + cell_loop (function, dst, src); +} + + +#endif // ifndef DOXYGEN + + + +DEAL_II_NAMESPACE_CLOSE + +#endif 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 new file mode 100644 index 0000000000..aaddd916d1 --- /dev/null +++ b/deal.II/include/deal.II/matrix_free/matrix_free.templates.h @@ -0,0 +1,1063 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2011 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 +#include + + +DEAL_II_NAMESPACE_OPEN + + +// --------------------- MatrixFree ----------------------------------- + +template +MatrixFree::MatrixFree() +: + indices_are_initialized (false), + mapping_is_initialized (false) +{} + + + +template +MatrixFree::~MatrixFree() +{} + + + +template +void MatrixFree:: +copy_from (const MatrixFree &v) +{ + clear (); + dof_handlers = v.dof_handlers; + dof_info = v.dof_info; + constraint_pool = v.constraint_pool; + mapping_info = v.mapping_info; + fe_evaluation_data = v.fe_evaluation_data; + cell_level_index = v.cell_level_index; + task_info = v.task_info; + size_info = v.size_info; + indices_are_initialized = v.indices_are_initialized; + mapping_is_initialized = v.mapping_is_initialized; +} + + + +namespace internal +{ + template + void assert_communicator_equality (const dealii::Triangulation&tria, + const MPI_Comm &comm_mf) + { +#if DEAL_II_COMPILER_SUPPORTS_MPI + const parallel::distributed::Triangulation* dist_tria = + dynamic_cast*>(&tria); + if (dist_tria != 0) + { + if (Utilities::System::job_supports_mpi()) + { + int communicators_same = 0; + MPI_Comm_compare (dist_tria->get_communicator(), comm_mf, + &communicators_same); + Assert (communicators_same == MPI_IDENT || + communicators_same == MPI_CONGRUENT, + ExcMessage ("MPI communicator in parallel::distributed::Triangulation " + "and matrix free class must be the same!")); + } + } +#else + (void)tria; + (void)comm_mf; +#endif + } +} + + + +template +template +void MatrixFree:: +internal_reinit(const Mapping &mapping, + const std::vector &dof_handler, + const std::vector &constraint, + const std::vector &locally_owned_set, + const std::vector > &quad, + const MatrixFree::AdditionalData additional_data) +{ + if (additional_data.initialize_indices == true) + { + clear(); + Assert (dof_handler.size() > 0, ExcMessage("No DoFHandler is given.")); + AssertDimension (dof_handler.size(), constraint.size()); + AssertDimension (dof_handler.size(), locally_owned_set.size()); + + // set variables that are independent of FE + internal::assert_communicator_equality (dof_handler[0]->get_tria(), + additional_data.mpi_communicator); + size_info.communicator = additional_data.mpi_communicator; + if (Utilities::System::job_supports_mpi() == true) + { + size_info.my_pid = + Utilities::MPI::this_mpi_process(size_info.communicator); + size_info.n_procs = + Utilities::MPI::n_mpi_processes(size_info.communicator); + } + else + { + size_info.my_pid = 0; + size_info.n_procs = 1; + } + + initialize_dof_handlers (dof_handler, additional_data.level_mg_handler); + for (unsigned int no=0; no(n_fe, n_quad, 1, 1)); + for (unsigned int no=0; no &fe = dof_handler[no]->get_fe(); + for(unsigned int nq =0;nqget_tria(), cell_level_index, + dof_info[0].cell_active_fe_index, mapping, quad, + additional_data.mapping_update_flags); + + mapping_is_initialized = true; + } +} + + + +template +void MatrixFree:: +internal_reinit(const Mapping &mapping, + const std::vector*> &dof_handler, + const std::vector &constraint, + const std::vector &locally_owned_set, + const std::vector > &quad, + const MatrixFree::AdditionalData additional_data) +{ + if (additional_data.initialize_indices == true) + { + clear(); + Assert (dof_handler.size() > 0, ExcMessage("No DoFHandler is given.")); + AssertDimension (dof_handler.size(), constraint.size()); + AssertDimension (dof_handler.size(), locally_owned_set.size()); + + // set variables that are independent of FE + internal::assert_communicator_equality (dof_handler[0]->get_tria(), + additional_data.mpi_communicator); + size_info.communicator = additional_data.mpi_communicator; + if (Utilities::System::job_supports_mpi() == true) + { + size_info.my_pid = + Utilities::MPI::this_mpi_process(size_info.communicator); + size_info.n_procs = + Utilities::MPI::n_mpi_processes(size_info.communicator); + } + else + { + size_info.my_pid = 0; + size_info.n_procs = 1; + } + + initialize_dof_handlers (dof_handler, additional_data.level_mg_handler); + for (unsigned int no=0; noget_fe().size()); + unsigned int n_quad_in_collection = 0; + for (unsigned int q=0; q(n_components, n_quad, + n_fe_in_collection, + n_quad_in_collection)); + for (unsigned int no=0; noget_fe().size(); ++fe_no) + { + const FiniteElement &fe = dof_handler[no]->get_fe()[fe_no]; + for(unsigned int nq =0; nqget_tria(), cell_level_index, + dof_info[0].cell_active_fe_index, mapping, quad, + additional_data.mapping_update_flags); + + mapping_is_initialized = true; + } +} + + + +namespace internal +{ + + // steps through all children and adds the + // active cells recursively + template + void resolve_cell (const InIterator &cell, + std::vector >&cell_its, + const unsigned int subdomain_id) + { + if (cell->has_children()) + for (unsigned int child=0; childn_children(); ++child) + resolve_cell (cell->child(child), cell_its, + subdomain_id); + else + if (cell->subdomain_id() == subdomain_id) + { + Assert (cell->active(), ExcInternalError()); + cell_its.push_back (std::pair + (cell->level(), cell->index())); + } + } +} + + + +template +void MatrixFree:: +initialize_dof_handlers (const std::vector*> &dof_handler, + const unsigned int) +{ + dof_handlers.active_dof_handler = DoFHandlers::usual; + dof_handlers.n_dof_handlers = dof_handler.size(); + dof_handlers.dof_handler.resize (dof_handlers.n_dof_handlers); + for (unsigned int no=0; no &tria = dof_handlers.dof_handler[0]->get_tria(); + { + if (n_mpi_procs == 1) + cell_level_index.reserve (tria.n_active_cells()); + typename Triangulation::cell_iterator cell = tria.begin(0), + end_cell = tria.end(0); + for ( ; cell != end_cell; ++cell) + internal::resolve_cell (cell, cell_level_index, my_pid); + } +} + + + +template +void MatrixFree:: +initialize_dof_handlers (const std::vector*> &dof_handler, + const unsigned int level) +{ + dof_handlers.active_dof_handler = DoFHandlers::multigrid; + dof_handlers.level = level; + dof_handlers.n_dof_handlers = dof_handler.size(); + dof_handlers.mg_dof_handler.resize (dof_handlers.n_dof_handlers); + for (unsigned int no=0; no &tria = dof_handlers.mg_dof_handler[0]->get_tria(); + + if (level == numbers::invalid_unsigned_int) + { + if (n_mpi_procs == 1) + cell_level_index.reserve (tria.n_active_cells()); + typename Triangulation::cell_iterator cell = tria.begin(0), + end_cell = tria.end(0); + for ( ; cell != end_cell; ++cell) + internal::resolve_cell (cell, cell_level_index, my_pid); + } + else + { + AssertIndexRange (level, tria.n_levels()); + cell_level_index.reserve (tria.n_cells(level)); + typename Triangulation::cell_iterator cell = tria.begin(level), + end_cell = tria.end(level); + for ( ; cell != end_cell; ++cell) + cell_level_index.push_back (std::pair + (cell->level(), cell->index())); + } +} + + + +template +void MatrixFree:: +initialize_dof_handlers (const std::vector*> &dof_handler, + const unsigned int) +{ + dof_handlers.active_dof_handler = DoFHandlers::hp; + dof_handlers.n_dof_handlers = dof_handler.size(); + dof_handlers.hp_dof_handler.resize (dof_handlers.n_dof_handlers); + for (unsigned int no=0; no &tria = dof_handler[0]->get_tria(); + + if (n_mpi_procs == 1) + { + cell_level_index.reserve (tria.n_active_cells()); + } + typename hp::DoFHandler::cell_iterator cell = dof_handler[0]->begin(0), + end_cell = dof_handler[0]->end(0); + for ( ; cell != end_cell; ++cell) + internal::resolve_cell (cell, cell_level_index, + my_pid); +} + + + +template +void MatrixFree::initialize_indices +(const std::vector &constraint, + const std::vector &locally_owned_set) +{ + const unsigned int n_fe = dof_handlers.n_dof_handlers; + const unsigned int n_active_cells = cell_level_index.size(); + const unsigned int n_mpi_procs = size_info.n_procs; + + AssertDimension (n_active_cells, cell_level_index.size()); + AssertDimension (n_fe, locally_owned_set.size()); + AssertDimension (n_fe, constraint.size()); + + std::vector local_dof_indices; + std::vector > ghost_dofs(n_fe); + std::vector > > lexicographic_inv(n_fe); + + internal::MatrixFreeFunctions::internal::ConstraintValues constraint_values; + std::vector constraint_indices; + + for(unsigned int no=0; no*> fes; + if (dof_handlers.active_dof_handler == DoFHandlers::hp) + { + const hp::DoFHandler *hpdof = dof_handlers.hp_dof_handler[no]; + const hp::FECollection &fe = hpdof->get_fe(); + for (unsigned int f=0; fget_fe().size(); ++ind) + dof_info[no].fe_index_conversion[ind] = + std::pair(fe[ind].degree, + fe[ind].dofs_per_cell); + } + else + { + const DoFHandler * dofh = + dof_handlers.active_dof_handler == DoFHandlers::usual ? + &*dof_handlers.dof_handler[no] : &*dof_handlers.mg_dof_handler[no]; + fes.push_back (&dofh->get_fe()); + dof_info[no].max_fe_index = 1; + } + + lexicographic_inv[no].resize (fes.size()); + for (unsigned int fe_index = 0; fe_index &fe = *fes[fe_index]; + Assert (fe.n_base_elements() == 1, + ExcMessage ("MatrixFree only works for DoFHandler with one base element")); + const unsigned int n_fe_components = fe.element_multiplicity (0); + + // 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; + + + // get permutation that gives lexicographic + // renumbering of the cell dofs + // renumber (this is necessary for FE_Q, for + // example, since there the vertex DoFs come + // first, which is incompatible with the + // lexicographic ordering necessary to apply + // tensor products efficiently) + const FE_Poly,dim,dim> *cast_fe = + 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 cast_fe == 0. + Assert (cast_fe != 0, ExcNotImplemented()); + + // create a derived finite element that gives + // us access to the inverse numbering (which + // we need in order to get a lexicographic + // ordering of local degrees of freedom) + const internal::MatrixFreeFunctions::internal::FE_PolyAccess&fe_acc = + static_cast &>(*cast_fe); + if (n_fe_components == 1) + { + lexicographic_inv[no][fe_index] = fe_acc.get_numbering_inverse(); + AssertDimension (lexicographic_inv[no][fe_index].size(), + dof_info[no].dofs_per_cell[fe_index]); + } + else + { + // ok, we have more than one component + Assert (n_fe_components > 1, ExcInternalError()); + std::vector scalar_lex=fe_acc.get_numbering(); + AssertDimension (scalar_lex.size() * n_fe_components, + dof_info[no].dofs_per_cell[fe_index]); + lexicographic_inv[no][fe_index].resize (dof_info[no].dofs_per_cell[fe_index]); + std::vector lexicographic (dof_info[no].dofs_per_cell[fe_index]); + for (unsigned int comp=0; comp (0,0,0); + dof_info[no].dof_indices.reserve + ((n_active_cells*dof_info[no].dofs_per_cell[0]*3)/2); + + // cache the constrained indices for use in + // matrix-vector products + { + const unsigned int + start_index = dof_info[no].vector_partitioner->local_range().first, + end_index = dof_info[no].vector_partitioner->local_range().second; + for (unsigned int i=start_index; iis_constrained(i)==true) + dof_info[no].constrained_dofs.push_back(i-start_index); + } + + if (n_mpi_procs > 1) + ghost_dofs[no].reserve (locally_owned_set[no].n_elements()/10+1); + } + + // extract all the global indices associated + // with the computation, and form the ghost + // indices + std::vector boundary_cells; + for(unsigned int counter = 0 ; counter < n_active_cells ; ++counter) + { + bool cell_at_boundary = false; + for (unsigned int no=0; no * dofh = + dof_handlers.active_dof_handler == DoFHandlers::usual ? + &*dof_handlers.dof_handler[no] : &*dof_handlers.mg_dof_handler[no]; + typename DoFHandler::active_cell_iterator + cell_it (&dofh->get_tria(), + cell_level_index[counter].first, + cell_level_index[counter].second, + dofh); + local_dof_indices.resize (dof_info[no].dofs_per_cell[0]); + cell_it->get_dof_indices(local_dof_indices); + dof_info[no].read_dof_indices (local_dof_indices, + lexicographic_inv[no][0], + *constraint[no], counter, + constraint_values, + cell_at_boundary); + } + // ok, now we are requested to use a level in + // a MGDoFHandler + else if (dof_handlers.active_dof_handler == DoFHandlers::multigrid && + dof_handlers.level != numbers::invalid_unsigned_int) + { + const MGDoFHandler * dofh = + dof_handlers.mg_dof_handler[no]; + AssertIndexRange (dof_handlers.level, dofh->get_tria().n_levels()); + typename MGDoFHandler::cell_iterator + cell_it (&dofh->get_tria(), + cell_level_index[counter].first, + cell_level_index[counter].second, + dofh); + local_dof_indices.resize (dof_info[no].dofs_per_cell[0]); + cell_it->get_mg_dof_indices(local_dof_indices); + dof_info[no].read_dof_indices (local_dof_indices, + lexicographic_inv[no][0], + *constraint[no], counter, + constraint_values, + cell_at_boundary); + } + else if (dof_handlers.active_dof_handler == DoFHandlers::hp) + { + const hp::DoFHandler * dofh = + dof_handlers.hp_dof_handler[no]; + typename hp::DoFHandler::active_cell_iterator + cell_it (&dofh->get_tria(), + cell_level_index[counter].first, + cell_level_index[counter].second, + dofh); + dof_info[no].cell_active_fe_index[counter] = + cell_it->active_fe_index(); + local_dof_indices.resize (cell_it->get_fe().dofs_per_cell); + cell_it->get_dof_indices(local_dof_indices); + dof_info[no].read_dof_indices (local_dof_indices, + lexicographic_inv[no][cell_it->active_fe_index()], + *constraint[no], counter, + constraint_values, + cell_at_boundary); + } + else + { + Assert (false, ExcNotImplemented()); + } + } + + // if we found dofs on some FE component that + // belong to other processors, the cell is + // added to the boundary cells. + if (cell_at_boundary == true) + boundary_cells.push_back(counter); + } + + // try to make the number of boundary cells + // divisible by the number of vectors in + // vectorization + const unsigned int n_vectors = VectorizedArray::n_array_elements; + { + unsigned int n_max_boundary_cells = boundary_cells.size(); + unsigned int n_boundary_cells = n_max_boundary_cells; + + /* + // try to balance the number of cells before + // and after the boundary part on each + // processor. probably not worth it! +#if DEAL_II_COMPILER_SUPPORTS_MPI + MPI_Allreduce (&n_boundary_cells, &n_max_boundary_cells, 1, MPI_UNSIGNED, + MPI_MAX, size_info.communicator); +#endif + if (n_max_boundary_cells > n_active_cells) + n_max_boundary_cells = n_active_cells; + */ + + unsigned int fillup_needed = + (n_vectors - n_boundary_cells%n_vectors)%n_vectors; + /* + if (task_info.use_multithreading == true) + fillup_needed = + (n_vectors - n_boundary_cells%n_vectors)%n_vectors; + else + fillup_needed = (n_max_boundary_cells + + (n_vectors - n_max_boundary_cells%n_vectors)%n_vectors - + n_boundary_cells); + */ + if (fillup_needed > 0 && n_boundary_cells < n_active_cells) + { + // fill additional cells into the list of + // boundary cells to get a balanced number. Go + // through the indices successively until we + // found enough indices + std::vector new_boundary_cells; + new_boundary_cells.reserve (n_max_boundary_cells); + + unsigned int next_free_slot = 0, bound_index = 0; + while (fillup_needed > 0 && bound_index < boundary_cells.size()) + { + if (next_free_slot < boundary_cells[bound_index]) + { + // check if there are enough cells to fill + // with in the current slot + if (next_free_slot + fillup_needed <= boundary_cells[bound_index]) + { + for (unsigned int j=boundary_cells[bound_index]-fillup_needed; + j < boundary_cells[bound_index]; ++j) + new_boundary_cells.push_back(j); + fillup_needed = 0; + } + // ok, not enough indices, so just take them + // all up to the next boundary cell + else + { + for (unsigned int j=next_free_slot; + j 0 && (new_boundary_cells.size()==0 || + new_boundary_cells.back() irregular_cells; + size_info.make_layout (n_active_cells, n_boundary_cells, n_vectors, + irregular_cells); + + for (unsigned int no=0; no renumbering (n_active_cells, + numbers::invalid_unsigned_int); + { + std::vector reverse_numbering (n_active_cells, + numbers::invalid_unsigned_int); + unsigned int counter; + if (task_info.use_multithreading == true) + { + for (unsigned int j=0; j &cell_active_fe_index = + dof_info[0].cell_active_fe_index; + std::vector > renumbering_fe_index; + renumbering_fe_index.resize(max_fe_index); + unsigned int counter,n_macro_cells_before = 0; + const unsigned int + start_bound = std::min (size_info.n_active_cells, + size_info.boundary_cells_start*n_vectors), + end_bound = std::min (size_info.n_active_cells, + size_info.boundary_cells_end*n_vectors); + for(counter=0; counter sorted_renumbering (renumbering); + std::sort (sorted_renumbering.begin(), sorted_renumbering.end()); + for (unsigned int i=0; i > + cell_level_index_old; + cell_level_index.swap (cell_level_index_old); + cell_level_index.reserve(size_info.n_macro_cells*n_vectors); + unsigned int position_cell=0; + for (unsigned int i=0; i0)? + irregular_cells[i] : n_vectors; + for (unsigned int j=0; j +void MatrixFree::clear() +{ + dof_info.clear(); + mapping_info.clear(); + cell_level_index.clear(); + size_info.clear(); + task_info.clear(); + indices_are_initialized = false; + mapping_is_initialized = false; +} + + + +template +std::size_t MatrixFree::memory_consumption () const +{ + std::size_t memory = MemoryConsumption::memory_consumption (dof_info); + memory += MemoryConsumption::memory_consumption (cell_level_index); + memory += MemoryConsumption::memory_consumption (fe_evaluation_data); + memory += MemoryConsumption::memory_consumption (constraint_pool); + memory += MemoryConsumption::memory_consumption (task_info); + memory += sizeof(this); + memory += mapping_info.memory_consumption(); + return memory; +} + + +template +template +void MatrixFree::print_memory_consumption (STREAM &out) const +{ + out << " Memory cell FE operator total: --> "; + size_info.print_mem (out, memory_consumption()); + out << " Memory cell index: "; + size_info.print_mem (out, MemoryConsumption::memory_consumption (cell_level_index)); + for (unsigned int j=0; j +void MatrixFree::print (std::ostream &out) const +{ + // print indices local to global + for (unsigned int no=0; no > (const FullMatrix MATRIX_FUNCTIONS(SparseMatrix); MATRIX_FUNCTIONS(SparseMatrix); +MATRIX_FUNCTIONS(FullMatrix); +MATRIX_FUNCTIONS(FullMatrix); MATRIX_VECTOR_FUNCTIONS(SparseMatrix, Vector); BLOCK_MATRIX_FUNCTIONS(BlockSparseMatrix); diff --git a/deal.II/source/numerics/matrix_free.cc b/deal.II/source/numerics/matrix_free.cc new file mode 100644 index 0000000000..efe7684e04 --- /dev/null +++ b/deal.II/source/numerics/matrix_free.cc @@ -0,0 +1,24 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2011 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 + +DEAL_II_NAMESPACE_OPEN + +#include "matrix_free.inst" + +DEAL_II_NAMESPACE_CLOSE diff --git a/deal.II/source/numerics/matrix_free.inst.in b/deal.II/source/numerics/matrix_free.inst.in new file mode 100644 index 0000000000..913f3220b7 --- /dev/null +++ b/deal.II/source/numerics/matrix_free.inst.in @@ -0,0 +1,62 @@ +//--------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 2010 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) +{ + template class MatrixFree; + template class MatrixFree; + + // reinit for DoFHandler + template void MatrixFree::reinit + (const Mapping &, + const std::vector*> &, + const std::vector &, + const std::vector &, + const std::vector > &, + const MatrixFree::AdditionalData); + template void MatrixFree::reinit + (const Mapping &, + const std::vector*> &, + const std::vector &, + const std::vector &, + const std::vector > &, + const MatrixFree::AdditionalData); + + + // reinit for MGDoFHandler + template void MatrixFree::reinit + (const Mapping &, + const std::vector*> &, + const std::vector &, + const std::vector &, + const std::vector > &, + const MatrixFree::AdditionalData); + template void MatrixFree::reinit + (const Mapping &, + const std::vector*> &, + const std::vector &, + const std::vector &, + const std::vector > &, + const MatrixFree::AdditionalData); + + template void MatrixFree:: + print_memory_consumption (std::ostream &) const; + template void MatrixFree:: + print_memory_consumption (ConditionalOStream &) const; + + template void MatrixFree:: + print_memory_consumption (std::ostream &) const; + template void MatrixFree:: + print_memory_consumption (ConditionalOStream &) const; +} + diff --git a/tests/Makefile b/tests/Makefile index cfef9f8e63..cc3a80ad51 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -15,6 +15,7 @@ nofail-dirs = a-framework \ multigrid \ bits \ hp \ + matrix_free \ aniso \ codim_one \ serialization \ diff --git a/tests/base/vectorization_01.cc b/tests/base/vectorization_01.cc new file mode 100644 index 0000000000..bb7b877e8f --- /dev/null +++ b/tests/base/vectorization_01.cc @@ -0,0 +1,97 @@ +//----------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 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. +// +//----------------------------------------------------------------------------- + +// test for AlignedVector which tests the basic stuff in the +// aligned vector + +#include "../tests.h" +#include +#include +#include + +#include + + +void test () +{ + typedef AlignedVector VEC; + VEC a(4); + deallog << "Constructor: "; + for (unsigned int i=0; i > + +#include "../tests.h" +#include +#include +#include + +#include + + +void test () +{ + typedef VectorizedArray vector_t; + const unsigned int n_vectors = VectorizedArray::n_array_elements; + typedef AlignedVector VEC; + std::vector a_ref(4), b_ref; + VEC a(4); + deallog << "Constructor: "; + for (unsigned int i=0; i > + +#include "../tests.h" +#include +#include +#include + +#include + + +typedef AlignedVector VEC; +typedef AlignedVector VECVEC; +void print_vec (VECVEC &v) +{ + for (unsigned int i=0; i VEC; + VEC a(4); + a[0] = 2; + a[1] = 1; + a[2] = 42; + VECVEC v (2); + deallog << "Constructor: "; + print_vec(v); + + v[0] = a; + v[1] = a; + + deallog << "Assignment: "; + print_vec(v); + + VECVEC w (v); + deallog << "Assignment vector: "; + print_vec(w); + deallog << "Data consistency after assignment: "; + print_vec(v); + + a[1] = 41; + a.push_back (100); + v.push_back(a); + deallog << "Insertion: "; + print_vec (v); + + v.resize(1); + deallog << "Shrinking: "; + print_vec (v); + + v.reserve(100); + deallog << "Reserve: "; + print_vec (v); + + v.resize (10); + deallog << "Resize: "; + print_vec (v); + + v.resize(0); + deallog << "Clear: "; + print_vec (v); +} + + + + +int main() +{ + std::ofstream logfile("vectorization_03/output"); + deallog.attach(logfile); + deallog.depth_console(0); + deallog.threshold_double(1.e-10); + + test (); +} diff --git a/tests/base/vectorization_03/cmp/generic b/tests/base/vectorization_03/cmp/generic new file mode 100644 index 0000000000..5563762ef9 --- /dev/null +++ b/tests/base/vectorization_03/cmp/generic @@ -0,0 +1,10 @@ + +DEAL::Constructor: [][] +DEAL::Assignment: [2 1 42 0 ][2 1 42 0 ] +DEAL::Assignment vector: [2 1 42 0 ][2 1 42 0 ] +DEAL::Data consistency after assignment: [2 1 42 0 ][2 1 42 0 ] +DEAL::Insertion: [2 1 42 0 ][2 1 42 0 ][2 41 42 0 100 ] +DEAL::Shrinking: [2 1 42 0 ] +DEAL::Reserve: [2 1 42 0 ] +DEAL::Resize: [2 1 42 0 ][][][][][][][][][] +DEAL::Clear: diff --git a/tests/base/vectorization_04.cc b/tests/base/vectorization_04.cc new file mode 100644 index 0000000000..1c2fed0fd9 --- /dev/null +++ b/tests/base/vectorization_04.cc @@ -0,0 +1,167 @@ +//----------------------------------------------------------------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 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. +// +//----------------------------------------------------------------------------- + +// test for arithmetic operations on VectorizedArray + +#include "../tests.h" +#include +#include + +#include + + +template +void test () +{ + // since the number of array elements is system dependent, it is not a good + // idea to print them to an output file. Instead, check the values manually + VectorizedArray a, b, c; + const unsigned int n_vectors = VectorizedArray::n_array_elements; + a = Number(2.); + b = Number(-1.); + for (unsigned int i=0; i d = a + b; + for (unsigned int i=0; i e = d - b; + for (unsigned int i=0; i::epsilon(), + ExcInternalError()); + + deallog << "OK" << std::endl + << "Absolute value: "; + d = -c; + d = std::abs(d); + for (unsigned int i=0; i::epsilon(), + ExcInternalError()); + deallog << "OK" << std::endl + << "Cosine: "; + e = std::cos(c); + for (unsigned int i=0; i::epsilon(), + ExcInternalError()); + deallog << "OK" << std::endl + << "Tangent: "; + d = std::tan(e); + for (unsigned int i=0; i::epsilon(), + ExcInternalError()); + deallog << "OK" << std::endl + << "Exponential: "; + d = std::exp(c-a); + for (unsigned int i=0; i::epsilon(), + ExcInternalError()); + deallog << "OK" << std::endl + << "Logarithm: "; + e = std::log(d); + for (unsigned int i=0; i::epsilon(), + ExcInternalError()); + deallog << "OK" << std::endl; +} + + + + +int main() +{ + std::ofstream logfile("vectorization_04/output"); + deallog.attach(logfile); + deallog.depth_console(0); + deallog.threshold_double(1.e-10); + + deallog.push("double"); + test (); + deallog.pop(); + deallog.push("float"); + test (); + deallog.pop(); + + // test long double: in that case, the default + // path of VectorizedArray is taken no matter + // what was done for double or float + deallog.push("long double"); + test (); + deallog.pop(); +} diff --git a/tests/base/vectorization_04/cmp/generic b/tests/base/vectorization_04/cmp/generic new file mode 100644 index 0000000000..c11369e533 --- /dev/null +++ b/tests/base/vectorization_04/cmp/generic @@ -0,0 +1,55 @@ + +DEAL:double::Addition: OK +DEAL:double::Subtraction: OK +DEAL:double::Multiplication: OK +DEAL:double::Division: OK +DEAL:double::Multiplication scalar: OK +DEAL:double::Division scalar left: OK +DEAL:double::Division scalar right: OK +DEAL:double::Unary operator -: OK +DEAL:double::Unary operator +: OK +DEAL:double::Square root: OK +DEAL:double::Absolute value: OK +DEAL:double::Maximum value: OK +DEAL:double::Minimum value: OK +DEAL:double::Sine: OK +DEAL:double::Cosine: OK +DEAL:double::Tangent: OK +DEAL:double::Exponential: OK +DEAL:double::Logarithm: OK +DEAL:float::Addition: OK +DEAL:float::Subtraction: OK +DEAL:float::Multiplication: OK +DEAL:float::Division: OK +DEAL:float::Multiplication scalar: OK +DEAL:float::Division scalar left: OK +DEAL:float::Division scalar right: OK +DEAL:float::Unary operator -: OK +DEAL:float::Unary operator +: OK +DEAL:float::Square root: OK +DEAL:float::Absolute value: OK +DEAL:float::Maximum value: OK +DEAL:float::Minimum value: OK +DEAL:float::Sine: OK +DEAL:float::Cosine: OK +DEAL:float::Tangent: OK +DEAL:float::Exponential: OK +DEAL:float::Logarithm: OK +DEAL:long double::Addition: OK +DEAL:long double::Subtraction: OK +DEAL:long double::Multiplication: OK +DEAL:long double::Division: OK +DEAL:long double::Multiplication scalar: OK +DEAL:long double::Division scalar left: OK +DEAL:long double::Division scalar right: OK +DEAL:long double::Unary operator -: OK +DEAL:long double::Unary operator +: OK +DEAL:long double::Square root: OK +DEAL:long double::Absolute value: OK +DEAL:long double::Maximum value: OK +DEAL:long double::Minimum value: OK +DEAL:long double::Sine: OK +DEAL:long double::Cosine: OK +DEAL:long double::Tangent: OK +DEAL:long double::Exponential: OK +DEAL:long double::Logarithm: OK diff --git a/tests/deal.II/constraints_local_to_global.cc b/tests/deal.II/constraints_local_to_global.cc new file mode 100644 index 0000000000..a267c15fff --- /dev/null +++ b/tests/deal.II/constraints_local_to_global.cc @@ -0,0 +1,119 @@ +//------------------ constraints_local_to_global.cc ------------------------ +// $Id$ +// Version: $Name$ +// +// Copyright (C) 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. +// +//------------------ constraints_local_to_global.cc ------------------------ + + +// this function tests the correctness of the implementation of +// ConstraintMatrix::distribute_local_to_global for FullMatrix by comparing +// the results with a sparse matrix. As a test case, we use a square mesh that +// is refined once globally and then the first cell is refined adaptively. + +#include "../tests.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +std::ofstream logfile("constraints_local_to_global/output"); + +template +void test () +{ + Triangulation tria; + GridGenerator::hyper_cube (tria); + tria.begin()->face(0)->set_boundary_indicator(1); + tria.refine_global(1); + tria.begin_active()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + + FE_Q fe (1); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints (dof, constraints); + VectorTools::interpolate_boundary_values (dof, 1, ZeroFunction(), + constraints); + constraints.close(); + + SparsityPattern sparsity; + { + CompressedSimpleSparsityPattern csp (dof.n_dofs(), dof.n_dofs()); + DoFTools::make_sparsity_pattern (dof, csp, constraints, false); + sparsity.copy_from (csp); + } + SparseMatrix sparse (sparsity); + FullMatrix full (dof.n_dofs(), dof.n_dofs()); + + FullMatrix local_mat (fe.dofs_per_cell, fe.dofs_per_cell); + std::vector local_dof_indices (fe.dofs_per_cell); + + // loop over cells, fill local matrix with + // random values, insert both into sparse and + // full matrix. Make some random entries equal + // to zero + typename DoFHandler::active_cell_iterator + cell = dof.begin_active(), endc = dof.end(); + unsigned int counter = 0; + for ( ; cell != endc; ++cell) + { + for (unsigned int i=0; iget_dof_indices (local_dof_indices); + constraints.distribute_local_to_global (local_mat, local_dof_indices, + sparse); + constraints.distribute_local_to_global (local_mat, local_dof_indices, + full); + } + + // now check that the entries are indeed the + // same by copying the sparse matrix into a + // full matrix and checking the Frobenius norm + // of the difference matrix + FullMatrix ref; + ref.copy_from (sparse); + full.add (-1., ref); + deallog << "Difference between full and sparse matrix: " + << full.frobenius_norm() << std::endl; +} + + +int main () +{ + deallog << std::setprecision (2); + logfile << std::setprecision (2); + deallog.attach(logfile); + deallog.depth_console(0); + deallog.threshold_double(1.e-14); + + test<2>(); +} + diff --git a/tests/deal.II/constraints_local_to_global/cmp/generic b/tests/deal.II/constraints_local_to_global/cmp/generic new file mode 100644 index 0000000000..03a45c21d7 --- /dev/null +++ b/tests/deal.II/constraints_local_to_global/cmp/generic @@ -0,0 +1,2 @@ + +DEAL::Difference between full and sparse matrix: 0 diff --git a/tests/matrix_free/Makefile b/tests/matrix_free/Makefile new file mode 100644 index 0000000000..6476e92010 --- /dev/null +++ b/tests/matrix_free/Makefile @@ -0,0 +1,10 @@ +############################################################ +# $Id$ +# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 by the deal.II authors +############################################################ + +include ../Makefile.paths +include $D/common/Make.global_options +include ../Makefile.rules +include Makefile.depend +include Makefile.tests diff --git a/tests/matrix_free/copy.cc b/tests/matrix_free/copy.cc new file mode 100644 index 0000000000..3ed0b82bc7 --- /dev/null +++ b/tests/matrix_free/copy.cc @@ -0,0 +1,112 @@ +//----------------------------- copy.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//----------------------------- copy.cc ------------------------ + + +// this function tests the correctness of the copy operation in the +// matrix-free class on matrix-vector products similar to +// thread_correctness.cc + +#include "../tests.h" +#include + +std::ofstream logfile("copy/output"); + +#include "matrix_vector_common.h" + + +template +void sub_test() +{ + Triangulation tria; + GridGenerator::hyper_cube (tria); + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + for (; cell!=endc; ++cell) + if (cell->center().norm()<1e-8) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (; cell!=endc; ++cell) + if (cell->center().norm()<0.2) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 || fe_degree < 2) + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + deallog << "Testing " << fe.get_name() << std::endl; + + // run test for several different meshes + for (unsigned int i=0; i<8-2*dim; ++i) + { + cell = tria.begin_active (); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (9-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction(), + constraints); + constraints.close(); + + //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + MatrixFree mf_data; + { + const QGauss<1> quad (fe_degree+1); + mf_data.reinit (dof, constraints, quad, + typename MatrixFree::AdditionalData(MPI_COMM_SELF,MatrixFree::AdditionalData::none)); + } + + MatrixFreeTest mf_ref (mf_data); + + Vector in_dist (dof.n_dofs()); + Vector out_ref (in_dist), out_copy (in_dist); + MatrixFree mf_copy; + mf_copy.copy_from (mf_data); + MatrixFreeTest copied (mf_copy); + + for (unsigned int i=0; i +void test () +{ + deallog << "Test doubles" << std::endl; + sub_test(); + deallog.threshold_double(1.e-6); + deallog << "Test floats" << std::endl; + sub_test(); +} diff --git a/tests/matrix_free/copy/cmp/generic b/tests/matrix_free/copy/cmp/generic new file mode 100644 index 0000000000..c3571bf17a --- /dev/null +++ b/tests/matrix_free/copy/cmp/generic @@ -0,0 +1,49 @@ + +DEAL:2d::Test doubles +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Error in copied MF: 0 +DEAL:2d::Error in copied MF: 0 +DEAL:2d::Error in copied MF: 0 +DEAL:2d::Error in copied MF: 0 +DEAL:2d:: +DEAL:2d::Test floats +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Error in copied MF: 0 +DEAL:2d::Error in copied MF: 0 +DEAL:2d::Error in copied MF: 0 +DEAL:2d::Error in copied MF: 0 +DEAL:2d:: +DEAL:2d::Test doubles +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Error in copied MF: 0 +DEAL:2d::Error in copied MF: 0 +DEAL:2d::Error in copied MF: 0 +DEAL:2d::Error in copied MF: 0 +DEAL:2d:: +DEAL:2d::Test floats +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Error in copied MF: 0 +DEAL:2d::Error in copied MF: 0 +DEAL:2d::Error in copied MF: 0 +DEAL:2d::Error in copied MF: 0 +DEAL:2d:: +DEAL:3d::Test doubles +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Error in copied MF: 0 +DEAL:3d::Error in copied MF: 0 +DEAL:3d:: +DEAL:3d::Test floats +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Error in copied MF: 0 +DEAL:3d::Error in copied MF: 0 +DEAL:3d:: +DEAL:3d::Test doubles +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Error in copied MF: 0 +DEAL:3d::Error in copied MF: 0 +DEAL:3d:: +DEAL:3d::Test floats +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Error in copied MF: 0 +DEAL:3d::Error in copied MF: 0 +DEAL:3d:: diff --git a/tests/matrix_free/create_mesh.h b/tests/matrix_free/create_mesh.h new file mode 100644 index 0000000000..dd38631993 --- /dev/null +++ b/tests/matrix_free/create_mesh.h @@ -0,0 +1,134 @@ +//----------------------- create_mesh.h ----------------------------- +// $Id$ +// Version: $Name$ +// +//----------------------- create_mesh.h ----------------------------- + + +// this creates a mesh that contains cells of all different kinds detected in +// the MatrixFree class: Use a mesh that consists of a square cell, then a +// triangular cell, a parallelepiped cell and two trapezoidal cells, where one +// is very close to being Cartesian (by 1e-8). + +#include +#include + +#include +#include + + +void create_mesh (Triangulation<2> &tria, + const double scale_grid = 1.) +{ + const unsigned int dim = 2; + std::vector > points (12); + + // build the mesh layer by layer from points + + // 1. cube cell + points[0] = Point (0, 0); + points[1] = Point (0, 1); + points[2] = Point (1 ,0); + points[3] = Point (1 ,1); + + // 2. rectangular cell + points[4] = Point (3., 0); + points[5] = Point (3., 1); + + // 3. parallelogram cell + points[6] = Point (5., 1.); + points[7] = Point (5., 2.); + + // almost square cell (but trapezoidal by + // 1e-8) + points[8] = Point (6., 1.); + points[9] = Point (6., 2.+1e-8); + + // apparently trapezoidal cell + points[10] = Point (7., 1.4); + points[11] = Point (7.5, numbers::PI); + + if (scale_grid != 1.) + for (unsigned int i=0; i > cells(5); + for (unsigned int i=0; i<5; ++i) + { + cells[i].vertices[0] = 0+2*i; + cells[i].vertices[1] = 2+2*i; + cells[i].vertices[2] = 1+2*i; + cells[i].vertices[3] = 3+2*i; + cells[i].material_id = 0; + } + + tria.create_triangulation (points, cells, SubCellData()); +} + + + +void create_mesh (Triangulation<3> &tria, + const double scale_grid = 1.) +{ + const unsigned int dim = 3; + std::vector > points (24); + + // build the mesh layer by layer from points + + // 1. cube cell + points[0] = Point (0,0,0); + points[1] = Point (0,1.,0); + points[2] = Point (0,0,1); + points[3] = Point (0,1.,1); + points[4] = Point (1.,0,0); + points[5] = Point (1.,1.,0); + points[6] = Point (1.,0,1); + points[7] = Point (1.,1.,1); + + // 2. rectangular cell + points[8] = Point (3., 0, 0); + points[9] = Point (3., 1, 0); + points[10] = Point (3., 0,1); + points[11] = Point (3., 1,1); + + // 3. parallelogram cell + points[12] = Point (5., 1., 1.); + points[13] = Point (5., 2., 1.); + points[14] = Point (5., 1., 2.); + points[15] = Point (5., 2., 2.); + + // almost square cell (but trapezoidal by + // 1e-8 in y-direction) + points[16] = Point (6., 1., 1.); + points[17] = Point (6., 2.+1e-8, 1.); + points[18] = Point (6., 1., 2.); + points[19] = Point (6., 2., 2.); + + // apparently trapezoidal cell + points[20] = Point (7., 1.4, 1.2231); + points[21] = Point (7.5, numbers::PI, 1.334); + points[22] = Point (7., 1.5, 7.1); + points[23] = Point (7.5, 3.8, 2.99); + + if (scale_grid != 1.) + for (unsigned int i=0; i > cells(5); + for (unsigned int i=0; i<5; ++i) + { + cells[i].vertices[0] = 0+4*i; + cells[i].vertices[1] = 4+4*i; + cells[i].vertices[2] = 1+4*i; + cells[i].vertices[3] = 5+4*i; + cells[i].vertices[4] = 2+4*i; + cells[i].vertices[5] = 6+4*i; + cells[i].vertices[6] = 3+4*i; + cells[i].vertices[7] = 7+4*i; + cells[i].material_id = 0; + } + tria.create_triangulation (points, cells, SubCellData()); +} diff --git a/tests/matrix_free/get_functions_cartesian.cc b/tests/matrix_free/get_functions_cartesian.cc new file mode 100644 index 0000000000..4a33c893e0 --- /dev/null +++ b/tests/matrix_free/get_functions_cartesian.cc @@ -0,0 +1,37 @@ +//------------------ get_functions_cartesian.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_functions_cartesian.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in getting the function values, the function gradients, and the +// function Laplacians on a cartesian mesh (hyper cube). This tests whether +// cartesian meshes are treated correctly. The test case is without any +// constraints + +#include "../tests.h" + + +std::ofstream logfile("get_functions_cartesian/output"); + +#include "get_functions_common.h" + + +template +void test () +{ + Triangulation tria; + GridGenerator::hyper_cube (tria); + tria.refine_global(1); + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + ConstraintMatrix constraints; + constraints.close(); + do_test (dof, constraints); +} + diff --git a/tests/matrix_free/get_functions_cartesian/cmp/generic b/tests/matrix_free/get_functions_cartesian/cmp/generic new file mode 100644 index 0000000000..402522d204 --- /dev/null +++ b/tests/matrix_free/get_functions_cartesian/cmp/generic @@ -0,0 +1,43 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(3) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(4) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: diff --git a/tests/matrix_free/get_functions_circle.cc b/tests/matrix_free/get_functions_circle.cc new file mode 100644 index 0000000000..28de48739a --- /dev/null +++ b/tests/matrix_free/get_functions_circle.cc @@ -0,0 +1,44 @@ +//------------------ get_functions_circle.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_functions_circle.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in getting the function values, the function gradients, and the +// function Laplacians on a hyperball mesh. This tests whether general Q1 +// transformations work correctly. The test case includes hanging node +// constraints, but no constraints from boundary conditions + +#include "../tests.h" + +std::ofstream logfile("get_functions_circle/output"); + +#include "get_functions_common.h" + + +template +void test () +{ + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + // refine first and last cell + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.refine_global (4-dim); + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints (dof, constraints); + constraints.close(); + + do_test (dof, constraints); +} + diff --git a/tests/matrix_free/get_functions_circle/cmp/generic b/tests/matrix_free/get_functions_circle/cmp/generic new file mode 100644 index 0000000000..402522d204 --- /dev/null +++ b/tests/matrix_free/get_functions_circle/cmp/generic @@ -0,0 +1,43 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(3) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(4) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: diff --git a/tests/matrix_free/get_functions_common.h b/tests/matrix_free/get_functions_common.h new file mode 100644 index 0000000000..77e9157727 --- /dev/null +++ b/tests/matrix_free/get_functions_common.h @@ -0,0 +1,278 @@ +//------------------ get_functions_common.h ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_functions_common.h ------------------------ + + +// this is a template for getting the function values and comparing them with +// the output of FEValues on different kinds of meshes (Cartesian, general, +// with and without hanging nodes). The tests does not include multithreading +// because FEValues is not thread-safe + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +// forward declare this function. will be implemented in .cc files +template +void test (); + + + +template +class MatrixFreeTest +{ + public: + static const std::size_t n_vectors = VectorizedArray::n_vectors; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in), + fe_val (data.get_dof_handler().get_fe(), + Quadrature(data.get_quad(0)), + update_values | update_gradients | update_hessians) + {}; + + MatrixFreeTest(const MatrixFree &data_in, + const Mapping &mapping): + data (data_in), + fe_val (mapping, data.get_dof_handler().get_fe(), + Quadrature(data.get_quad(0)), + update_values | update_gradients | update_hessians) + {}; + + virtual ~MatrixFreeTest () + {} + + // make function virtual to allow derived + // classes to define a different function + virtual void + operator () (const MatrixFree &data, + Vector &, + const Vector &src, + const std::pair &cell_range) const + { + typedef VectorizedArray vector_t; + FEEvaluation fe_eval (data); + + std::vector reference_values (fe_eval.n_q_points); + std::vector > reference_grads (fe_eval.n_q_points); + std::vector > reference_hess (fe_eval.n_q_points); + + for(unsigned int cell=cell_range.first;cell &src) const + { + for (unsigned int i=0; i<5; ++i) + { + errors[i] = 0; + total[i] = 0; + } + Vector dst_dummy; + data.cell_loop (&MatrixFreeTest::operator(), this, dst_dummy, src); + + // for doubles, use a stricter condition than + // for floats for the relative error size + if (types_are_equal::value == true) + { + deallog.threshold_double (5e-14); + deallog << "Error function values: " + << errors[0]/total[0] << std::endl; + deallog << "Error function gradients: " + << errors[1]/total[1] << std::endl; + + // need to set quite a loose tolerance because + // FEValues approximates Hessians with finite + // differences, which are not so + // accurate. moreover, Hessians are quite + // large since we chose random numbers. for + // some elements, it might also be zero + // (linear elements on quadrilaterals), so + // need to check for division by 0, too. + deallog.threshold_double (5e-7); + const double output2 = total[2] == 0 ? 0. : errors[2] / total[2]; + deallog << "Error function Laplacians: " << output2 << std::endl; + const double output3 = total[3] == 0 ? 0. : errors[3] / total[3]; + deallog << "Error function diagonal of Hessian: " << output3 << std::endl; + const double output4 = total[4] == 0 ? 0. : errors[4] / total[4]; + deallog << "Error function Hessians: " << output4 << std::endl; + } + else if (types_are_equal::value == true) + { + deallog.threshold_double (1e-6); + deallog << "Error function values: " + << errors[0]/total[0] << std::endl; + deallog << "Error function gradients: " + << errors[1]/total[1] << std::endl; + const double output2 = total[2] == 0 ? 0. : errors[2] / total[2]; + deallog.threshold_double (1e-5); + deallog << "Error function Laplacians: " << output2 << std::endl; + const double output3 = total[3] == 0 ? 0. : errors[3] / total[3]; + deallog << "Error function diagonal of Hessian: " << output3 << std::endl; + const double output4 = total[4] == 0 ? 0. : errors[4] / total[4]; + deallog << "Error function Hessians: " << output4 << std::endl; + } + deallog << std::endl; + }; + +protected: + const MatrixFree &data; + mutable FEValues fe_val; + mutable double errors[5], total[5]; +}; + + + +// dummy with empty quadrature formula +template +class MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_vectors; + + MatrixFreeTest(const MatrixFree &) + {}; + + MatrixFreeTest(const MatrixFree &, + const Mapping &) + {}; + + void cell_integration (Vector &, + const Vector &, + const std::pair) const {} + + void test_functions (const Vector &) const + {} +}; + + + + +template +void do_test (const DoFHandler &dof, + const ConstraintMatrix&constraints) +{ + deallog << "Testing " << dof.get_fe().get_name() << std::endl; + // use this for info on problem + //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() + // << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + Vector solution (dof.n_dofs()); + + // create vector with random entries + for (unsigned int i=0; i mf_data; + { + const QGauss<1> quad (fe_degree+1); + typename MatrixFree::AdditionalData data; + data.tasks_parallel_scheme = MatrixFree::AdditionalData::none; + data.mapping_update_flags = update_gradients | update_second_derivatives; + mf_data.reinit (dof, constraints, quad, data); + } + + MatrixFreeTest mf (mf_data); + mf.test_functions(solution); +} + + +int main () +{ + deallog.attach(logfile); + deallog.depth_console(0); + + deallog << std::setprecision (3); + { + deallog.push("2d"); + test<2,1>(); + test<2,2>(); + test<2,3>(); + test<2,4>(); + deallog.pop(); + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/get_functions_constraints.cc b/tests/matrix_free/get_functions_constraints.cc new file mode 100644 index 0000000000..83b397dd42 --- /dev/null +++ b/tests/matrix_free/get_functions_constraints.cc @@ -0,0 +1,42 @@ +//------------------ get_functions_constraints.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_functions_constraints.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in getting the function values, the function gradients, and the +// function Laplacians on a hypecube mesh with adaptive refinement. + +#include "../tests.h" + +std::ofstream logfile("get_functions_constraints/output"); + +#include "get_functions_common.h" + + +template +void test () +{ + Triangulation tria; + GridGenerator::hyper_cube (tria); + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + constraints.close(); + + do_test (dof, constraints); +} + diff --git a/tests/matrix_free/get_functions_constraints/cmp/generic b/tests/matrix_free/get_functions_constraints/cmp/generic new file mode 100644 index 0000000000..402522d204 --- /dev/null +++ b/tests/matrix_free/get_functions_constraints/cmp/generic @@ -0,0 +1,43 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(3) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(4) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: diff --git a/tests/matrix_free/get_functions_float.cc b/tests/matrix_free/get_functions_float.cc new file mode 100644 index 0000000000..f5d2a336ba --- /dev/null +++ b/tests/matrix_free/get_functions_float.cc @@ -0,0 +1,54 @@ +//------------------ get_functions_float.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_functions_float.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in getting the function values, the function gradients, and the +// function Laplacians on a mesh with all different cell types (same as in +// matrix_vector_06) with hanging nodes, boundary conditions for float values. + +#include "../tests.h" +#include +#include "create_mesh.h" + +std::ofstream logfile("get_functions_float/output"); + +#include "get_functions_common.h" + + +template +void test () +{ + Triangulation tria; + create_mesh (tria); + tria.refine_global(4-dim); + + // refine a few cells + for (unsigned int i=0; i<10-3*dim; ++i) + { + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints (dof, constraints); + VectorTools::interpolate_boundary_values (dof, 1, ZeroFunction(), + constraints); + constraints.close(); + + do_test (dof, constraints); +} + diff --git a/tests/matrix_free/get_functions_float/cmp/generic b/tests/matrix_free/get_functions_float/cmp/generic new file mode 100644 index 0000000000..402522d204 --- /dev/null +++ b/tests/matrix_free/get_functions_float/cmp/generic @@ -0,0 +1,43 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(3) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(4) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: diff --git a/tests/matrix_free/get_functions_gl.cc b/tests/matrix_free/get_functions_gl.cc new file mode 100644 index 0000000000..42d5b0a649 --- /dev/null +++ b/tests/matrix_free/get_functions_gl.cc @@ -0,0 +1,131 @@ +//------------------ get_functions_gl.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_functions_gl.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in getting the function values, the function gradients, and the +// function Laplacians on a hyperball mesh for Gauss-Lobatto elements +// (identity values transformation). The test case includes hanging node +// constraints, but no constraints from boundary conditions + +#include "../tests.h" + + +std::ofstream logfile("get_functions_gl/output"); + +#include "get_functions_common.h" + + +template +class MatrixFreeTestGL : public MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTestGL(const MatrixFree &data, + const Mapping &mapping): + MatrixFreeTest(data, mapping) + {}; + + void operator() (const MatrixFree &data, + Vector &, + const Vector &src, + const std::pair &cell_range) const + { + FEEvaluationGL fe_eval (this->data); + for(unsigned int cell=cell_range.first;cell reference_values (fe_eval.n_q_points); + std::vector > reference_grads (fe_eval.n_q_points); + std::vector > reference_hess (fe_eval.n_q_points); + fe_eval.read_dof_values(src); + fe_eval.evaluate (true,true,true); + + // compare values with the ones the FEValues + // gives us. Those are seen as reference + for (unsigned int j=0; jfe_val.reinit (data.get_cell_iterator(cell,j)); + this->fe_val.get_function_values(src, reference_values); + this->fe_val.get_function_gradients(src, reference_grads); + this->fe_val.get_function_hessians(src, reference_hess); + + for (int q=0; q<(int)fe_eval.n_q_points; q++) + { + this->errors[0] += std::fabs(fe_eval.get_value(q)[j]- + reference_values[q]); + for (unsigned int d=0; derrors[1] += std::fabs(fe_eval.get_gradient(q)[d][j]- + reference_grads[q][d]); + this->errors[2] += std::fabs(fe_eval.get_laplacian(q)[j]- + trace(reference_hess[q])); + this->total[0] += std::fabs(reference_values[q]); + for (unsigned int d=0; dtotal[1] += std::fabs(reference_grads[q][d]); + this->total[2] += std::fabs(fe_eval.get_laplacian(q)[j]); + } + } + } + } +}; + + + +template +void test () +{ + typedef double number; + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + // refine first and last cell + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.refine_global (4-dim); + + FE_Q fe (QGaussLobatto<1>(fe_degree+1)); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints (dof, constraints); + constraints.close(); + + deallog << "Testing " << dof.get_fe().get_name() << std::endl; + //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() + // << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + Vector solution (dof.n_dofs()); + + // create vector with random entries + for (unsigned int i=0; i mf_data; + deallog << "Test with fe_degree " << fe_degree + << std::endl; + const QGaussLobatto<1> quad (fe_degree+1); + MappingQ mapping (2); + typename MatrixFree::AdditionalData data; + data.tasks_parallel_scheme = MatrixFree::AdditionalData::none; + data.mapping_update_flags = update_gradients | update_second_derivatives; + mf_data.reinit (mapping, dof, constraints, quad, data); + MatrixFreeTestGL mf (mf_data, mapping); + mf.test_functions (solution); +} + diff --git a/tests/matrix_free/get_functions_gl/cmp/generic b/tests/matrix_free/get_functions_gl/cmp/generic new file mode 100644 index 0000000000..8cde2d2232 --- /dev/null +++ b/tests/matrix_free/get_functions_gl/cmp/generic @@ -0,0 +1,49 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Test with fe_degree 1 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Test with fe_degree 2 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(QGaussLobatto(4)) +DEAL:2d::Test with fe_degree 3 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(QGaussLobatto(5)) +DEAL:2d::Test with fe_degree 4 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Test with fe_degree 1 +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Test with fe_degree 2 +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: diff --git a/tests/matrix_free/get_functions_mappingq.cc b/tests/matrix_free/get_functions_mappingq.cc new file mode 100644 index 0000000000..5b4358e6ae --- /dev/null +++ b/tests/matrix_free/get_functions_mappingq.cc @@ -0,0 +1,78 @@ +//------------------ get_functions_circle_mappingq.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_functions_circle_mappingq.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in getting the function values, the function gradients, and the +// function Laplacians on a hyperball mesh. This tests whether the general +// MappingQ with higher order works correctly. The test case includes hanging +// node constraints, but no constraints from boundary conditions + +#include "../tests.h" + +std::ofstream logfile("get_functions_mappingq/output"); + +#include "get_functions_common.h" + + +template +void test () +{ + typedef double number; + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + // refine first and last cell + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.refine_global (4-dim); + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints (dof, constraints); + constraints.close(); + + // in the other functions, use do_test in + // get_functions_common, but here we have to + // manually choose another mapping + deallog << "Testing " << dof.get_fe().get_name() << std::endl; + //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() + // << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + Vector solution (dof.n_dofs()); + + // create vector with random entries + for (unsigned int i=0; i&>(solution)); + MappingQ mapping (4); + MatrixFree mf_data; + { + const QGauss<1> quad (fe_degree+1); + typename MatrixFree::AdditionalData data; + data.tasks_parallel_scheme = MatrixFree::AdditionalData::none; + data.mapping_update_flags = update_gradients | update_second_derivatives; + mf_data.reinit (mapping, dof, constraints, quad, data); + } + + MatrixFreeTest mf (mf_data, mapping); + mf.test_functions(solution); + deallog << std::endl; +} + diff --git a/tests/matrix_free/get_functions_mappingq/cmp/generic b/tests/matrix_free/get_functions_mappingq/cmp/generic new file mode 100644 index 0000000000..a5d906ac1e --- /dev/null +++ b/tests/matrix_free/get_functions_mappingq/cmp/generic @@ -0,0 +1,49 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(3) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(4) +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: +DEAL:3d:: diff --git a/tests/matrix_free/get_functions_multife.cc b/tests/matrix_free/get_functions_multife.cc new file mode 100644 index 0000000000..3d019d2eae --- /dev/null +++ b/tests/matrix_free/get_functions_multife.cc @@ -0,0 +1,291 @@ +//------------------ get_functions_multife.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_functions_multife.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in getting the function values, the function gradients, and the +// function Laplacians on a hypecube mesh with adaptive refinement with +// components in two different DoFHandlers. + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +std::ofstream logfile("get_functions_multife/output"); + + +template +class MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + typedef std::vector > VectorType; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in), + fe_val0 (data.get_dof_handler(0).get_fe(), + Quadrature(data.get_quad(0)), + update_values | update_gradients | update_hessians), + fe_val1 (data.get_dof_handler(1).get_fe(), + Quadrature(data.get_quad(1)), + update_values | update_gradients | update_hessians) + {}; + + void + operator () (const MatrixFree &data, + VectorType &, + const VectorType &src, + const std::pair &cell_range) const + { + FEEvaluation fe_eval0 (data,0,0); + FEEvaluation fe_eval1 (data,1,1); + std::vector reference_values0 (fe_eval0.n_q_points); + std::vector > reference_grads0 (fe_eval0.n_q_points); + std::vector > reference_hess0 (fe_eval0.n_q_points); + std::vector reference_values1 (fe_eval1.n_q_points); + std::vector > reference_grads1 (fe_eval1.n_q_points); + std::vector > reference_hess1 (fe_eval1.n_q_points); + for(unsigned int cell=cell_range.first;cell::operator(), + this, dst_dummy, src); + + // for doubles, use a stricter condition then + // for floats for the relative error size + for (unsigned int i=0; i<2; ++i) + { + if (types_are_equal::value == true) + { + deallog.threshold_double (4e-14); + deallog << "Error function values FE " << i << ": " + << errors[i*3+0]/total[i*3+0] << std::endl; + deallog << "Error function gradients FE " << i << ": " + << errors[i*3+1]/total[i*3+1] << std::endl; + + // need to set quite a loose tolerance because + // FEValues approximates Hessians with finite + // differences, which are not so + // accurate. moreover, Hessians are quite + // large since we chose random numbers. for + // some elements, it might also be zero + // (linear elements on quadrilaterals), so + // need to check for division by 0, too. + deallog.threshold_double (5e-7); + const double output2 = total[i*3+2] == 0 ? 0. : errors[i*3+2] / total[i*3+2]; + deallog << "Error function Laplacians FE " << i << ": " << output2 << std::endl; + } + else if (types_are_equal::value == true) + { + deallog.threshold_double (1e-6); + deallog << "Error function values FE " << i << ": " + << errors[i*3+0]/total[i*3+0] << std::endl; + deallog << "Error function gradients FE " << i << ": " + << errors[i*3+1]/total[i*3+1] << std::endl; + const double output2 = total[i*3+2] == 0 ? 0. : errors[i*3+2] / total[i*3+2]; + deallog.threshold_double (1e-6); + deallog << "Error function Laplacians FE " << i << ": " << output2 << std::endl; + } + } + }; + +private: + const MatrixFree &data; + mutable FEValues fe_val0; + mutable FEValues fe_val1; + mutable double errors[6], total[6]; +}; + + + +template +void test () +{ + typedef double number; + Triangulation tria; + GridGenerator::hyper_cube (tria); + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.begin_active(tria.n_levels()-2)->set_refine_flag(); + tria.begin_active(tria.n_levels()-3)->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + + FE_Q fe0(fe_degree); + FE_Q fe1(fe_degree+1); + DoFHandler dof0(tria); + dof0.distribute_dofs(fe0); + DoFHandler dof1(tria); + dof1.distribute_dofs(fe1); + + std::vector *>dof(2); + dof[0] = &dof0; + dof[1] = &dof1; + + deallog << "Testing " << fe0.get_name() << " and " << fe1.get_name() << std::endl; + //std::cout << "Number of cells: " << tria.n_active_cells() << std::endl; + + std::vector > src(dof.size()); + for (unsigned int no=0; non_dofs()); + + std::vector constraints(2); + ConstraintMatrix constraint0; + DoFTools::make_hanging_node_constraints(*dof[0],constraint0); + constraint0.close(); + constraints[0] = &constraint0; + ConstraintMatrix constraint1; + DoFTools::make_hanging_node_constraints(*dof[1],constraint1); + constraint1.close(); + constraints[1] = &constraint1; + + //std::cout << "Number of degrees of freedom FE 0: " << dof[0]->n_dofs() << std::endl; + //std::cout << "Number of constraints FE 0: " << constraints[0]->n_constraints() << std::endl; + //std::cout << "Number of degrees of freedom FE 1: " << dof[1]->n_dofs() << std::endl; + //std::cout << "Number of constraints FE 1: " << constraints[1]->n_constraints() << std::endl; + + // create vector with random entries + for (unsigned int no=0; no<2; ++no) + for (unsigned int i=0; in_dofs(); ++i) + { + if(constraints[no]->is_constrained(i)) + continue; + const double entry = rand()/(double)RAND_MAX; + src[no](i) = entry; + } + + + constraints[0]->distribute(src[0]); + constraints[1]->distribute(src[1]); + MatrixFree mf_data; + { + std::vector > quad; + for (unsigned int no=0; no<2; ++no) + quad.push_back(QGauss<1>(fe_degree+1+no)); + mf_data.reinit (dof, constraints, quad, + typename MatrixFree::AdditionalData + (MPI_COMM_SELF, + MatrixFree::AdditionalData::none)); + } + + MatrixFreeTest mf (mf_data); + mf.test_functions(src); + deallog << std::endl; +} + + +int main () +{ + deallog.attach(logfile); + deallog.depth_console(0); + // need to set quite a loose tolerance because + // FEValues approximates Hessians with finite + // differences, which are not so accurate + deallog.threshold_double(2.e-5); + deallog << std::setprecision (3); + + { + deallog.push("2d"); + test<2,1>(); + test<2,2>(); + test<2,3>(); + test<2,4>(); + deallog.pop(); + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + test<3,3>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/get_functions_multife/cmp/generic b/tests/matrix_free/get_functions_multife/cmp/generic new file mode 100644 index 0000000000..e205a840fd --- /dev/null +++ b/tests/matrix_free/get_functions_multife/cmp/generic @@ -0,0 +1,57 @@ + +DEAL:2d::Testing FE_Q<2>(1) and FE_Q<2>(2) +DEAL:2d::Error function values FE 0: 0 +DEAL:2d::Error function gradients FE 0: 0 +DEAL:2d::Error function Laplacians FE 0: 0 +DEAL:2d::Error function values FE 1: 0 +DEAL:2d::Error function gradients FE 1: 0 +DEAL:2d::Error function Laplacians FE 1: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) and FE_Q<2>(3) +DEAL:2d::Error function values FE 0: 0 +DEAL:2d::Error function gradients FE 0: 0 +DEAL:2d::Error function Laplacians FE 0: 0 +DEAL:2d::Error function values FE 1: 0 +DEAL:2d::Error function gradients FE 1: 0 +DEAL:2d::Error function Laplacians FE 1: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(3) and FE_Q<2>(4) +DEAL:2d::Error function values FE 0: 0 +DEAL:2d::Error function gradients FE 0: 0 +DEAL:2d::Error function Laplacians FE 0: 0 +DEAL:2d::Error function values FE 1: 0 +DEAL:2d::Error function gradients FE 1: 0 +DEAL:2d::Error function Laplacians FE 1: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(4) and FE_Q<2>(5) +DEAL:2d::Error function values FE 0: 0 +DEAL:2d::Error function gradients FE 0: 0 +DEAL:2d::Error function Laplacians FE 0: 0 +DEAL:2d::Error function values FE 1: 0 +DEAL:2d::Error function gradients FE 1: 0 +DEAL:2d::Error function Laplacians FE 1: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) and FE_Q<3>(2) +DEAL:3d::Error function values FE 0: 0 +DEAL:3d::Error function gradients FE 0: 0 +DEAL:3d::Error function Laplacians FE 0: 0 +DEAL:3d::Error function values FE 1: 0 +DEAL:3d::Error function gradients FE 1: 0 +DEAL:3d::Error function Laplacians FE 1: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) and FE_Q<3>(3) +DEAL:3d::Error function values FE 0: 0 +DEAL:3d::Error function gradients FE 0: 0 +DEAL:3d::Error function Laplacians FE 0: 0 +DEAL:3d::Error function values FE 1: 0 +DEAL:3d::Error function gradients FE 1: 0 +DEAL:3d::Error function Laplacians FE 1: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(3) and FE_Q<3>(4) +DEAL:3d::Error function values FE 0: 0 +DEAL:3d::Error function gradients FE 0: 0 +DEAL:3d::Error function Laplacians FE 0: 0 +DEAL:3d::Error function values FE 1: 0 +DEAL:3d::Error function gradients FE 1: 0 +DEAL:3d::Error function Laplacians FE 1: 0 +DEAL:3d:: diff --git a/tests/matrix_free/get_functions_multife2.cc b/tests/matrix_free/get_functions_multife2.cc new file mode 100644 index 0000000000..c500271072 --- /dev/null +++ b/tests/matrix_free/get_functions_multife2.cc @@ -0,0 +1,343 @@ +//------------------ get_functions_multife2.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_functions_multife2.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in getting the function values, the function gradients, and the +// function Laplacians on a hypecube mesh with adaptive refinement with +// components in three different DoFHandlers and two quadrature formulas, one +// of which is a FE_DGQ(0) + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +std::ofstream logfile("get_functions_multife2/output"); + + +template +class MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + typedef std::vector > VectorType; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in), + fe_val0 (data.get_dof_handler(0).get_fe(), + Quadrature(data.get_quad(0)), + update_values | update_gradients | update_hessians), + fe_val1 (data.get_dof_handler(1).get_fe(), + Quadrature(data.get_quad(1)), + update_values | update_gradients | update_hessians), + fe_val2 (data.get_dof_handler(2).get_fe(), + Quadrature(data.get_quad(1)), + update_values | update_gradients | update_hessians) + {}; + + void + operator () (const MatrixFree &data, + VectorType &, + const VectorType &src, + const std::pair &cell_range) const + { + FEEvaluation fe_eval0 (data,0,0); + FEEvaluation fe_eval1 (data,1,1); + FEEvaluation fe_eval2 (data,2,1); + std::vector reference_values0 (fe_eval0.n_q_points); + std::vector > reference_grads0 (fe_eval0.n_q_points); + std::vector > reference_hess0 (fe_eval0.n_q_points); + std::vector reference_values1 (fe_eval1.n_q_points); + std::vector > reference_grads1 (fe_eval1.n_q_points); + std::vector > reference_hess1 (fe_eval1.n_q_points); + std::vector reference_values2 (fe_eval2.n_q_points); + std::vector > reference_grads2 (fe_eval2.n_q_points); + std::vector > reference_hess2 (fe_eval2.n_q_points); + for(unsigned int cell=cell_range.first;cell::operator(), + this, dst_dummy, src); + + // avoid dividing by zero + for (unsigned int i=0; i<9; ++i) + if (std::fabs(total[i]) < 1e-20) + total[i] = 1; + + // for doubles, use a stricter condition then + // for floats for the relative error size + for (unsigned int i=0; i<3; ++i) + { + if (types_are_equal::value == true) + { + deallog.threshold_double (4e-14); + deallog << "Error function values FE " << i << ": " + << errors[i*3+0]/total[i*3+0] << std::endl; + deallog << "Error function gradients FE " << i << ": " + << errors[i*3+1]/total[i*3+1] << std::endl; + + // need to set quite a loose tolerance because + // FEValues approximates Hessians with finite + // differences, which are not so + // accurate. moreover, Hessians are quite + // large since we chose random numbers. for + // some elements, it might also be zero + // (linear elements on quadrilaterals), so + // need to check for division by 0, too. + deallog.threshold_double (2e-6); + const double output2 = total[i*3+2] == 0 ? 0. : errors[i*3+2] / total[i*3+2]; + deallog << "Error function Laplacians FE " << i << ": " << output2 << std::endl; + } + else if (types_are_equal::value == true) + { + deallog.threshold_double (1e-6); + deallog << "Error function values FE " << i << ": " + << errors[i*3+0]/total[i*3+0] << std::endl; + deallog << "Error function gradients FE " << i << ": " + << errors[i*3+1]/total[i*3+1] << std::endl; + const double output2 = total[i*3+2] == 0 ? 0. : errors[i*3+2] / total[i*3+2]; + deallog.threshold_double (1e-6); + deallog << "Error function Laplacians FE " << i << ": " << output2 << std::endl; + } + } + }; + +private: + const MatrixFree &data; + mutable FEValues fe_val0; + mutable FEValues fe_val1; + mutable FEValues fe_val2; + mutable double errors[9], total[9]; +}; + + + +template +void test () +{ + typedef double number; + Triangulation tria; + GridGenerator::hyper_cube (tria); + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.begin_active(tria.n_levels()-2)->set_refine_flag(); + tria.begin_active(tria.n_levels()-3)->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + + FE_DGQ fe0(0); + FE_Q fe1(fe_degree); + FE_Q fe2(fe_degree+1); + DoFHandler dof0(tria); + dof0.distribute_dofs(fe0); + DoFHandler dof1(tria); + dof1.distribute_dofs(fe1); + DoFHandler dof2(tria); + dof2.distribute_dofs(fe2); + + std::vector *>dof(3); + dof[0] = &dof0; + dof[1] = &dof1; + dof[2] = &dof2; + + deallog << "Testing " << fe0.get_name() << ", " << fe1.get_name() + << ", and " << fe1.get_name() << std::endl; + //std::cout << "Number of cells: " << tria.n_active_cells() << std::endl; + + std::vector > src (dof.size()); + for (unsigned int i=0; in_dofs()); + + std::vector constraints(3); + ConstraintMatrix constraint0; + DoFTools::make_hanging_node_constraints(*dof[0],constraint0); + constraint0.close(); + constraints[0] = &constraint0; + ConstraintMatrix constraint1; + DoFTools::make_hanging_node_constraints(*dof[1],constraint1); + constraint1.close(); + constraints[1] = &constraint1; + ConstraintMatrix constraint2; + DoFTools::make_hanging_node_constraints(*dof[2],constraint2); + constraint2.close(); + constraints[2] = &constraint2; + + //std::cout << "Number of degrees of freedom FE 0: " << dof[0]->n_dofs() << std::endl; + //std::cout << "Number of constraints FE 0: " << constraints[0]->n_constraints() << std::endl; + //std::cout << "Number of degrees of freedom FE 1: " << dof[1]->n_dofs() << std::endl; + //std::cout << "Number of constraints FE 1: " << constraints[1]->n_constraints() << std::endl; + //std::cout << "Number of degrees of freedom FE 2: " << dof[2]->n_dofs() << std::endl; + //std::cout << "Number of constraints FE 2: " << constraints[2]->n_constraints() << std::endl; + + // create vector with random entries + for (unsigned int no=0; no<3; ++no) + for (unsigned int i=0; in_dofs(); ++i) + { + if(constraints[no]->is_constrained(i)) + continue; + const double entry = rand()/(double)RAND_MAX; + src[no](i) = entry; + } + + + constraints[0]->distribute(src[0]); + constraints[1]->distribute(src[1]); + constraints[2]->distribute(src[2]); + MatrixFree mf_data; + { + std::vector > quad; + quad.push_back (QGauss<1>(1)); + quad.push_back (QGauss<1>(fe_degree+1)); + mf_data.reinit (dof, constraints, quad, + typename MatrixFree::AdditionalData + (MPI_COMM_SELF, + MatrixFree::AdditionalData::none)); + } + + MatrixFreeTest mf (mf_data); + mf.test_functions(src); + deallog << std::endl; +} + + +int main () +{ + deallog.attach(logfile); + deallog.depth_console(0); + // need to set quite a loose tolerance because + // FEValues approximates Hessians with finite + // differences, which are not so accurate + deallog.threshold_double(2.e-5); + deallog << std::setprecision (3); + + { + deallog.push("2d"); + test<2,1>(); + test<2,2>(); + test<2,3>(); + test<2,4>(); + deallog.pop(); + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + test<3,3>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/get_functions_multife2/cmp/generic b/tests/matrix_free/get_functions_multife2/cmp/generic new file mode 100644 index 0000000000..a9d69d7ddb --- /dev/null +++ b/tests/matrix_free/get_functions_multife2/cmp/generic @@ -0,0 +1,78 @@ + +DEAL:2d::Testing FE_DGQ<2>(0), FE_Q<2>(1), and FE_Q<2>(1) +DEAL:2d::Error function values FE 0: 0 +DEAL:2d::Error function gradients FE 0: 0 +DEAL:2d::Error function Laplacians FE 0: 0 +DEAL:2d::Error function values FE 1: 0 +DEAL:2d::Error function gradients FE 1: 0 +DEAL:2d::Error function Laplacians FE 1: 0 +DEAL:2d::Error function values FE 2: 0 +DEAL:2d::Error function gradients FE 2: 0 +DEAL:2d::Error function Laplacians FE 2: 0 +DEAL:2d:: +DEAL:2d::Testing FE_DGQ<2>(0), FE_Q<2>(2), and FE_Q<2>(2) +DEAL:2d::Error function values FE 0: 0 +DEAL:2d::Error function gradients FE 0: 0 +DEAL:2d::Error function Laplacians FE 0: 0 +DEAL:2d::Error function values FE 1: 0 +DEAL:2d::Error function gradients FE 1: 0 +DEAL:2d::Error function Laplacians FE 1: 0 +DEAL:2d::Error function values FE 2: 0 +DEAL:2d::Error function gradients FE 2: 0 +DEAL:2d::Error function Laplacians FE 2: 0 +DEAL:2d:: +DEAL:2d::Testing FE_DGQ<2>(0), FE_Q<2>(3), and FE_Q<2>(3) +DEAL:2d::Error function values FE 0: 0 +DEAL:2d::Error function gradients FE 0: 0 +DEAL:2d::Error function Laplacians FE 0: 0 +DEAL:2d::Error function values FE 1: 0 +DEAL:2d::Error function gradients FE 1: 0 +DEAL:2d::Error function Laplacians FE 1: 0 +DEAL:2d::Error function values FE 2: 0 +DEAL:2d::Error function gradients FE 2: 0 +DEAL:2d::Error function Laplacians FE 2: 0 +DEAL:2d:: +DEAL:2d::Testing FE_DGQ<2>(0), FE_Q<2>(4), and FE_Q<2>(4) +DEAL:2d::Error function values FE 0: 0 +DEAL:2d::Error function gradients FE 0: 0 +DEAL:2d::Error function Laplacians FE 0: 0 +DEAL:2d::Error function values FE 1: 0 +DEAL:2d::Error function gradients FE 1: 0 +DEAL:2d::Error function Laplacians FE 1: 0 +DEAL:2d::Error function values FE 2: 0 +DEAL:2d::Error function gradients FE 2: 0 +DEAL:2d::Error function Laplacians FE 2: 0 +DEAL:2d:: +DEAL:3d::Testing FE_DGQ<3>(0), FE_Q<3>(1), and FE_Q<3>(1) +DEAL:3d::Error function values FE 0: 0 +DEAL:3d::Error function gradients FE 0: 0 +DEAL:3d::Error function Laplacians FE 0: 0 +DEAL:3d::Error function values FE 1: 0 +DEAL:3d::Error function gradients FE 1: 0 +DEAL:3d::Error function Laplacians FE 1: 0 +DEAL:3d::Error function values FE 2: 0 +DEAL:3d::Error function gradients FE 2: 0 +DEAL:3d::Error function Laplacians FE 2: 0 +DEAL:3d:: +DEAL:3d::Testing FE_DGQ<3>(0), FE_Q<3>(2), and FE_Q<3>(2) +DEAL:3d::Error function values FE 0: 0 +DEAL:3d::Error function gradients FE 0: 0 +DEAL:3d::Error function Laplacians FE 0: 0 +DEAL:3d::Error function values FE 1: 0 +DEAL:3d::Error function gradients FE 1: 0 +DEAL:3d::Error function Laplacians FE 1: 0 +DEAL:3d::Error function values FE 2: 0 +DEAL:3d::Error function gradients FE 2: 0 +DEAL:3d::Error function Laplacians FE 2: 0 +DEAL:3d:: +DEAL:3d::Testing FE_DGQ<3>(0), FE_Q<3>(3), and FE_Q<3>(3) +DEAL:3d::Error function values FE 0: 0 +DEAL:3d::Error function gradients FE 0: 0 +DEAL:3d::Error function Laplacians FE 0: 0 +DEAL:3d::Error function values FE 1: 0 +DEAL:3d::Error function gradients FE 1: 0 +DEAL:3d::Error function Laplacians FE 1: 0 +DEAL:3d::Error function values FE 2: 0 +DEAL:3d::Error function gradients FE 2: 0 +DEAL:3d::Error function Laplacians FE 2: 0 +DEAL:3d:: diff --git a/tests/matrix_free/get_functions_q_hierarchical.cc b/tests/matrix_free/get_functions_q_hierarchical.cc new file mode 100644 index 0000000000..4bff0d5fee --- /dev/null +++ b/tests/matrix_free/get_functions_q_hierarchical.cc @@ -0,0 +1,135 @@ +//------------------ get_functions_q_hierarchical.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_functions_q_hierarchical.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in getting the function values, the function gradients, and the +// function Laplacians on a hyperball mesh for FE_Q_Hierarchical elements +// (no symmetry in transformation). The test case includes hanging node +// constraints, but no constraints from boundary conditions + +#include "../tests.h" +#include +#include +#include + +std::ofstream logfile("get_functions_q_hierarchical/output"); + +#include "get_functions_common.h" + + +template +class MatrixFreeTestGen : public MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTestGen(const MatrixFree &data, + const Mapping &mapping): + MatrixFreeTest(data, mapping) + {}; + + void operator () (const MatrixFree &data, + Vector &, + const Vector &src, + const std::pair &cell_range) const + { + FEEvaluationGeneral fe_eval (data); + for(unsigned int cell=cell_range.first;cell reference_values (fe_eval.n_q_points); + std::vector > reference_grads (fe_eval.n_q_points); + std::vector > reference_hess (fe_eval.n_q_points); + fe_eval.read_dof_values(src); + fe_eval.evaluate (true,true,true); + + // compare values with the ones the FEValues + // gives us. Those are seen as reference + for (unsigned int j=0; jfe_val.reinit (data.get_cell_iterator(cell,j)); + this->fe_val.get_function_values(src, reference_values); + this->fe_val.get_function_gradients(src, reference_grads); + this->fe_val.get_function_hessians(src, reference_hess); + + for (int q=0; q<(int)fe_eval.n_q_points; q++) + { + this->errors[0] += std::fabs(fe_eval.get_value(q)[j]- + reference_values[q]); + for (unsigned int d=0; derrors[1] += std::fabs(fe_eval.get_gradient(q)[d][j]- + reference_grads[q][d]); + } + this->errors[2] += std::fabs(fe_eval.get_laplacian(q)[j]- + trace(reference_hess[q])); + this->total[0] += std::fabs(reference_values[q]); + for (unsigned int d=0; dtotal[1] += std::fabs(reference_grads[q][d]); + this->total[2] += std::fabs(fe_eval.get_laplacian(q)[j]); + } + } + } + } +}; + + + +template +void test () +{ + typedef double number; + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + // refine first and last cell + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.refine_global (4-dim); + + FE_Q_Hierarchical fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints (dof, constraints); + constraints.close(); + + deallog << "Testing " << dof.get_fe().get_name() << std::endl; + //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() + // << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + Vector solution (dof.n_dofs()); + + // create vector with random entries + for (unsigned int i=0; i mf_data; + deallog << "Test with fe_degree " << fe_degree + << std::endl; + const QGauss<1> quad (fe_degree+1); + MappingQ mapping (4); + typename MatrixFree::AdditionalData data; + data.tasks_parallel_scheme = MatrixFree::AdditionalData::none; + data.mapping_update_flags = update_gradients | update_second_derivatives; + mf_data.reinit (mapping, dof, constraints, quad, data); + MatrixFreeTestGen mf (mf_data, mapping); + mf.test_functions (solution); +} + diff --git a/tests/matrix_free/get_functions_q_hierarchical/cmp/generic b/tests/matrix_free/get_functions_q_hierarchical/cmp/generic new file mode 100644 index 0000000000..25a1ff6910 --- /dev/null +++ b/tests/matrix_free/get_functions_q_hierarchical/cmp/generic @@ -0,0 +1,49 @@ + +DEAL:2d::Testing FE_Q_Hierarchical<2>(1) +DEAL:2d::Test with fe_degree 1 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q_Hierarchical<2>(2) +DEAL:2d::Test with fe_degree 2 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q_Hierarchical<2>(3) +DEAL:2d::Test with fe_degree 3 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q_Hierarchical<2>(4) +DEAL:2d::Test with fe_degree 4 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q_Hierarchical<3>(1) +DEAL:3d::Test with fe_degree 1 +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q_Hierarchical<3>(2) +DEAL:3d::Test with fe_degree 2 +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: diff --git a/tests/matrix_free/get_functions_rect.cc b/tests/matrix_free/get_functions_rect.cc new file mode 100644 index 0000000000..45a8ac922f --- /dev/null +++ b/tests/matrix_free/get_functions_rect.cc @@ -0,0 +1,98 @@ +//------------------ get_functions_rect.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_functions_rect.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in getting the function values, the function gradients, and the +// function Laplacians on a hyperball mesh with different sizes in the number +// of degrees of freedom per cell and quadrature points per cell. The test +// case includes hanging node constraints, but no constraints from boundary +// conditions + +#include "../tests.h" + +std::ofstream logfile("get_functions_rect/output"); + +#include "get_functions_common.h" + + +template +void sub_test (const DoFHandler &dof, + const ConstraintMatrix &constraints, + MatrixFree &mf_data, + Vector &solution) +{ + deallog << "Test with fe_degree " << fe_degree + << ", n_q_points_1d: " << (n_q_points_1d) << std::endl; + const QGauss<1> quad (n_q_points_1d); + MappingQ mapping (2); + typename MatrixFree::AdditionalData data; + data.tasks_parallel_scheme = MatrixFree::AdditionalData::none; + data.mapping_update_flags = update_gradients | update_second_derivatives; + mf_data.reinit (mapping, dof, constraints, quad, data); + MatrixFreeTest mf (mf_data, mapping); + mf.test_functions (solution); +} + + + +template +void test () +{ + typedef double number; + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + // refine first and last cell + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.refine_global (4-dim); + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints (dof, constraints); + constraints.close(); + + + // in the other functions, use do_test in + // get_functions_common, but here we have to + // manually choose non-rectangular tests. + deallog << "Testing " << dof.get_fe().get_name() << std::endl; + //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() + // << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + Vector solution (dof.n_dofs()); + + // create vector with random entries + for (unsigned int i=0; i mf_data; + if (fe_degree > 1) + sub_test (dof, constraints, mf_data, + solution); + sub_test (dof, constraints, mf_data, + solution); + sub_test (dof, constraints, mf_data, + solution); + if (dim == 2) + sub_test (dof, constraints, mf_data, + solution); +} + diff --git a/tests/matrix_free/get_functions_rect/cmp/generic b/tests/matrix_free/get_functions_rect/cmp/generic new file mode 100644 index 0000000000..bcea3cad30 --- /dev/null +++ b/tests/matrix_free/get_functions_rect/cmp/generic @@ -0,0 +1,147 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Test with fe_degree 1, n_q_points_1d: 1 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Test with fe_degree 1, n_q_points_1d: 3 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Test with fe_degree 1, n_q_points_1d: 4 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Test with fe_degree 2, n_q_points_1d: 1 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Test with fe_degree 2, n_q_points_1d: 2 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Test with fe_degree 2, n_q_points_1d: 4 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Test with fe_degree 2, n_q_points_1d: 5 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(3) +DEAL:2d::Test with fe_degree 3, n_q_points_1d: 2 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Test with fe_degree 3, n_q_points_1d: 3 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Test with fe_degree 3, n_q_points_1d: 5 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Test with fe_degree 3, n_q_points_1d: 6 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(4) +DEAL:2d::Test with fe_degree 4, n_q_points_1d: 3 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Test with fe_degree 4, n_q_points_1d: 4 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Test with fe_degree 4, n_q_points_1d: 6 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:2d::Test with fe_degree 4, n_q_points_1d: 7 +DEAL:2d::Error function values: 0 +DEAL:2d::Error function gradients: 0 +DEAL:2d::Error function Laplacians: 0 +DEAL:2d::Error function diagonal of Hessian: 0 +DEAL:2d::Error function Hessians: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Test with fe_degree 1, n_q_points_1d: 1 +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: +DEAL:3d::Test with fe_degree 1, n_q_points_1d: 3 +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Test with fe_degree 2, n_q_points_1d: 1 +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: +DEAL:3d::Test with fe_degree 2, n_q_points_1d: 2 +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: +DEAL:3d::Test with fe_degree 2, n_q_points_1d: 4 +DEAL:3d::Error function values: 0 +DEAL:3d::Error function gradients: 0 +DEAL:3d::Error function Laplacians: 0 +DEAL:3d::Error function diagonal of Hessian: 0 +DEAL:3d::Error function Hessians: 0 +DEAL:3d:: diff --git a/tests/matrix_free/get_functions_variants.cc b/tests/matrix_free/get_functions_variants.cc new file mode 100644 index 0000000000..11f248b141 --- /dev/null +++ b/tests/matrix_free/get_functions_variants.cc @@ -0,0 +1,207 @@ +//------------------ get_functions_variants.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_functions_variants.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in getting the function values, the function gradients, and the +// function Laplacians on a cartesian mesh (hyper cube) with the different +// variants (functions only, gradients only, some combinations). The reference +// is computed to the function that extracts all data at the same time (the +// correctness of that must be tested elsewhere). No constraints. + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +std::ofstream logfile("get_functions_variants/output"); + + +template +class MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + typedef Vector VectorType; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in) + {}; + + void operator () (const MatrixFree &data, + VectorType &dst, + const VectorType &src, + const std::pair &cell_range) const; + + void test_functions (const VectorType &src) const + { + for (unsigned int i=0; i<5; ++i) + errors[i] = 0; + data.cell_loop (&MatrixFreeTest::operator(), this, + const_cast(src), src); + + deallog << "Error val, function values alone: " + << errors[0] << std::endl; + deallog << "Error grad, function gradients alone: " + << errors[1] << std::endl; + deallog << "Error val, function values and gradients alone: " + << errors[2] << std::endl; + deallog << "Error grad, function values and gradients alone: " + << errors[3] << std::endl; + deallog << "Error Lapl, function Laplacians alone: " + << errors[4] << std::endl; + }; + +private: + const MatrixFree &data; + mutable double errors [5]; +}; + + + + +template +void MatrixFreeTest:: +operator () (const MatrixFree &data, + VectorType &, + const VectorType &src, + const std::pair &cell_range) const +{ + FEEvaluation fe_eval (data); + FEEvaluation fe_eval2 (data); + FEEvaluation fe_eval3 (data); + FEEvaluation fe_eval4 (data); + FEEvaluation fe_eval5 (data); + for(unsigned int cell=cell_range.first;cell +void test () +{ + Triangulation tria; + GridGenerator::hyper_cube (tria); + tria.refine_global(1); + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + deallog << "Testing " << fe.get_name() << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + + Vector solution_dist (dof.n_dofs()); + + // create vector with random entries + for (unsigned int i=0; i mf_data; + { + const QGauss<1> quad (fe_degree+1); + typename MatrixFree::AdditionalData data; + data.tasks_parallel_scheme = MatrixFree::AdditionalData::none; + data.mapping_update_flags = update_gradients | update_second_derivatives; + mf_data.reinit (dof, constraints, quad, data); + } + + MatrixFreeTest mf (mf_data); + mf.test_functions(solution_dist); + deallog << std::endl; +} + + +int main () +{ + deallog.attach(logfile); + deallog.depth_console(0); + + deallog.threshold_double(1.e-14); + deallog << std::setprecision (3); + + { + deallog.push("2d"); + test<2,1>(); + test<2,2>(); + test<2,3>(); + test<2,4>(); + test<2,5>(); + deallog.pop(); + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + test<3,3>(); + test<3,4>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/get_functions_variants/cmp/generic b/tests/matrix_free/get_functions_variants/cmp/generic new file mode 100644 index 0000000000..9ac254fb9f --- /dev/null +++ b/tests/matrix_free/get_functions_variants/cmp/generic @@ -0,0 +1,64 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Error val, function values alone: 0 +DEAL:2d::Error grad, function gradients alone: 0 +DEAL:2d::Error val, function values and gradients alone: 0 +DEAL:2d::Error grad, function values and gradients alone: 0 +DEAL:2d::Error Lapl, function Laplacians alone: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Error val, function values alone: 0 +DEAL:2d::Error grad, function gradients alone: 0 +DEAL:2d::Error val, function values and gradients alone: 0 +DEAL:2d::Error grad, function values and gradients alone: 0 +DEAL:2d::Error Lapl, function Laplacians alone: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(3) +DEAL:2d::Error val, function values alone: 0 +DEAL:2d::Error grad, function gradients alone: 0 +DEAL:2d::Error val, function values and gradients alone: 0 +DEAL:2d::Error grad, function values and gradients alone: 0 +DEAL:2d::Error Lapl, function Laplacians alone: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(4) +DEAL:2d::Error val, function values alone: 0 +DEAL:2d::Error grad, function gradients alone: 0 +DEAL:2d::Error val, function values and gradients alone: 0 +DEAL:2d::Error grad, function values and gradients alone: 0 +DEAL:2d::Error Lapl, function Laplacians alone: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(5) +DEAL:2d::Error val, function values alone: 0 +DEAL:2d::Error grad, function gradients alone: 0 +DEAL:2d::Error val, function values and gradients alone: 0 +DEAL:2d::Error grad, function values and gradients alone: 0 +DEAL:2d::Error Lapl, function Laplacians alone: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Error val, function values alone: 0 +DEAL:3d::Error grad, function gradients alone: 0 +DEAL:3d::Error val, function values and gradients alone: 0 +DEAL:3d::Error grad, function values and gradients alone: 0 +DEAL:3d::Error Lapl, function Laplacians alone: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Error val, function values alone: 0 +DEAL:3d::Error grad, function gradients alone: 0 +DEAL:3d::Error val, function values and gradients alone: 0 +DEAL:3d::Error grad, function values and gradients alone: 0 +DEAL:3d::Error Lapl, function Laplacians alone: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(3) +DEAL:3d::Error val, function values alone: 0 +DEAL:3d::Error grad, function gradients alone: 0 +DEAL:3d::Error val, function values and gradients alone: 0 +DEAL:3d::Error grad, function values and gradients alone: 0 +DEAL:3d::Error Lapl, function Laplacians alone: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(4) +DEAL:3d::Error val, function values alone: 0 +DEAL:3d::Error grad, function gradients alone: 0 +DEAL:3d::Error val, function values and gradients alone: 0 +DEAL:3d::Error grad, function values and gradients alone: 0 +DEAL:3d::Error Lapl, function Laplacians alone: 0 +DEAL:3d:: diff --git a/tests/matrix_free/get_values_plain.cc b/tests/matrix_free/get_values_plain.cc new file mode 100644 index 0000000000..31ffec46bf --- /dev/null +++ b/tests/matrix_free/get_values_plain.cc @@ -0,0 +1,189 @@ +//------------------ get_values_plain.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ get_values_plain.cc ------------------------ + + +// this tests whether FEEvaluationBase::read_dof_values and +// FEEvaluationBase::read_dof_values_plain get the same data on a vector where +// constraints are distributed + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +std::ofstream logfile("get_values_plain/output"); + + +template +class MatrixFreeTest +{ + public: + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in) + {}; + + // make function virtual to allow derived + // classes to define a different function + virtual void + operator () (const MatrixFree &data, + Vector &, + const Vector &src, + const std::pair &cell_range) const + { + typedef VectorizedArray vector_t; + const unsigned int n_vectors = sizeof(vector_t)/sizeof(Number); + FEEvaluation fe_eval (data); + FEEvaluation fe_eval_plain (data); + for(unsigned int cell=cell_range.first;cell &src) const + { + error = 0; + total = 0; + Vector dst_dummy; + data.cell_loop (&MatrixFreeTest::operator(), + this, dst_dummy, src); + + deallog.threshold_double(1e-10); + deallog << "Error read_dof_values vs read_dof_values_plain: " + << error/total << std::endl << std::endl; + }; + +protected: + const MatrixFree &data; + mutable double error, total; +}; + + + +template +void do_test (const DoFHandler &dof, + const ConstraintMatrix&constraints) +{ + deallog << "Testing " << dof.get_fe().get_name() << std::endl; + //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() + // << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + Vector solution (dof.n_dofs()); + + // create vector with random entries + for (unsigned int i=0; i mf_data; + { + const QGauss<1> quad (fe_degree+1); + typename MatrixFree::AdditionalData data; + data.tasks_parallel_scheme = MatrixFree::AdditionalData::none; + data.store_plain_indices = true; + mf_data.reinit (dof, constraints, quad, data); + } + + MatrixFreeTest mf (mf_data); + mf.test_functions(solution); +} + +template +void test () +{ + Triangulation tria; + GridGenerator::hyper_shell (tria, Point(), 1., 2., 96, true); + static const HyperShellBoundary boundary; + tria.set_boundary (0, boundary); + tria.set_boundary (1, boundary); + + // refine a few cells + for (unsigned int i=0; i<11-3*dim; ++i) + { + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + VectorTools::interpolate_boundary_values (dof, 1, ZeroFunction(), + constraints); + constraints.close(); + + do_test (dof, constraints); +} + + +int main () +{ + deallog.attach(logfile); + deallog.depth_console(0); + + deallog << std::setprecision (3); + { + deallog.push("2d"); + test<2,1>(); + test<2,2>(); + test<2,3>(); + test<2,4>(); + deallog.pop(); + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/get_values_plain/cmp/generic b/tests/matrix_free/get_values_plain/cmp/generic new file mode 100644 index 0000000000..c8bb43b31c --- /dev/null +++ b/tests/matrix_free/get_values_plain/cmp/generic @@ -0,0 +1,19 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Error read_dof_values vs read_dof_values_plain: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Error read_dof_values vs read_dof_values_plain: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(3) +DEAL:2d::Error read_dof_values vs read_dof_values_plain: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(4) +DEAL:2d::Error read_dof_values vs read_dof_values_plain: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Error read_dof_values vs read_dof_values_plain: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Error read_dof_values vs read_dof_values_plain: 0 +DEAL:3d:: diff --git a/tests/matrix_free/integrate_functions.cc b/tests/matrix_free/integrate_functions.cc new file mode 100644 index 0000000000..95faee6e62 --- /dev/null +++ b/tests/matrix_free/integrate_functions.cc @@ -0,0 +1,227 @@ +//------------------ integrate_functions.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ integrate_functions.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in integrating functions and gradients on a hypeball mesh with +// adaptive refinement. + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +std::ofstream logfile("integrate_functions/output"); + + +template +class MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + typedef std::vector*> VectorType; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in), + fe_val (data.get_dof_handler().get_fe(), + Quadrature(data.get_quad(0)), + update_values | update_gradients | update_JxW_values) + {}; + + void operator () (const MatrixFree &data, + VectorType &dst, + const VectorType &src, + const std::pair &cell_range) const; + + void test_functions (Vector &dst, + Vector &dst_deal) const + { + dst = 0; + dst_deal = 0; + VectorType dst_data (2); + dst_data[0] = &dst; + dst_data[1] = &dst_deal; + VectorType src_dummy; + data.cell_loop (&MatrixFreeTest::operator(), this, + dst_data, src_dummy); + }; + +private: + const MatrixFree &data; + mutable FEValues fe_val; +}; + + + + +template +void MatrixFreeTest:: +operator () (const MatrixFree &data, + std::vector*> &dst, + const std::vector*> &, + const std::pair &cell_range) const +{ + FEEvaluation fe_eval (data); + const unsigned int n_q_points = fe_eval.n_q_points; + const unsigned int dofs_per_cell = fe_eval.dofs_per_cell; + AlignedVector values (n_q_points); + AlignedVector gradients (dim*n_q_points); + std::vector dof_indices (dofs_per_cell); + for(unsigned int cell=cell_range.first;cellget_dof_indices(dof_indices); + + for (unsigned int i=0; i submit (false); + for (unsigned int d=0; d +void test () +{ + typedef double number; + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + for (; cell!=endc; ++cell) + if (cell->center().norm()<1e-8) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (; cell!=endc; ++cell) + if (cell->center().norm()<0.2) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 || fe_degree < 2) + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (unsigned int i=0; i<7-2*dim; ++i) + { + cell = tria.begin_active (); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + deallog << "Testing " << fe.get_name() << std::endl; + //std::cout << "Number of cells: " << tria.n_active_cells() << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + constraints.close(); + + MatrixFree mf_data; + { + const QGauss<1> quad (fe_degree+1); + mf_data.reinit (dof, constraints, quad, + typename MatrixFree::AdditionalData(MPI_COMM_SELF,MatrixFree::AdditionalData::none)); + } + + MatrixFreeTest mf (mf_data); + Vector solution (dof.n_dofs()); + Vector solution_dist (dof.n_dofs()); + + mf.test_functions(solution_dist, solution); + + constraints.condense (solution); + + Vector compare (solution_dist); + compare -= solution; + const double diff_norm = compare.linfty_norm(); + + deallog << "Norm of difference: " << diff_norm << std::endl << std::endl; +} + + +int main () +{ + deallog.attach(logfile); + deallog.depth_console(0); + deallog << std::setprecision (3); + + { + deallog.threshold_double(1.e-12); + deallog.push("2d"); + test<2,1>(); + test<2,2>(); + test<2,3>(); + test<2,4>(); + deallog.pop(); + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + test<3,3>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/integrate_functions/cmp/generic b/tests/matrix_free/integrate_functions/cmp/generic new file mode 100644 index 0000000000..3829c8ffdb --- /dev/null +++ b/tests/matrix_free/integrate_functions/cmp/generic @@ -0,0 +1,22 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(3) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(4) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(3) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/integrate_functions_multife.cc b/tests/matrix_free/integrate_functions_multife.cc new file mode 100644 index 0000000000..9b3bcaf00a --- /dev/null +++ b/tests/matrix_free/integrate_functions_multife.cc @@ -0,0 +1,350 @@ +//------------------ integrate_functions_multife.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ integrate_functions_multife.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in integrating functions and gradients on a hypeball mesh with +// adaptive refinement. It uses two different finite elements inside one +// matrix_free structure. + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +std::ofstream logfile("integrate_functions_multife/output"); + + +template +class MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + typedef std::vector > VectorType; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in), + fe_val0 (data.get_dof_handler(0).get_fe(), + Quadrature(data.get_quad(0)), + update_values | update_gradients | update_JxW_values), + fe_val01 (data.get_dof_handler(0).get_fe(), + Quadrature(data.get_quad(1)), + update_values | update_gradients | update_JxW_values), + fe_val1 (data.get_dof_handler(1).get_fe(), + Quadrature(data.get_quad(1)), + update_values | update_gradients | update_JxW_values) + {}; + + void operator () (const MatrixFree &data, + VectorType &dst, + const VectorType &src, + const std::pair &cell_range) const; + + void test_functions (VectorType &dst) const + { + for (unsigned int comp=0; comp::operator(), this, + dst, src_dummy); + }; + +private: + const MatrixFree &data; + mutable FEValues fe_val0; + mutable FEValues fe_val01; + mutable FEValues fe_val1; +}; + + + + +template +void MatrixFreeTest:: +operator () (const MatrixFree &data, + std::vector > &dst, + const std::vector > &, + const std::pair &cell_range) const +{ + FEEvaluation fe_eval0 (data, 0, 0); + FEEvaluation fe_eval1 (data, 1, 1); + FEEvaluation fe_eval01 (data, 0, 1); + const unsigned int n_q_points0 = fe_eval0.n_q_points; + const unsigned int n_q_points1 = fe_eval1.n_q_points; + const unsigned int dofs_per_cell0 = fe_eval0.dofs_per_cell; + const unsigned int dofs_per_cell1 = fe_eval1.dofs_per_cell; + AlignedVector values0 (n_q_points0); + AlignedVector gradients0 (dim*n_q_points0); + AlignedVector values1 (n_q_points1); + AlignedVector gradients1 (dim*n_q_points1); + std::vector dof_indices0 (dofs_per_cell0); + std::vector dof_indices1 (dofs_per_cell1); + for(unsigned int cell=cell_range.first;cellget_dof_indices(dof_indices0); + + for (unsigned int i=0; iget_dof_indices(dof_indices1); + + for (unsigned int q=0; q submit (false); + for (unsigned int d=0; d submit (false); + for (unsigned int d=0; d submit (false); + for (unsigned int d=0; d +void test () +{ + // create hyper ball geometry and refine some + // cells + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + for (; cell!=endc; ++cell) + if (cell->center().norm()<1e-8) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (; cell!=endc; ++cell) + if (cell->center().norm()<0.2) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 || fe_degree < 2) + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (unsigned int i=0; i<7-2*dim; ++i) + { + cell = tria.begin_active (); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe0(fe_degree); + FE_Q fe1(fe_degree+1); + DoFHandler dof0(tria); + dof0.distribute_dofs(fe0); + DoFHandler dof1(tria); + dof1.distribute_dofs(fe1); + + std::vector *>dof(2); + dof[0] = &dof0; + dof[1] = &dof1; + + deallog << "Testing " << fe0.get_name() << " and " << fe1.get_name() << std::endl; + //std::cout << "Number of cells: " << tria.n_active_cells() << std::endl; + + std::vector > dst (6); + dst[0].reinit (dof[0]->n_dofs()); + dst[1].reinit (dst[0]); + dst[2].reinit (dof[1]->n_dofs()); + dst[3].reinit (dst[2]); + dst[4].reinit (dst[0]); + dst[5].reinit (dst[0]); + + std::vector constraints(2); + ConstraintMatrix constraint0; + DoFTools::make_hanging_node_constraints(*dof[0],constraint0); + constraint0.close(); + constraints[0] = &constraint0; + ConstraintMatrix constraint1; + DoFTools::make_hanging_node_constraints(*dof[1],constraint1); + constraint1.close(); + constraints[1] = &constraint1; + + //std::cout << "Number of degrees of freedom FE 0: " << dof[0]->n_dofs() << std::endl; + //std::cout << "Number of constraints FE 0: " << constraints[0]->n_constraints() << std::endl; + //std::cout << "Number of degrees of freedom FE 1: " << dof[1]->n_dofs() << std::endl; + //std::cout << "Number of constraints FE 1: " << constraints[1]->n_constraints() << std::endl; + + MatrixFree mf_data; + { + std::vector > quad; + for (unsigned int no=0; no<2; ++no) + quad.push_back(QGauss<1>(fe_degree+1+no)); + mf_data.reinit (dof, constraints, quad, + typename MatrixFree::AdditionalData(MPI_COMM_SELF,MatrixFree::AdditionalData::none)); + } + + MatrixFreeTest mf (mf_data); + mf.test_functions(dst); + + constraints[0]->condense(dst[1]); + constraints[1]->condense(dst[3]); + constraints[0]->condense(dst[5]); + + dst[1] -= dst[0]; + double diff_norm = dst[1].linfty_norm(); + deallog << "FE 0, Quad 0; integration difference: " << diff_norm << std::endl; + + dst[3] -= dst[2]; + diff_norm = dst[3].linfty_norm(); + deallog << "FE 1, Quad 1; integration difference: " << diff_norm << std::endl; + + dst[5] -= dst[4]; + diff_norm = dst[5].linfty_norm(); + deallog << "FE 0, Quad 1; integration difference: " << diff_norm << std::endl << std::endl; +} + + +int main () +{ + deallog.attach(logfile); + deallog.depth_console(0); + deallog << std::setprecision (3); + + { + deallog.threshold_double(1.e-12); + deallog.push("2d"); + test<2,1,double>(); + test<2,2,double>(); + test<2,3,double>(); + deallog.pop(); + deallog.push("3d"); + test<3,1,double>(); + test<3,2,double>(); + deallog.pop(); + } + + { + deallog << std::endl << "Test with floats" << std::endl << std::endl; + deallog.threshold_double(1.e-7); + deallog.push("2d"); + test<2,1,float>(); + deallog.pop(); + deallog.push("3d"); + test<3,1,float>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/integrate_functions_multife/cmp/generic b/tests/matrix_free/integrate_functions_multife/cmp/generic new file mode 100644 index 0000000000..3de4bbcf19 --- /dev/null +++ b/tests/matrix_free/integrate_functions_multife/cmp/generic @@ -0,0 +1,39 @@ + +DEAL:2d::Testing FE_Q<2>(1) and FE_Q<2>(2) +DEAL:2d::FE 0, Quad 0; integration difference: 0 +DEAL:2d::FE 1, Quad 1; integration difference: 0 +DEAL:2d::FE 0, Quad 1; integration difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) and FE_Q<2>(3) +DEAL:2d::FE 0, Quad 0; integration difference: 0 +DEAL:2d::FE 1, Quad 1; integration difference: 0 +DEAL:2d::FE 0, Quad 1; integration difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(3) and FE_Q<2>(4) +DEAL:2d::FE 0, Quad 0; integration difference: 0 +DEAL:2d::FE 1, Quad 1; integration difference: 0 +DEAL:2d::FE 0, Quad 1; integration difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) and FE_Q<3>(2) +DEAL:3d::FE 0, Quad 0; integration difference: 0 +DEAL:3d::FE 1, Quad 1; integration difference: 0 +DEAL:3d::FE 0, Quad 1; integration difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) and FE_Q<3>(3) +DEAL:3d::FE 0, Quad 0; integration difference: 0 +DEAL:3d::FE 1, Quad 1; integration difference: 0 +DEAL:3d::FE 0, Quad 1; integration difference: 0 +DEAL:3d:: +DEAL:: +DEAL::Test with floats +DEAL:: +DEAL:2d::Testing FE_Q<2>(1) and FE_Q<2>(2) +DEAL:2d::FE 0, Quad 0; integration difference: 0 +DEAL:2d::FE 1, Quad 1; integration difference: 0 +DEAL:2d::FE 0, Quad 1; integration difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) and FE_Q<3>(2) +DEAL:3d::FE 0, Quad 0; integration difference: 0 +DEAL:3d::FE 1, Quad 1; integration difference: 0 +DEAL:3d::FE 0, Quad 1; integration difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/integrate_functions_multife2.cc b/tests/matrix_free/integrate_functions_multife2.cc new file mode 100644 index 0000000000..ec6e28bb07 --- /dev/null +++ b/tests/matrix_free/integrate_functions_multife2.cc @@ -0,0 +1,350 @@ +//------------------ integrate_functions_multife2.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ integrate_functions_multife2.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// operations in integrating functions and gradients on a hypeball mesh with +// adaptive refinement. Similar to integrate_functions_multife, but uses +// FE_DGQ(0) as first component + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +std::ofstream logfile("integrate_functions_multife2/output"); + + +template +class MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + typedef std::vector > VectorType; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in), + fe_val0 (data.get_dof_handler(0).get_fe(), + Quadrature(data.get_quad(0)), + update_values | update_gradients | update_JxW_values), + fe_val01 (data.get_dof_handler(0).get_fe(), + Quadrature(data.get_quad(1)), + update_values | update_gradients | update_JxW_values), + fe_val1 (data.get_dof_handler(1).get_fe(), + Quadrature(data.get_quad(1)), + update_values | update_gradients | update_JxW_values) + {}; + + void operator () (const MatrixFree &data, + VectorType &dst, + const VectorType &src, + const std::pair &cell_range) const; + + void test_functions (VectorType &dst) const + { + for (unsigned int comp=0; comp::operator(), this, + dst, src_dummy); + }; + +private: + const MatrixFree &data; + mutable FEValues fe_val0; + mutable FEValues fe_val01; + mutable FEValues fe_val1; +}; + + + + +template +void MatrixFreeTest:: +operator () (const MatrixFree &data, + std::vector > &dst, + const std::vector > &, + const std::pair &cell_range) const +{ + FEEvaluation fe_eval0 (data, 0, 0); + FEEvaluation fe_eval1 (data, 1, 1); + FEEvaluation fe_eval01 (data, 0, 1); + const unsigned int n_q_points0 = fe_eval0.n_q_points; + const unsigned int n_q_points1 = fe_eval1.n_q_points; + const unsigned int dofs_per_cell0 = fe_eval0.dofs_per_cell; + const unsigned int dofs_per_cell1 = fe_eval1.dofs_per_cell; + AlignedVector values0 (n_q_points0); + AlignedVector gradients0 (dim*n_q_points0); + AlignedVector values1 (n_q_points1); + AlignedVector gradients1 (dim*n_q_points1); + std::vector dof_indices0 (dofs_per_cell0); + std::vector dof_indices1 (dofs_per_cell1); + for(unsigned int cell=cell_range.first;cellget_dof_indices(dof_indices0); + + for (unsigned int i=0; iget_dof_indices(dof_indices1); + + for (unsigned int q=0; q submit (false); + for (unsigned int d=0; d submit (false); + for (unsigned int d=0; d submit (false); + for (unsigned int d=0; d +void test () +{ + // create hyper ball geometry and refine some + // cells + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + for (; cell!=endc; ++cell) + if (cell->center().norm()<1e-8) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (; cell!=endc; ++cell) + if (cell->center().norm()<0.2) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 || fe_degree < 2) + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (unsigned int i=0; i<7-2*dim; ++i) + { + cell = tria.begin_active (); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_DGQ fe0(0); + FE_Q fe1(fe_degree); + DoFHandler dof0(tria); + dof0.distribute_dofs(fe0); + DoFHandler dof1(tria); + dof1.distribute_dofs(fe1); + + std::vector *>dof(2); + dof[0] = &dof0; + dof[1] = &dof1; + + deallog << "Testing " << fe0.get_name() << " and " << fe1.get_name() << std::endl; + //std::cout << "Number of cells: " << tria.n_active_cells() << std::endl; + + std::vector > dst (6); + dst[0].reinit (dof[0]->n_dofs()); + dst[1].reinit (dst[0]); + dst[2].reinit (dof[1]->n_dofs()); + dst[3].reinit (dst[2]); + dst[4].reinit (dst[0]); + dst[5].reinit (dst[0]); + + std::vector constraints(2); + ConstraintMatrix constraint0; + constraint0.close(); + constraints[0] = &constraint0; + ConstraintMatrix constraint1; + DoFTools::make_hanging_node_constraints(*dof[1],constraint1); + constraint1.close(); + constraints[1] = &constraint1; + + //std::cout << "Number of degrees of freedom FE 0: " << dof[0]->n_dofs() << std::endl; + //std::cout << "Number of constraints FE 0: " << constraints[0]->n_constraints() << std::endl; + //std::cout << "Number of degrees of freedom FE 1: " << dof[1]->n_dofs() << std::endl; + //std::cout << "Number of constraints FE 1: " << constraints[1]->n_constraints() << std::endl; + + MatrixFree mf_data; + { + std::vector > quad; + quad.push_back (QGauss<1>(1)); + quad.push_back (QGauss<1>(fe_degree+1)); + mf_data.reinit (dof, constraints, quad, + typename MatrixFree::AdditionalData(MPI_COMM_SELF,MatrixFree::AdditionalData::none)); + } + + MatrixFreeTest mf (mf_data); + mf.test_functions(dst); + + constraints[0]->condense(dst[1]); + constraints[1]->condense(dst[3]); + constraints[0]->condense(dst[5]); + + dst[1] -= dst[0]; + double diff_norm = dst[1].linfty_norm(); + deallog << "FE 0, Quad 0; integration difference: " << diff_norm << std::endl; + + dst[3] -= dst[2]; + diff_norm = dst[3].linfty_norm(); + deallog << "FE 1, Quad 1; integration difference: " << diff_norm << std::endl; + + dst[5] -= dst[4]; + diff_norm = dst[5].linfty_norm(); + deallog << "FE 0, Quad 1; integration difference: " << diff_norm << std::endl << std::endl; +} + + +int main () +{ + deallog.attach(logfile); + deallog.depth_console(0); + deallog << std::setprecision (3); + + { + deallog.threshold_double(1.e-12); + deallog.push("2d"); + test<2,1,double>(); + test<2,2,double>(); + test<2,3,double>(); + deallog.pop(); + deallog.push("3d"); + test<3,1,double>(); + test<3,2,double>(); + deallog.pop(); + } + + { + deallog << std::endl << "Test with floats" << std::endl << std::endl; + deallog.threshold_double(1.e-7); + deallog.push("2d"); + test<2,1,float>(); + deallog.pop(); + deallog.push("3d"); + test<3,1,float>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/integrate_functions_multife2/cmp/generic b/tests/matrix_free/integrate_functions_multife2/cmp/generic new file mode 100644 index 0000000000..fe3e2882fa --- /dev/null +++ b/tests/matrix_free/integrate_functions_multife2/cmp/generic @@ -0,0 +1,39 @@ + +DEAL:2d::Testing FE_DGQ<2>(0) and FE_Q<2>(1) +DEAL:2d::FE 0, Quad 0; integration difference: 0 +DEAL:2d::FE 1, Quad 1; integration difference: 0 +DEAL:2d::FE 0, Quad 1; integration difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_DGQ<2>(0) and FE_Q<2>(2) +DEAL:2d::FE 0, Quad 0; integration difference: 0 +DEAL:2d::FE 1, Quad 1; integration difference: 0 +DEAL:2d::FE 0, Quad 1; integration difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_DGQ<2>(0) and FE_Q<2>(3) +DEAL:2d::FE 0, Quad 0; integration difference: 0 +DEAL:2d::FE 1, Quad 1; integration difference: 0 +DEAL:2d::FE 0, Quad 1; integration difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_DGQ<3>(0) and FE_Q<3>(1) +DEAL:3d::FE 0, Quad 0; integration difference: 0 +DEAL:3d::FE 1, Quad 1; integration difference: 0 +DEAL:3d::FE 0, Quad 1; integration difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_DGQ<3>(0) and FE_Q<3>(2) +DEAL:3d::FE 0, Quad 0; integration difference: 0 +DEAL:3d::FE 1, Quad 1; integration difference: 0 +DEAL:3d::FE 0, Quad 1; integration difference: 0 +DEAL:3d:: +DEAL:: +DEAL::Test with floats +DEAL:: +DEAL:2d::Testing FE_DGQ<2>(0) and FE_Q<2>(1) +DEAL:2d::FE 0, Quad 0; integration difference: 0 +DEAL:2d::FE 1, Quad 1; integration difference: 0 +DEAL:2d::FE 0, Quad 1; integration difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_DGQ<3>(0) and FE_Q<3>(1) +DEAL:3d::FE 0, Quad 0; integration difference: 0 +DEAL:3d::FE 1, Quad 1; integration difference: 0 +DEAL:3d::FE 0, Quad 1; integration difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_01.cc b/tests/matrix_free/matrix_vector_01.cc new file mode 100644 index 0000000000..6bda45a62e --- /dev/null +++ b/tests/matrix_free/matrix_vector_01.cc @@ -0,0 +1,35 @@ +//------------------ matrix_vector_01.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_01.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// matrix-vector products by comparing with the result of deal.II sparse +// matrix. The mesh uses a hypercube mesh with no hanging nodes and no other +// constraints + +#include "../tests.h" + +std::ofstream logfile("matrix_vector_01/output"); + +#include "matrix_vector_common.h" + + +template +void test () +{ + Triangulation tria; + GridGenerator::hyper_cube (tria); + tria.refine_global(5-dim); + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + constraints.close(); + + do_test (dof, constraints); +} + diff --git a/tests/matrix_free/matrix_vector_01/cmp/generic b/tests/matrix_free/matrix_vector_01/cmp/generic new file mode 100644 index 0000000000..c8d1cd1e9f --- /dev/null +++ b/tests/matrix_free/matrix_vector_01/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_02.cc b/tests/matrix_free/matrix_vector_02.cc new file mode 100644 index 0000000000..758b33c639 --- /dev/null +++ b/tests/matrix_free/matrix_vector_02.cc @@ -0,0 +1,37 @@ +//------------------ matrix_vector_02.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_02.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// matrix-vector products by comparing with the result of deal.II sparse +// matrix. The mesh uses a hypercube mesh with no hanging nodes, but with zero +// Dirichlet conditions. + +#include "../tests.h" +#include + +std::ofstream logfile("matrix_vector_02/output"); + +#include "matrix_vector_common.h" + + +template +void test () +{ + Triangulation tria; + GridGenerator::hyper_cube (tria); + tria.refine_global(5-dim); + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction(), + constraints); + constraints.close(); + + do_test (dof, constraints); +} diff --git a/tests/matrix_free/matrix_vector_02/cmp/generic b/tests/matrix_free/matrix_vector_02/cmp/generic new file mode 100644 index 0000000000..c8d1cd1e9f --- /dev/null +++ b/tests/matrix_free/matrix_vector_02/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_03.cc b/tests/matrix_free/matrix_vector_03.cc new file mode 100644 index 0000000000..030c3bf951 --- /dev/null +++ b/tests/matrix_free/matrix_vector_03.cc @@ -0,0 +1,65 @@ +//------------------ matrix_vector_03.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_03.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// matrix-vector products by comparing with the result of deal.II sparse +// matrix. The mesh uses a hypercube mesh with hanging nodes (created by +// randomly refining some cells) and zero Dirichlet conditions. + +#include "../tests.h" +#include + +std::ofstream logfile("matrix_vector_03/output"); + +#include "matrix_vector_common.h" + +template +void test () +{ + Triangulation tria; + GridGenerator::hyper_cube (tria); + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + for (; cell!=endc; ++cell) + if (cell->center().norm()<1e-8) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (; cell!=endc; ++cell) + if (cell->center().norm()<0.2) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 || fe_degree < 2) + tria.refine_global(2); + else + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (unsigned int i=0; i<10-3*dim; ++i) + { + cell = tria.begin_active (); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction(), + constraints); + constraints.close(); + + do_test (dof, constraints); +} diff --git a/tests/matrix_free/matrix_vector_03/cmp/generic b/tests/matrix_free/matrix_vector_03/cmp/generic new file mode 100644 index 0000000000..c8d1cd1e9f --- /dev/null +++ b/tests/matrix_free/matrix_vector_03/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_04.cc b/tests/matrix_free/matrix_vector_04.cc new file mode 100644 index 0000000000..a8e76a1945 --- /dev/null +++ b/tests/matrix_free/matrix_vector_04.cc @@ -0,0 +1,40 @@ +//------------------ matrix_vector_04.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_04.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// matrix-vector products by comparing with the result of deal.II sparse +// matrix. The mesh uses a hypershell mesh without hanging nodes (only cell +// type: 2) + +#include "../tests.h" + +std::ofstream logfile("matrix_vector_04/output"); + +#include "matrix_vector_common.h" + + +template +void test () +{ + Triangulation tria; + GridGenerator::hyper_shell (tria, Point(), + 0.5, 1., 96, true); + static const HyperShellBoundary boundary; + tria.set_boundary (0, boundary); + tria.set_boundary (1, boundary); + if (dim == 2) + tria.refine_global (2); + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + constraints.close(); + + do_test (dof, constraints); +} diff --git a/tests/matrix_free/matrix_vector_04/cmp/generic b/tests/matrix_free/matrix_vector_04/cmp/generic new file mode 100644 index 0000000000..c8d1cd1e9f --- /dev/null +++ b/tests/matrix_free/matrix_vector_04/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_05.cc b/tests/matrix_free/matrix_vector_05.cc new file mode 100644 index 0000000000..eeed34c4b7 --- /dev/null +++ b/tests/matrix_free/matrix_vector_05.cc @@ -0,0 +1,68 @@ +//------------------ matrix_vector_05.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_05.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// matrix-vector products by comparing with the result of deal.II sparse +// matrix. The mesh uses a parallelogram mesh with hanging nodes (only cell +// type: 1 = linear). + +#include "../tests.h" + +std::ofstream logfile("matrix_vector_05/output"); + +#include "matrix_vector_common.h" + + +template +void test () +{ + if (dim == 3) + return; + Triangulation tria; + Tensor<2,dim> points; + points[0][0] = 0.25; + points[0][1] = 0.123; + points[1][0] = 0.09983712334; + points[1][1] = 0.314159265358979; + GridGenerator::parallelogram (tria, points); + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + for (; cell!=endc; ++cell) + if (cell->center().norm()<1e-8) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (; cell!=endc; ++cell) + if (cell->center().norm()<0.2) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 || fe_degree < 2) + tria.refine_global(2); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (unsigned int i=0; i<5; ++i) + { + cell = tria.begin_active (); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + constraints.close(); + + do_test (dof, constraints); +} diff --git a/tests/matrix_free/matrix_vector_05/cmp/generic b/tests/matrix_free/matrix_vector_05/cmp/generic new file mode 100644 index 0000000000..f2943c08e9 --- /dev/null +++ b/tests/matrix_free/matrix_vector_05/cmp/generic @@ -0,0 +1,7 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: diff --git a/tests/matrix_free/matrix_vector_06.cc b/tests/matrix_free/matrix_vector_06.cc new file mode 100644 index 0000000000..62ae39486f --- /dev/null +++ b/tests/matrix_free/matrix_vector_06.cc @@ -0,0 +1,64 @@ +//------------------ matrix_vector_06.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_06.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// matrix-vector products by comparing with the result of deal.II sparse +// matrix. The mesh uses a mesh consisting of several different cell types +// according to the create_mesh helper function. Quite large mesh +// mesh so that the thread parallelization is actually used + +#include "../tests.h" +#include +#include "create_mesh.h" + +std::ofstream logfile("matrix_vector_06/output"); + +#include "matrix_vector_common.h" + +template +void test () +{ + Triangulation tria; + create_mesh (tria); + tria.begin_active ()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + typename Triangulation::active_cell_iterator cell, endc; + cell = tria.begin_active (); + endc = tria.end(); + for (; cell!=endc; ++cell) + if (cell->center().norm()<0.5) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 || fe_degree < 2) + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.refine_global(1); + cell = tria.begin_active (); + for (unsigned int i=0; i<10-3*dim; ++i) + { + cell = tria.begin_active (); + endc = tria.end(); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction(), + constraints); + constraints.close(); + + do_test (dof, constraints); +} diff --git a/tests/matrix_free/matrix_vector_06/cmp/generic b/tests/matrix_free/matrix_vector_06/cmp/generic new file mode 100644 index 0000000000..c8d1cd1e9f --- /dev/null +++ b/tests/matrix_free/matrix_vector_06/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_07.cc b/tests/matrix_free/matrix_vector_07.cc new file mode 100644 index 0000000000..274fc22eac --- /dev/null +++ b/tests/matrix_free/matrix_vector_07.cc @@ -0,0 +1,78 @@ +//------------------ matrix_vector_06.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_06.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// matrix-vector products by comparing with the result of deal.II sparse +// matrix. The mesh uses a hyperball mesh with hanging nodes generated by +// randomly refining some cells. Same as matrix_vector_06, but uses FE_DGQ +// which do not have any connections because DoFs do not overlap and we do not +// evaluate flux terms (this stresses the partitioning in a different way +// where non-connected DoFs are to be handled) + +#include "../tests.h" +#include +#include + +std::ofstream logfile("matrix_vector_07/output"); + +#include "matrix_vector_common.h" + + +template +void test () +{ + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + for (; cell!=endc; ++cell) + if (cell->center().norm()<1e-8) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (; cell!=endc; ++cell) + if (cell->center().norm()<0.2) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 || fe_degree < 2) + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.refine_global(4-dim); + cell = tria.begin_active (); + for (unsigned int i=0; i<10-3*dim; ++i) + { + cell = tria.begin_active (); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_DGQ fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + // there should not be any hanging nodes or + // boundary conditions for FE_DGQ as there are + // only interior DoFs on the elements, but try + // anyway + DoFTools::make_hanging_node_constraints(dof, constraints); + VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction(), + constraints); + constraints.close(); + + do_test (dof, constraints); + + // test with coloring only as well + do_test (dof, constraints, 2); +} diff --git a/tests/matrix_free/matrix_vector_07/cmp/generic b/tests/matrix_free/matrix_vector_07/cmp/generic new file mode 100644 index 0000000000..88b86ec837 --- /dev/null +++ b/tests/matrix_free/matrix_vector_07/cmp/generic @@ -0,0 +1,29 @@ + +DEAL:2d::Testing FE_DGQ<2>(1) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_DGQ<2>(1) +DEAL:2d::Parallel option: 2 +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_DGQ<2>(2) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_DGQ<2>(2) +DEAL:2d::Parallel option: 2 +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_DGQ<3>(1) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_DGQ<3>(1) +DEAL:3d::Parallel option: 2 +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_DGQ<3>(2) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_DGQ<3>(2) +DEAL:3d::Parallel option: 2 +DEAL:3d::Norm of difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_08.cc b/tests/matrix_free/matrix_vector_08.cc new file mode 100644 index 0000000000..6d0a5782b2 --- /dev/null +++ b/tests/matrix_free/matrix_vector_08.cc @@ -0,0 +1,62 @@ +//------------------ matrix_vector_08.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_08.cc ------------------------ + + +// same test as matrix_vector_06 (quite large mesh, hanging nodes, different +// cell types), but very large domain of size 1e20 to test correctness of +// relative scaling in mapping info + +#include "../tests.h" +#include +#include "create_mesh.h" + +std::ofstream logfile("matrix_vector_08/output"); + +#include "matrix_vector_common.h" + +template +void test () +{ + Triangulation tria; + create_mesh (tria, 1e20); + tria.begin_active ()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + typename Triangulation::active_cell_iterator cell, endc; + cell = tria.begin_active (); + endc = tria.end(); + for (; cell!=endc; ++cell) + if (cell->center().norm()<0.5*1e20) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 || fe_degree < 2) + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.refine_global(1); + cell = tria.begin_active (); + for (unsigned int i=0; i<10-3*dim; ++i) + { + cell = tria.begin_active (); + endc = tria.end(); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction(), + constraints); + constraints.close(); + + do_test (dof, constraints); +} diff --git a/tests/matrix_free/matrix_vector_08/cmp/generic b/tests/matrix_free/matrix_vector_08/cmp/generic new file mode 100644 index 0000000000..c8d1cd1e9f --- /dev/null +++ b/tests/matrix_free/matrix_vector_08/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_09.cc b/tests/matrix_free/matrix_vector_09.cc new file mode 100644 index 0000000000..a0509e8c3c --- /dev/null +++ b/tests/matrix_free/matrix_vector_09.cc @@ -0,0 +1,62 @@ +//------------------ matrix_vector_09.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_09.cc ------------------------ + + +// same test as matrix_vector_06 (quite large mesh, hanging nodes, different +// cell types), but tiny domain of size 1e-20 to test correctness of +// relative scaling in mapping info + +#include "../tests.h" +#include +#include "create_mesh.h" + +std::ofstream logfile("matrix_vector_09/output"); + +#include "matrix_vector_common.h" + +template +void test () +{ + Triangulation tria; + create_mesh (tria, 1e-20); + tria.begin_active ()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + typename Triangulation::active_cell_iterator cell, endc; + cell = tria.begin_active (); + endc = tria.end(); + for (; cell!=endc; ++cell) + if (cell->center().norm()<0.5*1e-20) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 || fe_degree < 2) + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + tria.refine_global(1); + cell = tria.begin_active (); + for (unsigned int i=0; i<10-3*dim; ++i) + { + cell = tria.begin_active (); + endc = tria.end(); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction(), + constraints); + constraints.close(); + + do_test (dof, constraints); +} diff --git a/tests/matrix_free/matrix_vector_09/cmp/generic b/tests/matrix_free/matrix_vector_09/cmp/generic new file mode 100644 index 0000000000..c8d1cd1e9f --- /dev/null +++ b/tests/matrix_free/matrix_vector_09/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_common.h b/tests/matrix_free/matrix_vector_common.h new file mode 100644 index 0000000000..a4e5b05e0b --- /dev/null +++ b/tests/matrix_free/matrix_vector_common.h @@ -0,0 +1,231 @@ +//------------------ matrix_vector_common.h ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_common.h ------------------------ + + +// this is a template for matrix-vector products with the Helmholtz equation +// (zero and first derivatives) on different kinds of meshes (Cartesian, +// general, with and without hanging nodes). It also tests the multithreading +// in case it was enabled + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +// forward declare this function. will be implemented in .cc files +template +void test (); + + + +template +void +helmholtz_operator (const MatrixFree &data, + Vector &dst, + const Vector &src, + const std::pair &cell_range) +{ + FEEvaluation fe_eval (data); + const unsigned int n_q_points = fe_eval.n_q_points; + + for(unsigned int cell=cell_range.first;cell +class MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_vectors; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in) + {}; + + void vmult (Vector &dst, + const Vector &src) const + { + dst = 0; + const std_cxx1x::function &, + Vector &, + const Vector &, + const std::pair&)> + wrap = helmholtz_operator; + data.cell_loop (wrap, dst, src); + }; + +private: + const MatrixFree &data; +}; + + + + + +template +void do_test (const DoFHandler &dof, + const ConstraintMatrix&constraints, + const unsigned int parallel_option = 0) +{ + + deallog << "Testing " << dof.get_fe().get_name() << std::endl; + if (parallel_option > 0) + deallog << "Parallel option: " << parallel_option << std::endl; + //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + MatrixFree mf_data; + { + const QGauss<1> quad (fe_degree+1); + typename MatrixFree::AdditionalData data; + if (parallel_option == 1) + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::partition_color; + else if (parallel_option == 2) + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::color; + else + { + Assert (parallel_option == 0, ExcInternalError()); + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::partition_partition; + } + data.tasks_block_size = 7; + + mf_data.reinit (dof, constraints, quad, data); + } + + MatrixFreeTest mf (mf_data); + Vector in (dof.n_dofs()), out (dof.n_dofs()); + Vector in_dist (dof.n_dofs()); + Vector out_dist (in_dist); + + for (unsigned int i=0; i sparse_matrix (sparsity); + { + QGauss quadrature_formula(fe_degree+1); + + FEValues fe_values (dof.get_fe(), quadrature_formula, + update_values | update_gradients | + update_JxW_values); + + const unsigned int dofs_per_cell = dof.get_fe().dofs_per_cell; + const unsigned int n_q_points = quadrature_formula.size(); + + FullMatrix cell_matrix (dofs_per_cell, dofs_per_cell); + std::vector local_dof_indices (dofs_per_cell); + + typename DoFHandler::active_cell_iterator + cell = dof.begin_active(), + endc = dof.end(); + for (; cell!=endc; ++cell) + { + cell_matrix = 0; + fe_values.reinit (cell); + + for (unsigned int q_point=0; q_pointget_dof_indices(local_dof_indices); + constraints.distribute_local_to_global (cell_matrix, + local_dof_indices, + sparse_matrix); + } + } + + sparse_matrix.vmult (out, in); + out -= out_dist; + const double diff_norm = out.linfty_norm() / out_dist.linfty_norm(); + + deallog << "Norm of difference: " << diff_norm << std::endl << std::endl; +} + + +int main () +{ + deallog.attach(logfile); + deallog.depth_console(0); + + deallog << std::setprecision (3); + + { + deallog.threshold_double(5.e-11); + deallog.push("2d"); + test<2,1>(); + test<2,2>(); + deallog.pop(); + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/matrix_vector_curl.cc b/tests/matrix_free/matrix_vector_curl.cc new file mode 100644 index 0000000000..c90f385562 --- /dev/null +++ b/tests/matrix_free/matrix_vector_curl.cc @@ -0,0 +1,297 @@ +//------------------ matrix_vector_curl.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_curl.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// matrix-vector products by comparing with the result of deal.II sparse +// matrix. The mesh uses a hyperball mesh with hanging nodes for a +// vector-valued problem (curl-curl operator which does not really make a lot +// of sense from a problem point of view, though). + +#include "../tests.h" + +std::ofstream logfile("matrix_vector_curl/output"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +const double global_coefficient = 0.1; + + +template +class MatrixFreeTest +{ + public: + typedef typename DoFHandler::active_cell_iterator CellIterator; + typedef double Number; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in) + {}; + + void + local_apply (const MatrixFree &data, + VectorType &dst, + const VectorType &src, + const std::pair &cell_range) const + { + typedef VectorizedArray vector_t; + FEEvaluation phi (data); + vector_t coeff = make_vectorized_array(global_coefficient); + + for(unsigned int cell=cell_range.first;cell::local_apply, + this, dst, src); + }; + +private: + const MatrixFree &data; +}; + + + +template +void test () +{ + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + tria.refine_global(4-dim); + + // refine a few cells + for (unsigned int i=0; i<10-3*dim; ++i) + { + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + + FE_Q fe_sca (QGaussLobatto<1>(fe_degree+1)); + FESystem fe (fe_sca, dim); + DoFHandler dof_handler_sca (tria); + DoFHandler dof_handler (tria); + + MatrixFree mf_data; + + ConstraintMatrix constraints; + + BlockSparsityPattern sparsity_pattern; + BlockSparseMatrix system_matrix; + + BlockVector solution; + BlockVector system_rhs; + std::vector > vec1, vec2; + + dof_handler.distribute_dofs (fe); + dof_handler_sca.distribute_dofs (fe_sca); + DoFRenumbering::component_wise (dof_handler); + + DoFTools::make_hanging_node_constraints (dof_handler, constraints); + constraints.close (); + + const unsigned int dofs_per_block = dof_handler_sca.n_dofs(); + { + BlockCompressedSimpleSparsityPattern csp (dim,dim); + for (unsigned int d=0; d quadrature_formula(fe_degree+1); + + FEValues fe_values (fe, quadrature_formula, + update_values | + update_JxW_values | + update_gradients); + + const unsigned int dofs_per_cell = fe.dofs_per_cell; + const unsigned int n_q_points = quadrature_formula.size(); + + FullMatrix local_matrix (dofs_per_cell, dofs_per_cell); + + std::vector local_dof_indices (dofs_per_cell); + + const FEValuesExtractors::Vector sc (0); + + const unsigned int curl_dim = dim==2 ? 1 : dim; + std::vector > phi_curl (dofs_per_cell); + + typename DoFHandler::active_cell_iterator + cell = dof_handler.begin_active(), + endc = dof_handler.end(); + for (; cell!=endc; ++cell) + { + fe_values.reinit (cell); + local_matrix = 0; + + for (unsigned int q=0; q phi_grad = fe_values[sc].gradient(k,q); + if (dim == 2) + phi_curl[k][0] = phi_grad[1][0] - phi_grad[0][1]; + else + { + phi_curl[k][0] = phi_grad[2][1] - phi_grad[1][2]; + phi_curl[k][1] = phi_grad[0][2] - phi_grad[2][0]; + phi_curl[k][2] = phi_grad[1][0] - phi_grad[0][1]; + } + } + + for (unsigned int i=0; iget_dof_indices (local_dof_indices); + constraints.distribute_local_to_global (local_matrix, + local_dof_indices, + system_matrix); + } + } + + // first system_rhs with random numbers + for (unsigned int i=0; i quad(fe_degree+1); + mf_data.reinit (dof_handler_sca, constraints, quad, + typename MatrixFree::AdditionalData + (MPI_COMM_WORLD, + MatrixFree::AdditionalData::none)); + } + + system_matrix.vmult (solution, system_rhs); + + typedef std::vector > VectorType; + MatrixFreeTest mf (mf_data); + mf.vmult (vec2, vec1); + + // Verification + double error = 0.; + for (unsigned int i=0; i(); + test<2,2>(); + deallog.pop(); + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/matrix_vector_curl/cmp/generic b/tests/matrix_free/matrix_vector_curl/cmp/generic new file mode 100644 index 0000000000..619c272047 --- /dev/null +++ b/tests/matrix_free/matrix_vector_curl/cmp/generic @@ -0,0 +1,12 @@ + +DEAL:: +DEAL::Test with doubles +DEAL:: +DEAL:2d:: Verification fe degree 1: 0 +DEAL:2d:: +DEAL:2d:: Verification fe degree 2: 0 +DEAL:2d:: +DEAL:3d:: Verification fe degree 1: 0 +DEAL:3d:: +DEAL:3d:: Verification fe degree 2: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_float.cc b/tests/matrix_free/matrix_vector_float.cc new file mode 100644 index 0000000000..f058329404 --- /dev/null +++ b/tests/matrix_free/matrix_vector_float.cc @@ -0,0 +1,63 @@ +//------------------ matrix_vector_float.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_float.cc ------------------------ + + +// Same as matrix_vector_06 for testing correctness of matrix-vector products +// compared with deal.II sparse matrices, but using floats instead of +// doubles. this tests whether everything is alright for longer vector lengths +// as well + +#include "../tests.h" + +std::ofstream logfile("matrix_vector_float/output"); + +#include "create_mesh.h" +#include "matrix_vector_common.h" + + +template +void test () +{ + Triangulation tria; + create_mesh (tria); + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + for (; cell!=endc; ++cell) + if (cell->center().norm()<1e-8) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (; cell!=endc; ++cell) + if (cell->center().norm()<0.2) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 || fe_degree < 2) + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (unsigned int i=0; i<10-3*dim; ++i) + { + cell = tria.begin_active (); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + constraints.close(); + + deallog.threshold_double (5e-6); + do_test (dof, constraints); +} diff --git a/tests/matrix_free/matrix_vector_float/cmp/generic b/tests/matrix_free/matrix_vector_float/cmp/generic new file mode 100644 index 0000000000..c8d1cd1e9f --- /dev/null +++ b/tests/matrix_free/matrix_vector_float/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_hp.cc b/tests/matrix_free/matrix_vector_hp.cc new file mode 100644 index 0000000000..7c048fcecd --- /dev/null +++ b/tests/matrix_free/matrix_vector_hp.cc @@ -0,0 +1,229 @@ +//------------------ matrix_vector_hp.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_hp.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// matrix-vector products by comparing with the result of deal.II sparse +// matrix for hp DoFHandler on a hyperball mesh with hanging nodes and finite +// elements orders distributed randomly. + +#include "../tests.h" + +std::ofstream logfile("matrix_vector_hp/output"); + +#include "matrix_vector_common.h" +#include +#include +#include + + + +template +class MatrixFreeTestHP +{ + public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTestHP(const MatrixFree &data_in): + data (data_in) + {}; + + void local_apply(const MatrixFree &data, + Vector &dst, + const Vector &src, + const std::pair &cell_range) const + { + // ask MatrixFree for cell_range for different orders + std::pair subrange_deg = + data.create_cell_subrange_hp (cell_range, 1); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + subrange_deg = data.create_cell_subrange_hp (cell_range, 2); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + subrange_deg = data.create_cell_subrange_hp (cell_range, 3); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + subrange_deg = data.create_cell_subrange_hp (cell_range, 4); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + subrange_deg = data.create_cell_subrange_hp (cell_range, 5); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + subrange_deg = data.create_cell_subrange_hp (cell_range, 6); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + subrange_deg = data.create_cell_subrange_hp (cell_range, 7); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + } + + void vmult (Vector &dst, + const Vector &src) const + { + dst = 0; + data.cell_loop (&MatrixFreeTestHP::local_apply, this, dst, src); + }; + +private: + const MatrixFree &data; +}; + + + +template +void test () +{ + if (fe_degree > 1) + return; + + typedef double number; + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + tria.refine_global(1); + + // refine a few cells + for (unsigned int i=0; i<11-3*dim; ++i) + { + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + const unsigned int max_degree = 9-2*dim; + + hp::FECollection fe_collection; + hp::QCollection quadrature_collection; + hp::QCollection<1> quadrature_collection_mf; + + for (unsigned int deg=1; deg<=max_degree; ++deg) + { + fe_collection.push_back (FE_Q(QGaussLobatto<1>(deg+1))); + quadrature_collection.push_back (QGauss(deg+1)); + quadrature_collection_mf.push_back (QGauss<1>(deg+1)); + } + + hp::DoFHandler dof(tria); + // set the active FE index in a random order + { + typename hp::DoFHandler::active_cell_iterator + cell = dof.begin_active(), + endc = dof.end(); + for (; cell!=endc; ++cell) + { + const unsigned int fe_index = rand() % max_degree; + cell->set_active_fe_index (fe_index); + } + } + + // setup DoFs + dof.distribute_dofs(fe_collection); + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints (dof, + constraints); + VectorTools::interpolate_boundary_values (dof, + 0, + ZeroFunction(), + constraints); + constraints.close (); + CompressedSimpleSparsityPattern csp (dof.n_dofs(), + dof.n_dofs()); + DoFTools::make_sparsity_pattern (dof, csp, constraints, false); + SparsityPattern sparsity; + sparsity.copy_from (csp); + SparseMatrix system_matrix (sparsity); + + //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + // set up MatrixFree + MatrixFree mf_data; + typename MatrixFree::AdditionalData data; + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::none; + mf_data.reinit (dof, constraints, quadrature_collection_mf, data); + MatrixFreeTestHP mf (mf_data); + + // assemble sparse matrix with (\nabla v, + // \nabla u) + (v, 10 * u) + { + hp::FEValues hp_fe_values (fe_collection, + quadrature_collection, + update_values | update_gradients | + update_JxW_values); + FullMatrix cell_matrix; + std::vector local_dof_indices; + + typename hp::DoFHandler::active_cell_iterator + cell = dof.begin_active(), + endc = dof.end(); + for (; cell!=endc; ++cell) + { + const unsigned int dofs_per_cell = cell->get_fe().dofs_per_cell; + + cell_matrix.reinit (dofs_per_cell, dofs_per_cell); + cell_matrix = 0; + hp_fe_values.reinit (cell); + const FEValues &fe_values = hp_fe_values.get_present_fe_values (); + + for (unsigned int q_point=0; + q_pointget_dof_indices (local_dof_indices); + + constraints.distribute_local_to_global (cell_matrix, + local_dof_indices, + system_matrix); + } + } + + // fill a right hand side vector with random + // numbers in unconstrained degrees of freedom + Vector src (dof.n_dofs()); + Vector result_spmv(src), result_mf (src); + + for (unsigned int i=0; i +#include +#include +#include + + + +template +void test () +{ + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + tria.refine_global(5-dim); + + FE_Q fe (fe_degree); + + // setup DoFs + MGDoFHandler dof(tria); + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + VectorTools::interpolate_boundary_values (dof, + 0, + ZeroFunction(), + constraints); + constraints.close (); + + //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + + // set up MatrixFree + QGauss<1> quad (fe_degree+1); + MatrixFree mf_data; + mf_data.reinit (dof, constraints, quad); + SparsityPattern sparsity; + SparseMatrix system_matrix; + { + CompressedSimpleSparsityPattern csp (dof.n_dofs(), dof.n_dofs()); + DoFTools::make_sparsity_pattern (static_cast&>(dof), + csp, constraints, false); + sparsity.copy_from (csp); + } + system_matrix.reinit (sparsity); + + // setup MG levels + const unsigned int nlevels = tria.n_levels(); + typedef MatrixFree MatrixFreeTestType; + MGLevelObject mg_matrices; + MGLevelObject mg_constraints; + MGLevelObject mg_sparsities; + MGLevelObject > mg_ref_matrices; + mg_matrices.resize (0, nlevels-1); + mg_constraints.resize (0, nlevels-1); + mg_sparsities.resize (0, nlevels-1); + mg_ref_matrices.resize (0, nlevels-1); + + typename FunctionMap::type dirichlet_boundary; + ZeroFunction homogeneous_dirichlet_bc (1); + dirichlet_boundary[0] = &homogeneous_dirichlet_bc; + std::vector > boundary_indices(nlevels); + MGTools::make_boundary_list (dof, dirichlet_boundary, boundary_indices); + for (unsigned int level=0;level::iterator bc_it = boundary_indices[level].begin(); + for ( ; bc_it != boundary_indices[level].end(); ++bc_it) + mg_constraints[level].add_line(*bc_it); + mg_constraints[level].close(); + typename MatrixFree::AdditionalData data; + data.level_mg_handler = level; + mg_matrices[level].reinit(dof, mg_constraints[level], quad, data); + + CompressedSimpleSparsityPattern csp; + csp.reinit (dof.n_dofs(level), dof.n_dofs(level)); + MGTools::make_sparsity_pattern (dof, csp, level); + mg_sparsities[level].copy_from (csp); + mg_ref_matrices[level].reinit (mg_sparsities[level]); + } + + // assemble sparse matrix with (\nabla v, + // \nabla u) + (v, 10 * u) on the actual + // discretization and on all levels + { + QGauss quad (fe_degree+1); + FEValues fe_values (fe, quad, + update_values | update_gradients | + update_JxW_values); + const unsigned int n_quadrature_points = quad.size(); + const unsigned int dofs_per_cell = fe.dofs_per_cell; + FullMatrix cell_matrix (dofs_per_cell, dofs_per_cell); + std::vector local_dof_indices (dofs_per_cell); + + typename DoFHandler::active_cell_iterator + cell = dof.begin_active(), + endc = dof.end(); + for (; cell!=endc; ++cell) + { + cell_matrix = 0; + fe_values.reinit (cell); + + for (unsigned int q_point=0; q_pointget_dof_indices (local_dof_indices); + constraints.distribute_local_to_global (cell_matrix, + local_dof_indices, + system_matrix); + } + + // now to the MG assembly + typename MGDoFHandler::cell_iterator + cellm = dof.begin(), + endcm = dof.end(); + for (; cellm!=endcm; ++cellm) + { + cell_matrix = 0; + fe_values.reinit (cellm); + + for (unsigned int q_point=0; q_pointget_mg_dof_indices (local_dof_indices); + mg_constraints[cellm->level()] + .distribute_local_to_global (cell_matrix, + local_dof_indices, + mg_ref_matrices[cellm->level()]); + } + } + + // fill a right hand side vector with random + // numbers in unconstrained degrees of freedom + Vector src (dof.n_dofs()); + Vector result_spmv(src), result_mf (src); + + for (unsigned int i=0; i mf (mf_data); + mf.vmult (result_mf, src); + + result_mf -= result_spmv; + const double diff_norm = result_mf.linfty_norm(); + deallog << "Norm of difference active: " << diff_norm << std::endl; + + for (unsigned int level=0; level src (dof.n_dofs(level)); + Vector result_spmv(src), result_mf (src); + + for (unsigned int i=0; i mf_lev (mg_matrices[level]); + mf_lev.vmult (result_mf, src); + + result_mf -= result_spmv; + const double diff_norm = result_mf.linfty_norm(); + deallog << "Norm of difference MG level " << level + << ": " << diff_norm << std::endl; + } + deallog << std::endl; +} + diff --git a/tests/matrix_free/matrix_vector_mg/cmp/generic b/tests/matrix_free/matrix_vector_mg/cmp/generic new file mode 100644 index 0000000000..34111bba39 --- /dev/null +++ b/tests/matrix_free/matrix_vector_mg/cmp/generic @@ -0,0 +1,23 @@ + +DEAL:2d::Norm of difference active: 0 +DEAL:2d::Norm of difference MG level 0: 0 +DEAL:2d::Norm of difference MG level 1: 0 +DEAL:2d::Norm of difference MG level 2: 0 +DEAL:2d::Norm of difference MG level 3: 0 +DEAL:2d:: +DEAL:2d::Norm of difference active: 0 +DEAL:2d::Norm of difference MG level 0: 0 +DEAL:2d::Norm of difference MG level 1: 0 +DEAL:2d::Norm of difference MG level 2: 0 +DEAL:2d::Norm of difference MG level 3: 0 +DEAL:2d:: +DEAL:3d::Norm of difference active: 0 +DEAL:3d::Norm of difference MG level 0: 0 +DEAL:3d::Norm of difference MG level 1: 0 +DEAL:3d::Norm of difference MG level 2: 0 +DEAL:3d:: +DEAL:3d::Norm of difference active: 0 +DEAL:3d::Norm of difference MG level 0: 0 +DEAL:3d::Norm of difference MG level 1: 0 +DEAL:3d::Norm of difference MG level 2: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_stokes.cc b/tests/matrix_free/matrix_vector_stokes.cc new file mode 100644 index 0000000000..508aa84dfb --- /dev/null +++ b/tests/matrix_free/matrix_vector_stokes.cc @@ -0,0 +1,332 @@ +//------------------ matrix_vector_stokes.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_stokes.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// matrix-vector products by comparing with the result of deal.II sparse +// matrix. The mesh uses a hypercube mesh with no hanging nodes and no other +// constraints for a vector-valued problem (stokes equations). + +#include "../tests.h" + +std::ofstream logfile("matrix_vector_stokes/output"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + + +template +class MatrixFreeTest +{ + public: + typedef typename DoFHandler::active_cell_iterator CellIterator; + typedef double Number; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in) + {}; + + void + local_apply (const MatrixFree &data, + VectorType &dst, + const VectorType &src, + const std::pair &cell_range) const + { + typedef VectorizedArray vector_t; + FEEvaluation velocity (data, 0); + FEEvaluation pressure (data, 1); + + for(unsigned int cell=cell_range.first;cell sym_grad_u = + velocity.get_symmetric_gradient (q); + vector_t pres = pressure.get_value(q); + vector_t div = -velocity.get_divergence(q); + pressure.submit_value (div, q); + + // subtract p * I + for (unsigned int d=0; d::local_apply, + this, dst, src); + }; + +private: + const MatrixFree &data; +}; + + + +template +void test () +{ + Triangulation triangulation; + { + std::vector subdivisions (dim, 1); + subdivisions[0] = 4; + + const Point bottom_left = (dim == 2 ? + Point(-2,-1) : + Point(-2,0,-1)); + const Point top_right = (dim == 2 ? + Point(2,0) : + Point(2,1,0)); + + GridGenerator::subdivided_hyper_rectangle (triangulation, + subdivisions, + bottom_left, + top_right); + } + triangulation.refine_global (4-dim); + + FE_Q fe_u (fe_degree+1); + FE_Q fe_p (fe_degree); + FESystem fe (fe_u, dim, fe_p, 1); + DoFHandler dof_handler_u (triangulation); + DoFHandler dof_handler_p (triangulation); + DoFHandler dof_handler (triangulation); + + MatrixFree mf_data; + + ConstraintMatrix constraints; + + BlockSparsityPattern sparsity_pattern; + BlockSparseMatrix system_matrix; + + BlockVector solution; + BlockVector system_rhs; + std::vector > vec1, vec2; + + dof_handler.distribute_dofs (fe); + dof_handler_u.distribute_dofs (fe_u); + dof_handler_p.distribute_dofs (fe_p); + DoFRenumbering::component_wise (dof_handler); + + constraints.close (); + + std::vector dofs_per_block (dim+1); + DoFTools::count_dofs_per_component (dof_handler, dofs_per_block); + + //std::cout << " Number of active cells: " + // << triangulation.n_active_cells() + // << std::endl + // << " Number of degrees of freedom: " + // << dof_handler.n_dofs() + // << " (" << n_u << '+' << n_p << ')' + // << std::endl; + + { + BlockCompressedSimpleSparsityPattern csp (dim+1,dim+1); + + for (unsigned int d=0; d quadrature_formula(fe_degree+2); + + FEValues fe_values (fe, quadrature_formula, + update_values | + update_JxW_values | + update_gradients); + + const unsigned int dofs_per_cell = fe.dofs_per_cell; + const unsigned int n_q_points = quadrature_formula.size(); + + FullMatrix local_matrix (dofs_per_cell, dofs_per_cell); + + std::vector local_dof_indices (dofs_per_cell); + + const FEValuesExtractors::Vector velocities (0); + const FEValuesExtractors::Scalar pressure (dim); + + std::vector > phi_grads_u (dofs_per_cell); + std::vector div_phi_u (dofs_per_cell); + std::vector phi_p (dofs_per_cell); + + typename DoFHandler::active_cell_iterator + cell = dof_handler.begin_active(), + endc = dof_handler.end(); + for (; cell!=endc; ++cell) + { + fe_values.reinit (cell); + local_matrix = 0; + + for (unsigned int q=0; qget_dof_indices (local_dof_indices); + constraints.distribute_local_to_global (local_matrix, + local_dof_indices, + system_matrix); + } + } + + // first system_rhs with random numbers + for (unsigned int i=0; i*> dofs; + dofs.push_back(&dof_handler_u); + dofs.push_back(&dof_handler_p); + ConstraintMatrix dummy_constraints; + dummy_constraints.close(); + std::vector constraints; + constraints.push_back (&dummy_constraints); + constraints.push_back (&dummy_constraints); + QGauss<1> quad(fe_degree+2); + mf_data.reinit (dofs, constraints, quad, + typename MatrixFree::AdditionalData + (MPI_COMM_WORLD, + MatrixFree::AdditionalData::none)); + } + + system_matrix.vmult (solution, system_rhs); + + typedef std::vector > VectorType; + MatrixFreeTest mf (mf_data); + mf.vmult (vec2, vec1); + + // Verification + double error = 0.; + for (unsigned int i=0; i(); + test<2,2>(); + test<2,3>(); + test<2,4>(); + deallog.pop(); + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/matrix_vector_stokes/cmp/generic b/tests/matrix_free/matrix_vector_stokes/cmp/generic new file mode 100644 index 0000000000..fca0544652 --- /dev/null +++ b/tests/matrix_free/matrix_vector_stokes/cmp/generic @@ -0,0 +1,16 @@ + +DEAL:: +DEAL::Test with doubles +DEAL:: +DEAL:2d:: Verification fe degree 1: 0 +DEAL:2d:: +DEAL:2d:: Verification fe degree 2: 0 +DEAL:2d:: +DEAL:2d:: Verification fe degree 3: 0 +DEAL:2d:: +DEAL:2d:: Verification fe degree 4: 0 +DEAL:2d:: +DEAL:3d:: Verification fe degree 1: 0 +DEAL:3d:: +DEAL:3d:: Verification fe degree 2: 0 +DEAL:3d:: diff --git a/tests/matrix_free/matrix_vector_stokes_noflux.cc b/tests/matrix_free/matrix_vector_stokes_noflux.cc new file mode 100644 index 0000000000..8ab68f615f --- /dev/null +++ b/tests/matrix_free/matrix_vector_stokes_noflux.cc @@ -0,0 +1,352 @@ +//------------------ matrix_vector_stokes_noflux.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_vector_stokes_noflux.cc ------------------------ + + +// this function tests the correctness of the implementation of matrix free +// matrix-vector products by comparing with the result of deal.II sparse +// matrix. The mesh uses a hypershell mesh with hanging nodes and constraints +// between the vector components in the form of no-normal flux constraints on +// the Stokes equations. + +#include "../tests.h" + +std::ofstream logfile("matrix_vector_stokes_noflux/output"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + + +template +class MatrixFreeTest +{ + public: + typedef typename DoFHandler::active_cell_iterator CellIterator; + typedef double Number; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in) + {}; + + void + local_apply (const MatrixFree &data, + VectorType &dst, + const VectorType &src, + const std::pair &cell_range) const + { + typedef VectorizedArray vector_t; + FEEvaluation velocity (data, 0); + FEEvaluation pressure (data, 1); + + for(unsigned int cell=cell_range.first;cell sym_grad_u = + velocity.get_symmetric_gradient (q); + vector_t pres = pressure.get_value(q); + vector_t div = -velocity.get_divergence(q); + pressure.submit_value (div, q); + + // subtract p * I + for (unsigned int d=0; d::local_apply, + this, dst, src); + }; + +private: + const MatrixFree &data; +}; + + + +template +void test () +{ + Triangulation triangulation; + GridGenerator::hyper_shell (triangulation, Point(), + 0.5, 1., 96, true); + static HyperShellBoundary boundary; + triangulation.set_boundary (0, boundary); + triangulation.set_boundary (1, boundary); + triangulation.begin_active()->set_refine_flag(); + triangulation.last()->set_refine_flag(); + triangulation.execute_coarsening_and_refinement(); + triangulation.refine_global (3-dim); + triangulation.last()->set_refine_flag(); + triangulation.execute_coarsening_and_refinement(); + + MappingQ mapping (3); + FE_Q fe_u_scal (fe_degree+1); + FESystem fe_u (fe_u_scal,dim); + FE_Q fe_p (fe_degree); + FESystem fe (fe_u_scal, dim, fe_p, 1); + DoFHandler dof_handler_u (triangulation); + DoFHandler dof_handler_p (triangulation); + DoFHandler dof_handler (triangulation); + + MatrixFree mf_data; + + ConstraintMatrix constraints, constraints_u, constraints_p; + + BlockSparsityPattern sparsity_pattern; + BlockSparseMatrix system_matrix; + + BlockVector solution; + BlockVector system_rhs; + std::vector > vec1, vec2; + + dof_handler.distribute_dofs (fe); + dof_handler_u.distribute_dofs (fe_u); + dof_handler_p.distribute_dofs (fe_p); + std::vector stokes_sub_blocks (dim+1,0); + stokes_sub_blocks[dim] = 1; + DoFRenumbering::component_wise (dof_handler, stokes_sub_blocks); + + std::set no_normal_flux_boundaries; + no_normal_flux_boundaries.insert (0); + no_normal_flux_boundaries.insert (1); + DoFTools::make_hanging_node_constraints (dof_handler, + constraints); + VectorTools::compute_no_normal_flux_constraints (dof_handler, 0, + no_normal_flux_boundaries, + constraints, mapping); + constraints.close (); + DoFTools::make_hanging_node_constraints (dof_handler_u, + constraints_u); + VectorTools::compute_no_normal_flux_constraints (dof_handler_u, 0, + no_normal_flux_boundaries, + constraints_u, mapping); + constraints_u.close (); + DoFTools::make_hanging_node_constraints (dof_handler_p, + constraints_p); + constraints_p.close (); + + std::vector dofs_per_block (2); + DoFTools::count_dofs_per_block (dof_handler, dofs_per_block, + stokes_sub_blocks); + + //std::cout << "Number of active cells: " + // << triangulation.n_active_cells() + // << std::endl + // << "Number of degrees of freedom: " + // << dof_handler.n_dofs() + // << " (" << n_u << '+' << n_p << ')' + // << std::endl; + + { + BlockCompressedSimpleSparsityPattern csp (2,2); + + for (unsigned int d=0; d<2; ++d) + for (unsigned int e=0; e<2; ++e) + csp.block(d,e).reinit (dofs_per_block[d], dofs_per_block[e]); + + csp.collect_sizes(); + + DoFTools::make_sparsity_pattern (dof_handler, csp, constraints, false); + sparsity_pattern.copy_from (csp); + } + + system_matrix.reinit (sparsity_pattern); + + // this is from step-22 + { + QGauss quadrature_formula(fe_degree+2); + + FEValues fe_values (mapping, fe, quadrature_formula, + update_values | + update_JxW_values | + update_gradients); + + const unsigned int dofs_per_cell = fe.dofs_per_cell; + const unsigned int n_q_points = quadrature_formula.size(); + + FullMatrix local_matrix (dofs_per_cell, dofs_per_cell); + + std::vector local_dof_indices (dofs_per_cell); + + const FEValuesExtractors::Vector velocities (0); + const FEValuesExtractors::Scalar pressure (dim); + + std::vector > phi_grads_u (dofs_per_cell); + std::vector div_phi_u (dofs_per_cell); + std::vector phi_p (dofs_per_cell); + + typename DoFHandler::active_cell_iterator + cell = dof_handler.begin_active(), + endc = dof_handler.end(); + for (; cell!=endc; ++cell) + { + fe_values.reinit (cell); + local_matrix = 0; + + for (unsigned int q=0; qget_dof_indices (local_dof_indices); + constraints.distribute_local_to_global (local_matrix, + local_dof_indices, + system_matrix); + } + } + + + solution.reinit (2); + for (unsigned int d=0; d<2; ++d) + solution.block(d).reinit (dofs_per_block[d]); + solution.collect_sizes (); + + system_rhs.reinit (solution); + + vec1.resize (2); + vec2.resize (2); + for (unsigned int d=0; d<2; ++d) + { + vec1[d].reinit (dofs_per_block[d]); + vec2[d].reinit (vec1[d]); + } + + // fill system_rhs with random numbers + for (unsigned int j=0; j*> dofs; + dofs.push_back(&dof_handler_u); + dofs.push_back(&dof_handler_p); + std::vector constraints; + constraints.push_back (&constraints_u); + constraints.push_back (&constraints_p); + QGauss<1> quad(fe_degree+2); + // no parallelism + mf_data.reinit (mapping, dofs, constraints, quad, + typename MatrixFree::AdditionalData + (MPI_COMM_WORLD, + MatrixFree::AdditionalData::none)); + } + + system_matrix.vmult (solution, system_rhs); + + typedef std::vector > VectorType; + MatrixFreeTest mf (mf_data); + mf.vmult (vec2, vec1); + + // Verification + double error = 0.; + for (unsigned int i=0; i<2; ++i) + for (unsigned int j=0; j(); + test<2,2>(); + test<2,3>(); + test<2,4>(); + deallog.pop(); + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/matrix_vector_stokes_noflux/cmp/generic b/tests/matrix_free/matrix_vector_stokes_noflux/cmp/generic new file mode 100644 index 0000000000..afab4527f1 --- /dev/null +++ b/tests/matrix_free/matrix_vector_stokes_noflux/cmp/generic @@ -0,0 +1,16 @@ + +DEAL:: +DEAL::Test with doubles +DEAL:: +DEAL:2d::Verification fe degree 1: 0 +DEAL:2d:: +DEAL:2d::Verification fe degree 2: 0 +DEAL:2d:: +DEAL:2d::Verification fe degree 3: 0 +DEAL:2d:: +DEAL:2d::Verification fe degree 4: 0 +DEAL:2d:: +DEAL:3d::Verification fe degree 1: 0 +DEAL:3d:: +DEAL:3d::Verification fe degree 2: 0 +DEAL:3d:: diff --git a/tests/matrix_free/quadrature_points.cc b/tests/matrix_free/quadrature_points.cc new file mode 100644 index 0000000000..6c54314ea6 --- /dev/null +++ b/tests/matrix_free/quadrature_points.cc @@ -0,0 +1,112 @@ +//------------------ quadrature_points.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ quadrature_points.cc ------------------------ + + +// this function tests the correctness of cached quadrature points + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +std::ofstream logfile("quadrature_points/output"); + + +template +void test () +{ + typedef double number; + Triangulation tria; + GridGenerator::hyper_ball (tria); + static const HyperBallBoundary boundary; + tria.set_boundary (0, boundary); + tria.refine_global(5-dim); + + MappingQ mapping (4); + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + deallog << "Testing " << fe.get_name() << std::endl; + //std::cout << "Number of cells: " << tria.n_active_cells() << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + constraints.close(); + + MatrixFree mf_data; + { + const QGauss<1> quad (fe_degree+1); + typename MatrixFree::AdditionalData data; + data.tasks_parallel_scheme = MatrixFree::AdditionalData::none; + data.mapping_update_flags = update_quadrature_points; + mf_data.reinit (mapping, dof, constraints, quad, data); + } + + double error_points = 0, abs_points = 0; + const unsigned int n_cells = mf_data.get_size_info().n_macro_cells; + FEEvaluation fe_eval (mf_data); + FEValues fe_values (mapping, fe, mf_data.get_quad(), + update_quadrature_points); + + typedef VectorizedArray vector_t; + for (unsigned int cell=0; cell(); + test<2,2>(); + test<2,4>(); + deallog.pop(); + deallog.push("3d"); + test<3,1>(); + test<3,3>(); + deallog.pop(); + } +} + diff --git a/tests/matrix_free/quadrature_points/cmp/generic b/tests/matrix_free/quadrature_points/cmp/generic new file mode 100644 index 0000000000..922c85ad1b --- /dev/null +++ b/tests/matrix_free/quadrature_points/cmp/generic @@ -0,0 +1,16 @@ + +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:2d::Testing FE_Q<2>(4) +DEAL:2d::Norm of difference: 0 +DEAL:2d:: +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: +DEAL:3d::Testing FE_Q<3>(3) +DEAL:3d::Norm of difference: 0 +DEAL:3d:: diff --git a/tests/matrix_free/thread_correctness.cc b/tests/matrix_free/thread_correctness.cc new file mode 100644 index 0000000000..29a89cbce4 --- /dev/null +++ b/tests/matrix_free/thread_correctness.cc @@ -0,0 +1,141 @@ +//------------------ thread_correctness.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ thread_correctness.cc ------------------------ + + +// this function tests the correctness of the thread parallelization of the +// matrix-free class extensively + +#include "../tests.h" +#include +#include "create_mesh.h" + +std::ofstream logfile("thread_correctness/output"); + +#include "matrix_vector_common.h" + + +template +void sub_test() +{ + Triangulation tria; + create_mesh (tria); + tria.begin_active ()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + for (; cell!=endc; ++cell) + if (cell->center().norm()<0.5) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 || fe_degree < 2) + tria.refine_global(1); + tria.begin(tria.n_levels()-1)->set_refine_flag(); + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim==2 && fe_degree < 2) + tria.refine_global(2); + else + tria.refine_global(1); + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + deallog << "Testing " << fe.get_name() << std::endl; + + // run test for several different meshes + for (unsigned int i=0; i<8-2*dim; ++i) + { + cell = tria.begin_active (); + endc = tria.end(); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (counter % (9-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + + dof.distribute_dofs(fe); + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints(dof, constraints); + VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction(), + constraints); + constraints.close(); + + //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + MatrixFree mf_data, mf_data_color, mf_data_partition; + { + const QGauss<1> quad (fe_degree+1); + mf_data.reinit (dof, constraints, quad, + typename MatrixFree::AdditionalData(MPI_COMM_SELF,MatrixFree::AdditionalData::none)); + + // choose block size of 3 which introduces + // some irregularity to the blocks (stress the + // non-overlapping computation harder) + mf_data_color.reinit (dof, constraints, quad, + typename MatrixFree::AdditionalData + (MPI_COMM_SELF, + MatrixFree::AdditionalData::partition_color, + 3)); + mf_data_partition.reinit (dof, constraints, quad, + typename MatrixFree::AdditionalData + (MPI_COMM_SELF, + MatrixFree::AdditionalData::partition_partition, + 3)); + } + + MatrixFreeTest mf_ref (mf_data); + MatrixFreeTest mf_color (mf_data_color); + MatrixFreeTest mf_partition (mf_data_partition); + Vector in_dist (dof.n_dofs()); + Vector out_dist (in_dist), out_color (in_dist), + out_partition(in_dist); + + for (unsigned int i=0; i +void test () +{ + deallog << "Test doubles" << std::endl; + sub_test(); + deallog.threshold_double(2.e-6); + deallog << "Test floats" << std::endl; + sub_test(); +} diff --git a/tests/matrix_free/thread_correctness/cmp/generic b/tests/matrix_free/thread_correctness/cmp/generic new file mode 100644 index 0000000000..132d50e530 --- /dev/null +++ b/tests/matrix_free/thread_correctness/cmp/generic @@ -0,0 +1,529 @@ + +DEAL:2d::Test doubles +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d:: +DEAL:2d::Test floats +DEAL:2d::Testing FE_Q<2>(1) +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d:: +DEAL:2d::Test doubles +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d:: +DEAL:2d::Test floats +DEAL:2d::Testing FE_Q<2>(2) +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d::Sweep 0, error in partition/color: 0 +DEAL:2d::Sweep 0, error in partition/partition: 0 +DEAL:2d::Sweep 1, error in partition/color: 0 +DEAL:2d::Sweep 1, error in partition/partition: 0 +DEAL:2d::Sweep 2, error in partition/color: 0 +DEAL:2d::Sweep 2, error in partition/partition: 0 +DEAL:2d::Sweep 3, error in partition/color: 0 +DEAL:2d::Sweep 3, error in partition/partition: 0 +DEAL:2d::Sweep 4, error in partition/color: 0 +DEAL:2d::Sweep 4, error in partition/partition: 0 +DEAL:2d::Sweep 5, error in partition/color: 0 +DEAL:2d::Sweep 5, error in partition/partition: 0 +DEAL:2d::Sweep 6, error in partition/color: 0 +DEAL:2d::Sweep 6, error in partition/partition: 0 +DEAL:2d::Sweep 7, error in partition/color: 0 +DEAL:2d::Sweep 7, error in partition/partition: 0 +DEAL:2d::Sweep 8, error in partition/color: 0 +DEAL:2d::Sweep 8, error in partition/partition: 0 +DEAL:2d::Sweep 9, error in partition/color: 0 +DEAL:2d::Sweep 9, error in partition/partition: 0 +DEAL:2d:: +DEAL:2d:: +DEAL:3d::Test doubles +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Sweep 0, error in partition/color: 0 +DEAL:3d::Sweep 0, error in partition/partition: 0 +DEAL:3d::Sweep 1, error in partition/color: 0 +DEAL:3d::Sweep 1, error in partition/partition: 0 +DEAL:3d::Sweep 2, error in partition/color: 0 +DEAL:3d::Sweep 2, error in partition/partition: 0 +DEAL:3d::Sweep 3, error in partition/color: 0 +DEAL:3d::Sweep 3, error in partition/partition: 0 +DEAL:3d::Sweep 4, error in partition/color: 0 +DEAL:3d::Sweep 4, error in partition/partition: 0 +DEAL:3d::Sweep 5, error in partition/color: 0 +DEAL:3d::Sweep 5, error in partition/partition: 0 +DEAL:3d::Sweep 6, error in partition/color: 0 +DEAL:3d::Sweep 6, error in partition/partition: 0 +DEAL:3d::Sweep 7, error in partition/color: 0 +DEAL:3d::Sweep 7, error in partition/partition: 0 +DEAL:3d::Sweep 8, error in partition/color: 0 +DEAL:3d::Sweep 8, error in partition/partition: 0 +DEAL:3d::Sweep 9, error in partition/color: 0 +DEAL:3d::Sweep 9, error in partition/partition: 0 +DEAL:3d:: +DEAL:3d::Sweep 0, error in partition/color: 0 +DEAL:3d::Sweep 0, error in partition/partition: 0 +DEAL:3d::Sweep 1, error in partition/color: 0 +DEAL:3d::Sweep 1, error in partition/partition: 0 +DEAL:3d::Sweep 2, error in partition/color: 0 +DEAL:3d::Sweep 2, error in partition/partition: 0 +DEAL:3d::Sweep 3, error in partition/color: 0 +DEAL:3d::Sweep 3, error in partition/partition: 0 +DEAL:3d::Sweep 4, error in partition/color: 0 +DEAL:3d::Sweep 4, error in partition/partition: 0 +DEAL:3d::Sweep 5, error in partition/color: 0 +DEAL:3d::Sweep 5, error in partition/partition: 0 +DEAL:3d::Sweep 6, error in partition/color: 0 +DEAL:3d::Sweep 6, error in partition/partition: 0 +DEAL:3d::Sweep 7, error in partition/color: 0 +DEAL:3d::Sweep 7, error in partition/partition: 0 +DEAL:3d::Sweep 8, error in partition/color: 0 +DEAL:3d::Sweep 8, error in partition/partition: 0 +DEAL:3d::Sweep 9, error in partition/color: 0 +DEAL:3d::Sweep 9, error in partition/partition: 0 +DEAL:3d:: +DEAL:3d:: +DEAL:3d::Test floats +DEAL:3d::Testing FE_Q<3>(1) +DEAL:3d::Sweep 0, error in partition/color: 0 +DEAL:3d::Sweep 0, error in partition/partition: 0 +DEAL:3d::Sweep 1, error in partition/color: 0 +DEAL:3d::Sweep 1, error in partition/partition: 0 +DEAL:3d::Sweep 2, error in partition/color: 0 +DEAL:3d::Sweep 2, error in partition/partition: 0 +DEAL:3d::Sweep 3, error in partition/color: 0 +DEAL:3d::Sweep 3, error in partition/partition: 0 +DEAL:3d::Sweep 4, error in partition/color: 0 +DEAL:3d::Sweep 4, error in partition/partition: 0 +DEAL:3d::Sweep 5, error in partition/color: 0 +DEAL:3d::Sweep 5, error in partition/partition: 0 +DEAL:3d::Sweep 6, error in partition/color: 0 +DEAL:3d::Sweep 6, error in partition/partition: 0 +DEAL:3d::Sweep 7, error in partition/color: 0 +DEAL:3d::Sweep 7, error in partition/partition: 0 +DEAL:3d::Sweep 8, error in partition/color: 0 +DEAL:3d::Sweep 8, error in partition/partition: 0 +DEAL:3d::Sweep 9, error in partition/color: 0 +DEAL:3d::Sweep 9, error in partition/partition: 0 +DEAL:3d:: +DEAL:3d::Sweep 0, error in partition/color: 0 +DEAL:3d::Sweep 0, error in partition/partition: 0 +DEAL:3d::Sweep 1, error in partition/color: 0 +DEAL:3d::Sweep 1, error in partition/partition: 0 +DEAL:3d::Sweep 2, error in partition/color: 0 +DEAL:3d::Sweep 2, error in partition/partition: 0 +DEAL:3d::Sweep 3, error in partition/color: 0 +DEAL:3d::Sweep 3, error in partition/partition: 0 +DEAL:3d::Sweep 4, error in partition/color: 0 +DEAL:3d::Sweep 4, error in partition/partition: 0 +DEAL:3d::Sweep 5, error in partition/color: 0 +DEAL:3d::Sweep 5, error in partition/partition: 0 +DEAL:3d::Sweep 6, error in partition/color: 0 +DEAL:3d::Sweep 6, error in partition/partition: 0 +DEAL:3d::Sweep 7, error in partition/color: 0 +DEAL:3d::Sweep 7, error in partition/partition: 0 +DEAL:3d::Sweep 8, error in partition/color: 0 +DEAL:3d::Sweep 8, error in partition/partition: 0 +DEAL:3d::Sweep 9, error in partition/color: 0 +DEAL:3d::Sweep 9, error in partition/partition: 0 +DEAL:3d:: +DEAL:3d:: +DEAL:3d::Test doubles +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Sweep 0, error in partition/color: 0 +DEAL:3d::Sweep 0, error in partition/partition: 0 +DEAL:3d::Sweep 1, error in partition/color: 0 +DEAL:3d::Sweep 1, error in partition/partition: 0 +DEAL:3d::Sweep 2, error in partition/color: 0 +DEAL:3d::Sweep 2, error in partition/partition: 0 +DEAL:3d::Sweep 3, error in partition/color: 0 +DEAL:3d::Sweep 3, error in partition/partition: 0 +DEAL:3d::Sweep 4, error in partition/color: 0 +DEAL:3d::Sweep 4, error in partition/partition: 0 +DEAL:3d::Sweep 5, error in partition/color: 0 +DEAL:3d::Sweep 5, error in partition/partition: 0 +DEAL:3d::Sweep 6, error in partition/color: 0 +DEAL:3d::Sweep 6, error in partition/partition: 0 +DEAL:3d::Sweep 7, error in partition/color: 0 +DEAL:3d::Sweep 7, error in partition/partition: 0 +DEAL:3d::Sweep 8, error in partition/color: 0 +DEAL:3d::Sweep 8, error in partition/partition: 0 +DEAL:3d::Sweep 9, error in partition/color: 0 +DEAL:3d::Sweep 9, error in partition/partition: 0 +DEAL:3d:: +DEAL:3d::Sweep 0, error in partition/color: 0 +DEAL:3d::Sweep 0, error in partition/partition: 0 +DEAL:3d::Sweep 1, error in partition/color: 0 +DEAL:3d::Sweep 1, error in partition/partition: 0 +DEAL:3d::Sweep 2, error in partition/color: 0 +DEAL:3d::Sweep 2, error in partition/partition: 0 +DEAL:3d::Sweep 3, error in partition/color: 0 +DEAL:3d::Sweep 3, error in partition/partition: 0 +DEAL:3d::Sweep 4, error in partition/color: 0 +DEAL:3d::Sweep 4, error in partition/partition: 0 +DEAL:3d::Sweep 5, error in partition/color: 0 +DEAL:3d::Sweep 5, error in partition/partition: 0 +DEAL:3d::Sweep 6, error in partition/color: 0 +DEAL:3d::Sweep 6, error in partition/partition: 0 +DEAL:3d::Sweep 7, error in partition/color: 0 +DEAL:3d::Sweep 7, error in partition/partition: 0 +DEAL:3d::Sweep 8, error in partition/color: 0 +DEAL:3d::Sweep 8, error in partition/partition: 0 +DEAL:3d::Sweep 9, error in partition/color: 0 +DEAL:3d::Sweep 9, error in partition/partition: 0 +DEAL:3d:: +DEAL:3d:: +DEAL:3d::Test floats +DEAL:3d::Testing FE_Q<3>(2) +DEAL:3d::Sweep 0, error in partition/color: 0 +DEAL:3d::Sweep 0, error in partition/partition: 0 +DEAL:3d::Sweep 1, error in partition/color: 0 +DEAL:3d::Sweep 1, error in partition/partition: 0 +DEAL:3d::Sweep 2, error in partition/color: 0 +DEAL:3d::Sweep 2, error in partition/partition: 0 +DEAL:3d::Sweep 3, error in partition/color: 0 +DEAL:3d::Sweep 3, error in partition/partition: 0 +DEAL:3d::Sweep 4, error in partition/color: 0 +DEAL:3d::Sweep 4, error in partition/partition: 0 +DEAL:3d::Sweep 5, error in partition/color: 0 +DEAL:3d::Sweep 5, error in partition/partition: 0 +DEAL:3d::Sweep 6, error in partition/color: 0 +DEAL:3d::Sweep 6, error in partition/partition: 0 +DEAL:3d::Sweep 7, error in partition/color: 0 +DEAL:3d::Sweep 7, error in partition/partition: 0 +DEAL:3d::Sweep 8, error in partition/color: 0 +DEAL:3d::Sweep 8, error in partition/partition: 0 +DEAL:3d::Sweep 9, error in partition/color: 0 +DEAL:3d::Sweep 9, error in partition/partition: 0 +DEAL:3d:: +DEAL:3d::Sweep 0, error in partition/color: 0 +DEAL:3d::Sweep 0, error in partition/partition: 0 +DEAL:3d::Sweep 1, error in partition/color: 0 +DEAL:3d::Sweep 1, error in partition/partition: 0 +DEAL:3d::Sweep 2, error in partition/color: 0 +DEAL:3d::Sweep 2, error in partition/partition: 0 +DEAL:3d::Sweep 3, error in partition/color: 0 +DEAL:3d::Sweep 3, error in partition/partition: 0 +DEAL:3d::Sweep 4, error in partition/color: 0 +DEAL:3d::Sweep 4, error in partition/partition: 0 +DEAL:3d::Sweep 5, error in partition/color: 0 +DEAL:3d::Sweep 5, error in partition/partition: 0 +DEAL:3d::Sweep 6, error in partition/color: 0 +DEAL:3d::Sweep 6, error in partition/partition: 0 +DEAL:3d::Sweep 7, error in partition/color: 0 +DEAL:3d::Sweep 7, error in partition/partition: 0 +DEAL:3d::Sweep 8, error in partition/color: 0 +DEAL:3d::Sweep 8, error in partition/partition: 0 +DEAL:3d::Sweep 9, error in partition/color: 0 +DEAL:3d::Sweep 9, error in partition/partition: 0 +DEAL:3d:: +DEAL:3d:: diff --git a/tests/matrix_free/thread_correctness_hp.cc b/tests/matrix_free/thread_correctness_hp.cc new file mode 100644 index 0000000000..41fad29114 --- /dev/null +++ b/tests/matrix_free/thread_correctness_hp.cc @@ -0,0 +1,220 @@ +//------------------ thread_correctness_hp.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ thread_correctness_hp.cc ------------------------ + + +// this function tests the correctness of the implementation of parallel +// matrix free matrix-vector products for hp elements by comparing to the +// serial version + +#include "../tests.h" + +std::ofstream logfile("thread_correctness_hp/output"); + +#include "create_mesh.h" +#include "matrix_vector_common.h" +#include +#include +#include +#include + + + +template +class MatrixFreeTestHP +{ + public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTestHP(const MatrixFree &data_in): + data (data_in) + {}; + + void + local_apply (const MatrixFree &data, + Vector &dst, + const Vector &src, + const std::pair &cell_range) const + { + // Ask MatrixFree for cell_range for different + // orders + std::pair subrange_deg = + data.create_cell_subrange_hp (cell_range, 1); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + subrange_deg = data.create_cell_subrange_hp (cell_range, 2); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + subrange_deg = data.create_cell_subrange_hp (cell_range, 3); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + subrange_deg = data.create_cell_subrange_hp (cell_range, 4); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + subrange_deg = data.create_cell_subrange_hp (cell_range, 5); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + subrange_deg = data.create_cell_subrange_hp (cell_range, 6); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + subrange_deg = data.create_cell_subrange_hp (cell_range, 7); + if (subrange_deg.second > subrange_deg.first) + helmholtz_operator (data, dst, src, + subrange_deg); + } + + void vmult (Vector &dst, + const Vector &src) const + { + dst = 0; + data.cell_loop (&MatrixFreeTestHP::local_apply, this, dst, src); + }; + +private: + const MatrixFree &data; +}; + + + +template +void do_test (const unsigned int parallel_option) +{ + Triangulation tria; + create_mesh (tria); + tria.refine_global(2); + + // refine a few cells + for (unsigned int i=0; i<11-3*dim; ++i) + { + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + for (; cell!=endc; ++cell) + if (rand() % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + const unsigned int max_degree = 9-2*dim; + + hp::FECollection fe_collection; + hp::QCollection<1> quadrature_collection_mf; + + for (unsigned int deg=1; deg<=max_degree; ++deg) + { + fe_collection.push_back (FE_Q(QGaussLobatto<1>(deg+1))); + quadrature_collection_mf.push_back (QGauss<1>(deg+1)); + } + + hp::DoFHandler dof(tria); + // set the active FE index in a random order + { + typename hp::DoFHandler::active_cell_iterator + cell = dof.begin_active(), + endc = dof.end(); + for (; cell!=endc; ++cell) + { + const unsigned int fe_index = rand() % max_degree; + cell->set_active_fe_index (fe_index); + } + } + + // setup DoFs + dof.distribute_dofs(fe_collection); + ConstraintMatrix constraints; + DoFTools::make_hanging_node_constraints (dof, + constraints); + VectorTools::interpolate_boundary_values (dof, + 0, + ZeroFunction(), + constraints); + constraints.close (); + + //std::cout << "Number of cells: " << dof.get_tria().n_active_cells() << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + // set up reference MatrixFree + MatrixFree mf_data; + typename MatrixFree::AdditionalData data; + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::none; + mf_data.reinit (dof, constraints, quadrature_collection_mf, data); + MatrixFreeTestHP mf (mf_data); + + MatrixFree mf_data_par; + if (parallel_option == 0) + { + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::partition_partition; + deallog << "Parallel option partition/partition" << std::endl; + } + else + { + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::partition_color; + deallog << "Parallel option partition/color" << std::endl; + } + data.tasks_block_size = 1; + mf_data_par.reinit (dof, constraints, quadrature_collection_mf, data); + MatrixFreeTestHP mf_par(mf_data_par); + + // fill a right hand side vector with random + // numbers in unconstrained degrees of freedom + Vector src (dof.n_dofs()); + Vector result_ref(src), result_mf (src); + + for (unsigned int i=0; i +void test () +{ + // 'misuse' fe_degree for setting the parallel + // option here + unsigned int parallel_option = 0; + if (fe_degree == 1) + parallel_option = 0; + else if (fe_degree == 2) + parallel_option = 1; + else + return; + deallog.push("double"); + deallog.threshold_double(1.e-12); + do_test(parallel_option); + deallog.pop(); + deallog.push("float"); + deallog.threshold_double(1.e-6); + do_test(parallel_option); + deallog.pop(); +} + diff --git a/tests/matrix_free/thread_correctness_hp/cmp/generic b/tests/matrix_free/thread_correctness_hp/cmp/generic new file mode 100644 index 0000000000..0bfece6913 --- /dev/null +++ b/tests/matrix_free/thread_correctness_hp/cmp/generic @@ -0,0 +1,25 @@ + +DEAL:2d:double::Parallel option partition/partition +DEAL:2d:double::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +DEAL:2d:double:: +DEAL:2d:float::Parallel option partition/partition +DEAL:2d:float::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +DEAL:2d:float:: +DEAL:2d:double::Parallel option partition/color +DEAL:2d:double::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +DEAL:2d:double:: +DEAL:2d:float::Parallel option partition/color +DEAL:2d:float::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +DEAL:2d:float:: +DEAL:3d:double::Parallel option partition/partition +DEAL:3d:double::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +DEAL:3d:double:: +DEAL:3d:float::Parallel option partition/partition +DEAL:3d:float::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +DEAL:3d:float:: +DEAL:3d:double::Parallel option partition/color +DEAL:3d:double::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +DEAL:3d:double:: +DEAL:3d:float::Parallel option partition/color +DEAL:3d:float::Norm of difference: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +DEAL:3d:float:: diff --git a/tests/mpi/matrix_free_01.cc b/tests/mpi/matrix_free_01.cc new file mode 100644 index 0000000000..93bb5157e6 --- /dev/null +++ b/tests/mpi/matrix_free_01.cc @@ -0,0 +1,284 @@ +//------------------ matrix_free_01.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_free_01.cc ------------------------ + + +// this tests the correctness of matrix free matrix-vector products by +// comparing the result with a Trilinos sparse matrix assembled in the usual +// way. The mesh is distributed among processors (hypercube) and has both +// hanging nodes (by randomly refining some cells, so the mesh is going to be +// different when run with different numbers of processors) and Dirichlet +// boundary conditions +// +// this test does not use multithreading within the MPI nodes. + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +template +void +helmholtz_operator (const MatrixFree &data, + parallel::distributed::Vector &dst, + const parallel::distributed::Vector &src, + const std::pair &cell_range) +{ + FEEvaluation fe_eval (data); + const unsigned int n_q_points = fe_eval.n_q_points; + + for(unsigned int cell=cell_range.first;cell +class MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in) + {}; + + void vmult (parallel::distributed::Vector &dst, + const parallel::distributed::Vector &src) const + { + dst = 0; + const std_cxx1x::function &, + parallel::distributed::Vector&, + const parallel::distributed::Vector&, + const std::pair&)> + wrap = helmholtz_operator; + data.cell_loop (wrap, dst, src); + }; + +private: + const MatrixFree &data; +}; + + + + + +template +void test () +{ + typedef double number; + + parallel::distributed::Triangulation tria (MPI_COMM_WORLD); + GridGenerator::hyper_cube (tria); + tria.refine_global(1); + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + cell = tria.begin_active (); + for (; cell!=endc; ++cell) + if (cell->is_locally_owned()) + if (cell->center().norm()<0.2) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 && fe_degree < 2) + tria.refine_global(2); + else + tria.refine_global(1); + if (tria.begin(tria.n_levels()-1)->is_locally_owned()) + tria.begin(tria.n_levels()-1)->set_refine_flag(); + if (tria.last()->is_locally_owned()) + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (unsigned int i=0; i<10-3*dim; ++i) + { + cell = tria.begin_active (); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (cell->is_locally_owned()) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + IndexSet owned_set = dof.locally_owned_dofs(); + IndexSet relevant_set; + DoFTools::extract_locally_relevant_dofs (dof, relevant_set); + + ConstraintMatrix constraints (relevant_set); + DoFTools::make_hanging_node_constraints(dof, constraints); + VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction(), + constraints); + constraints.close(); + + deallog << "Testing " << dof.get_fe().get_name() << std::endl; + //std::cout << "Number of cells: " << tria.n_global_active_cells() << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + MatrixFree mf_data; + { + const QGauss<1> quad (fe_degree+1); + typename MatrixFree::AdditionalData data; + data.mpi_communicator = MPI_COMM_WORLD; + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::none; + data.tasks_block_size = 7; + mf_data.reinit (dof, constraints, quad, data); + } + + MatrixFreeTest mf (mf_data); + parallel::distributed::Vector in, out, ref; + mf_data.initialize_dof_vector (in); + out.reinit (in); + ref.reinit (in); + + for (unsigned int i=0; i quadrature_formula(fe_degree+1); + + FEValues fe_values (dof.get_fe(), quadrature_formula, + update_values | update_gradients | + update_JxW_values); + + const unsigned int dofs_per_cell = dof.get_fe().dofs_per_cell; + const unsigned int n_q_points = quadrature_formula.size(); + + FullMatrix cell_matrix (dofs_per_cell, dofs_per_cell); + std::vector local_dof_indices (dofs_per_cell); + + typename DoFHandler::active_cell_iterator + cell = dof.begin_active(), + endc = dof.end(); + for (; cell!=endc; ++cell) + if (cell->is_locally_owned()) + { + cell_matrix = 0; + fe_values.reinit (cell); + + for (unsigned int q_point=0; q_pointget_dof_indices(local_dof_indices); + constraints.distribute_local_to_global (cell_matrix, + local_dof_indices, + sparse_matrix); + } + } + sparse_matrix.compress(); + + sparse_matrix.vmult (ref, in); + out -= ref; + const double diff_norm = out.linfty_norm(); + + deallog << "Norm of difference: " << diff_norm << std::endl << std::endl; +} + + +int main (int argc, char ** argv) +{ + Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv); + + unsigned int myid = Utilities::System::get_this_mpi_process (MPI_COMM_WORLD); + deallog.push(Utilities::int_to_string(myid)); + + if (myid == 0) + { + std::ofstream logfile(output_file_for_mpi("matrix_free_01").c_str()); + deallog.attach(logfile); + deallog << std::setprecision(4); + deallog.depth_console(0); + deallog.threshold_double(1.e-10); + + deallog.push("2d"); + test<2,1>(); + test<2,2>(); + deallog.pop(); + + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + deallog.pop(); + } + else + { + deallog.depth_console(0); + test<2,1>(); + test<2,2>(); + test<3,1>(); + test<3,2>(); + } +} + diff --git a/tests/mpi/matrix_free_01/ncpu_1/cmp/generic b/tests/mpi/matrix_free_01/ncpu_1/cmp/generic new file mode 100644 index 0000000000..25e13d5f8f --- /dev/null +++ b/tests/mpi/matrix_free_01/ncpu_1/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:0:2d::Testing FE_Q<2>(1) +DEAL:0:2d::Norm of difference: 0 +DEAL:0:2d:: +DEAL:0:2d::Testing FE_Q<2>(2) +DEAL:0:2d::Norm of difference: 0 +DEAL:0:2d:: +DEAL:0:3d::Testing FE_Q<3>(1) +DEAL:0:3d::Norm of difference: 0 +DEAL:0:3d:: +DEAL:0:3d::Testing FE_Q<3>(2) +DEAL:0:3d::Norm of difference: 0 +DEAL:0:3d:: diff --git a/tests/mpi/matrix_free_01/ncpu_10/cmp/generic b/tests/mpi/matrix_free_01/ncpu_10/cmp/generic new file mode 100644 index 0000000000..25e13d5f8f --- /dev/null +++ b/tests/mpi/matrix_free_01/ncpu_10/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:0:2d::Testing FE_Q<2>(1) +DEAL:0:2d::Norm of difference: 0 +DEAL:0:2d:: +DEAL:0:2d::Testing FE_Q<2>(2) +DEAL:0:2d::Norm of difference: 0 +DEAL:0:2d:: +DEAL:0:3d::Testing FE_Q<3>(1) +DEAL:0:3d::Norm of difference: 0 +DEAL:0:3d:: +DEAL:0:3d::Testing FE_Q<3>(2) +DEAL:0:3d::Norm of difference: 0 +DEAL:0:3d:: diff --git a/tests/mpi/matrix_free_01/ncpu_4/cmp/generic b/tests/mpi/matrix_free_01/ncpu_4/cmp/generic new file mode 100644 index 0000000000..25e13d5f8f --- /dev/null +++ b/tests/mpi/matrix_free_01/ncpu_4/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:0:2d::Testing FE_Q<2>(1) +DEAL:0:2d::Norm of difference: 0 +DEAL:0:2d:: +DEAL:0:2d::Testing FE_Q<2>(2) +DEAL:0:2d::Norm of difference: 0 +DEAL:0:2d:: +DEAL:0:3d::Testing FE_Q<3>(1) +DEAL:0:3d::Norm of difference: 0 +DEAL:0:3d:: +DEAL:0:3d::Testing FE_Q<3>(2) +DEAL:0:3d::Norm of difference: 0 +DEAL:0:3d:: diff --git a/tests/mpi/matrix_free_02.cc b/tests/mpi/matrix_free_02.cc new file mode 100644 index 0000000000..fd46b07a6a --- /dev/null +++ b/tests/mpi/matrix_free_02.cc @@ -0,0 +1,261 @@ +//------------------ matrix_free_02.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_free_02.cc ------------------------ + + +// this tests the correctness of matrix free matrix-vector products when +// threads are additionally used to MPI by comparing the result with a +// non-parallel version. Otherwise same problem as matrix_free_01.cc + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +template +void +helmholtz_operator (const MatrixFree &data, + parallel::distributed::Vector &dst, + const parallel::distributed::Vector &src, + const std::pair &cell_range) +{ + FEEvaluation fe_eval (data); + const unsigned int n_q_points = fe_eval.n_q_points; + + for(unsigned int cell=cell_range.first;cell +class MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in) + {}; + + void vmult (parallel::distributed::Vector &dst, + const parallel::distributed::Vector &src) const + { + dst = 0; + const std_cxx1x::function &, + parallel::distributed::Vector&, + const parallel::distributed::Vector&, + const std::pair&)> + wrap = helmholtz_operator; + data.cell_loop (wrap, dst, src); + }; + +private: + const MatrixFree &data; +}; + + + + + +template +void test () +{ + typedef double number; + + parallel::distributed::Triangulation tria (MPI_COMM_WORLD); + GridGenerator::hyper_cube (tria); + tria.refine_global(1); + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + cell = tria.begin_active (); + for (; cell!=endc; ++cell) + if (cell->is_locally_owned()) + if (cell->center().norm()<0.2) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (fe_degree < 2) + tria.refine_global(2); + else + tria.refine_global(1); + if (tria.begin(tria.n_levels()-1)->is_locally_owned()) + tria.begin(tria.n_levels()-1)->set_refine_flag(); + if (tria.last()->is_locally_owned()) + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (unsigned int i=0; i<11-3*dim; ++i) + { + cell = tria.begin_active (); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (cell->is_locally_owned()) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + IndexSet owned_set = dof.locally_owned_dofs(); + IndexSet relevant_set; + DoFTools::extract_locally_relevant_dofs (dof, relevant_set); + + ConstraintMatrix constraints (relevant_set); + DoFTools::make_hanging_node_constraints(dof, constraints); + VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction(), + constraints); + constraints.close(); + + deallog << "Testing " << dof.get_fe().get_name() << std::endl; + //std::cout << "Number of cells: " << tria.n_global_active_cells() << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints on 0: " << constraints.n_constraints() << std::endl; + + MatrixFree mf_data; + { + const QGauss<1> quad (fe_degree+1); + typename MatrixFree::AdditionalData data; + data.mpi_communicator = MPI_COMM_WORLD; + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::none; + data.tasks_block_size = 7; + mf_data.reinit (dof, constraints, quad, data); + } + + MatrixFreeTest mf (mf_data); + parallel::distributed::Vector in, out, ref; + mf_data.initialize_dof_vector (in); + out.reinit (in); + ref.reinit (in); + + for (unsigned int i=0; i quad (fe_degree+1); + typename MatrixFree::AdditionalData data; + data.mpi_communicator = MPI_COMM_WORLD; + if (parallel_option == 0) + { + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::partition_partition; + deallog << "Parallel option: partition partition" << std::endl; + } + else if (parallel_option == 1) + { + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::partition_color; + deallog << "Parallel option: partition color" << std::endl; + } + else if (parallel_option == 2) + { + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::color; + deallog << "Parallel option: color" << std::endl; + } + + data.tasks_block_size = 3; + mf_data.reinit (dof, constraints, quad, data); + MatrixFreeTest mf (mf_data); + MPI_Barrier(MPI_COMM_WORLD); + deallog << "Norm of difference:"; + + // run 10 times to make a possible error more + // likely to show up + for (unsigned int run=0; run<10; ++run) + { + mf.vmult (out, in); + out -= ref; + const double diff_norm = out.linfty_norm(); + deallog << " " << diff_norm; + } + deallog << std::endl; + } + deallog << std::endl; +} + + +int main (int argc, char ** argv) +{ + Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv); + + unsigned int myid = Utilities::System::get_this_mpi_process (MPI_COMM_WORLD); + deallog.push(Utilities::int_to_string(myid)); + + if (myid == 0) + { + std::ofstream logfile(output_file_for_mpi("matrix_free_02").c_str()); + deallog.attach(logfile); + deallog << std::setprecision(4); + deallog.depth_console(0); + deallog.threshold_double(1.e-10); + + deallog.push("2d"); + test<2,1>(); + test<2,2>(); + deallog.pop(); + + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + deallog.pop(); + } + else + { + deallog.depth_console(0); + test<2,1>(); + test<2,2>(); + test<3,1>(); + test<3,2>(); + } +} + diff --git a/tests/mpi/matrix_free_02/ncpu_10/cmp/generic b/tests/mpi/matrix_free_02/ncpu_10/cmp/generic new file mode 100644 index 0000000000..887f3f9e1e --- /dev/null +++ b/tests/mpi/matrix_free_02/ncpu_10/cmp/generic @@ -0,0 +1,33 @@ + +DEAL:0:2d::Testing FE_Q<2>(1) +DEAL:0:2d::Parallel option: partition partition +DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:2d::Parallel option: partition color +DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:2d::Parallel option: color +DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:2d:: +DEAL:0:2d::Testing FE_Q<2>(2) +DEAL:0:2d::Parallel option: partition partition +DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:2d::Parallel option: partition color +DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:2d::Parallel option: color +DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:2d:: +DEAL:0:3d::Testing FE_Q<3>(1) +DEAL:0:3d::Parallel option: partition partition +DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:3d::Parallel option: partition color +DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:3d::Parallel option: color +DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:3d:: +DEAL:0:3d::Testing FE_Q<3>(2) +DEAL:0:3d::Parallel option: partition partition +DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:3d::Parallel option: partition color +DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:3d::Parallel option: color +DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:3d:: diff --git a/tests/mpi/matrix_free_02/ncpu_4/cmp/generic b/tests/mpi/matrix_free_02/ncpu_4/cmp/generic new file mode 100644 index 0000000000..887f3f9e1e --- /dev/null +++ b/tests/mpi/matrix_free_02/ncpu_4/cmp/generic @@ -0,0 +1,33 @@ + +DEAL:0:2d::Testing FE_Q<2>(1) +DEAL:0:2d::Parallel option: partition partition +DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:2d::Parallel option: partition color +DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:2d::Parallel option: color +DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:2d:: +DEAL:0:2d::Testing FE_Q<2>(2) +DEAL:0:2d::Parallel option: partition partition +DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:2d::Parallel option: partition color +DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:2d::Parallel option: color +DEAL:0:2d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:2d:: +DEAL:0:3d::Testing FE_Q<3>(1) +DEAL:0:3d::Parallel option: partition partition +DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:3d::Parallel option: partition color +DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:3d::Parallel option: color +DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:3d:: +DEAL:0:3d::Testing FE_Q<3>(2) +DEAL:0:3d::Parallel option: partition partition +DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:3d::Parallel option: partition color +DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:3d::Parallel option: color +DEAL:0:3d::Norm of difference: 0 0 0 0 0 0 0 0 0 0 +DEAL:0:3d:: diff --git a/tests/mpi/matrix_free_03.cc b/tests/mpi/matrix_free_03.cc new file mode 100644 index 0000000000..cc0589a4d7 --- /dev/null +++ b/tests/mpi/matrix_free_03.cc @@ -0,0 +1,289 @@ +//------------------ matrix_free_03.cc ------------------------ +// $Id$ +// Version: $Name$ +// +//------------------ matrix_free_03.cc ------------------------ + + +// this tests the correctness of matrix free matrix-vector products for two +// vectors on the same DoFHandler. Otherwise the same as matrix_free_01 + +#include "../tests.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +template +void +helmholtz_operator (const MatrixFree &data, + std::vector > &dst, + const std::vector > &src, + const std::pair &cell_range) +{ + FEEvaluation fe_eval (data); + const unsigned int n_q_points = fe_eval.n_q_points; + + for(unsigned int cell=cell_range.first;cell +class MatrixFreeTest +{ + public: + typedef VectorizedArray vector_t; + static const std::size_t n_vectors = VectorizedArray::n_array_elements; + + MatrixFreeTest(const MatrixFree &data_in): + data (data_in) + {}; + + void vmult (std::vector > &dst, + const std::vector > &src) const + { + for (unsigned int i=0; i &, + std::vector >&, + const std::vector >&, + const std::pair&)> + wrap = helmholtz_operator; + data.cell_loop (wrap, dst, src); + }; + +private: + const MatrixFree &data; +}; + + + + + +template +void test () +{ + typedef double number; + + parallel::distributed::Triangulation tria (MPI_COMM_WORLD); + GridGenerator::hyper_cube (tria); + tria.refine_global(1); + typename Triangulation::active_cell_iterator + cell = tria.begin_active (), + endc = tria.end(); + cell = tria.begin_active (); + for (; cell!=endc; ++cell) + if (cell->is_locally_owned()) + if (cell->center().norm()<0.2) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + if (dim < 3 && fe_degree < 2) + tria.refine_global(2); + else + tria.refine_global(1); + if (tria.begin(tria.n_levels()-1)->is_locally_owned()) + tria.begin(tria.n_levels()-1)->set_refine_flag(); + if (tria.last()->is_locally_owned()) + tria.last()->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + cell = tria.begin_active (); + for (unsigned int i=0; i<10-3*dim; ++i) + { + cell = tria.begin_active (); + unsigned int counter = 0; + for (; cell!=endc; ++cell, ++counter) + if (cell->is_locally_owned()) + if (counter % (7-i) == 0) + cell->set_refine_flag(); + tria.execute_coarsening_and_refinement(); + } + + FE_Q fe (fe_degree); + DoFHandler dof (tria); + dof.distribute_dofs(fe); + + IndexSet owned_set = dof.locally_owned_dofs(); + IndexSet relevant_set; + DoFTools::extract_locally_relevant_dofs (dof, relevant_set); + + ConstraintMatrix constraints (relevant_set); + DoFTools::make_hanging_node_constraints(dof, constraints); + VectorTools::interpolate_boundary_values (dof, 0, ZeroFunction(), + constraints); + constraints.close(); + + deallog << "Testing " << dof.get_fe().get_name() << std::endl; + //std::cout << "Number of cells: " << tria.n_global_active_cells() << std::endl; + //std::cout << "Number of degrees of freedom: " << dof.n_dofs() << std::endl; + //std::cout << "Number of constraints: " << constraints.n_constraints() << std::endl; + + MatrixFree mf_data; + { + const QGauss<1> quad (fe_degree+1); + typename MatrixFree::AdditionalData data; + data.mpi_communicator = MPI_COMM_WORLD; + data.tasks_parallel_scheme = + MatrixFree::AdditionalData::none; + data.tasks_block_size = 7; + mf_data.reinit (dof, constraints, quad, data); + } + + MatrixFreeTest mf (mf_data); + parallel::distributed::Vector ref; + std::vector > in(2), out(2); + for (unsigned int i=0; i<2; ++i) + { + mf_data.initialize_dof_vector (in[i]); + mf_data.initialize_dof_vector (out[i]); + } + mf_data.initialize_dof_vector (ref); + + for (unsigned int i=0; i quadrature_formula(fe_degree+1); + + FEValues fe_values (dof.get_fe(), quadrature_formula, + update_values | update_gradients | + update_JxW_values); + + const unsigned int dofs_per_cell = dof.get_fe().dofs_per_cell; + const unsigned int n_q_points = quadrature_formula.size(); + + FullMatrix cell_matrix (dofs_per_cell, dofs_per_cell); + std::vector local_dof_indices (dofs_per_cell); + + typename DoFHandler::active_cell_iterator + cell = dof.begin_active(), + endc = dof.end(); + for (; cell!=endc; ++cell) + if (cell->is_locally_owned()) + { + cell_matrix = 0; + fe_values.reinit (cell); + + for (unsigned int q_point=0; q_pointget_dof_indices(local_dof_indices); + constraints.distribute_local_to_global (cell_matrix, + local_dof_indices, + sparse_matrix); + } + } + sparse_matrix.compress(); + + deallog << "Norm of difference (component 1/2): "; + for (unsigned int i=0; i<2; ++i) + { + sparse_matrix.vmult (ref, in[i]); + out[i] -= ref; + const double diff_norm = out[i].linfty_norm(); + deallog << diff_norm << " "; + } + deallog << std::endl << std::endl; +} + + +int main (int argc, char ** argv) +{ + Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv); + + unsigned int myid = Utilities::System::get_this_mpi_process (MPI_COMM_WORLD); + deallog.push(Utilities::int_to_string(myid)); + + if (myid == 0) + { + std::ofstream logfile(output_file_for_mpi("matrix_free_03").c_str()); + deallog.attach(logfile); + deallog << std::setprecision(4); + deallog.depth_console(0); + deallog.threshold_double(1.e-10); + + deallog.push("2d"); + test<2,1>(); + test<2,2>(); + deallog.pop(); + + deallog.push("3d"); + test<3,1>(); + test<3,2>(); + deallog.pop(); + } + else + { + deallog.depth_console(0); + test<2,1>(); + test<2,2>(); + test<3,1>(); + test<3,2>(); + } +} + diff --git a/tests/mpi/matrix_free_03/ncpu_1/cmp/generic b/tests/mpi/matrix_free_03/ncpu_1/cmp/generic new file mode 100644 index 0000000000..c81a56551e --- /dev/null +++ b/tests/mpi/matrix_free_03/ncpu_1/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:0:2d::Testing FE_Q<2>(1) +DEAL:0:2d::Norm of difference (component 1/2): 0 0 +DEAL:0:2d:: +DEAL:0:2d::Testing FE_Q<2>(2) +DEAL:0:2d::Norm of difference (component 1/2): 0 0 +DEAL:0:2d:: +DEAL:0:3d::Testing FE_Q<3>(1) +DEAL:0:3d::Norm of difference (component 1/2): 0 0 +DEAL:0:3d:: +DEAL:0:3d::Testing FE_Q<3>(2) +DEAL:0:3d::Norm of difference (component 1/2): 0 0 +DEAL:0:3d:: diff --git a/tests/mpi/matrix_free_03/ncpu_10/cmp/generic b/tests/mpi/matrix_free_03/ncpu_10/cmp/generic new file mode 100644 index 0000000000..c81a56551e --- /dev/null +++ b/tests/mpi/matrix_free_03/ncpu_10/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:0:2d::Testing FE_Q<2>(1) +DEAL:0:2d::Norm of difference (component 1/2): 0 0 +DEAL:0:2d:: +DEAL:0:2d::Testing FE_Q<2>(2) +DEAL:0:2d::Norm of difference (component 1/2): 0 0 +DEAL:0:2d:: +DEAL:0:3d::Testing FE_Q<3>(1) +DEAL:0:3d::Norm of difference (component 1/2): 0 0 +DEAL:0:3d:: +DEAL:0:3d::Testing FE_Q<3>(2) +DEAL:0:3d::Norm of difference (component 1/2): 0 0 +DEAL:0:3d:: diff --git a/tests/mpi/matrix_free_03/ncpu_3/cmp/generic b/tests/mpi/matrix_free_03/ncpu_3/cmp/generic new file mode 100644 index 0000000000..c81a56551e --- /dev/null +++ b/tests/mpi/matrix_free_03/ncpu_3/cmp/generic @@ -0,0 +1,13 @@ + +DEAL:0:2d::Testing FE_Q<2>(1) +DEAL:0:2d::Norm of difference (component 1/2): 0 0 +DEAL:0:2d:: +DEAL:0:2d::Testing FE_Q<2>(2) +DEAL:0:2d::Norm of difference (component 1/2): 0 0 +DEAL:0:2d:: +DEAL:0:3d::Testing FE_Q<3>(1) +DEAL:0:3d::Norm of difference (component 1/2): 0 0 +DEAL:0:3d:: +DEAL:0:3d::Testing FE_Q<3>(2) +DEAL:0:3d::Norm of difference (component 1/2): 0 0 +DEAL:0:3d:: diff --git a/tests/mpi/parallel_vector_06.cc b/tests/mpi/parallel_vector_06.cc index 0c327214e4..366b619c24 100644 --- a/tests/mpi/parallel_vector_06.cc +++ b/tests/mpi/parallel_vector_06.cc @@ -31,7 +31,7 @@ void test () if (myid==0) deallog << "numproc=" << numproc << std::endl; - // each processor from processor 1 to 8 + // each processor from processor 1 to 8 // owns 2 indices (the other processors do // not own any dof), and all processors are // ghosting element 1 (the second) @@ -58,57 +58,57 @@ void test () Assert(v(myid*2+1) == myid*4.0+2.0, ExcInternalError()); } - // check l2 norm + // check l2 norm { const double l2_norm = v.l2_norm(); if (myid == 0) deallog << "l2 norm: " << l2_norm << std::endl; } - // check l1 norm + // check l1 norm { const double l1_norm = v.l1_norm(); if (myid == 0) deallog << "l1 norm: " << l1_norm << std::endl; } - // check linfty norm + // check linfty norm { const double linfty_norm = v.linfty_norm(); if (myid == 0) deallog << "linfty norm: " << linfty_norm << std::endl; } - // check lp norm + // check lp norm { const double lp_norm = v.lp_norm(2.2); if (myid == 0) deallog << "l2.2 norm: " << lp_norm << std::endl; Assert (std::fabs (v.l2_norm() - v.lp_norm(2.0)) < 1e-14, - ExcInternalError()); + ExcInternalError()); } - // check mean value (should be equal to l1 - // norm here since we have no negative - // entries) + // check mean value (should be equal to l1 + // norm here since we have no negative + // entries) { const double mean = v.mean_value(); if (myid == 0) deallog << "Mean value: " << mean << std::endl; Assert (std::fabs (mean * v.size() - v.l1_norm()) < 1e-15, - ExcInternalError()); + ExcInternalError()); } - // check inner product + // check inner product { const double norm_sqr = v.norm_sqr(); Assert (std::fabs(v * v - norm_sqr) < 1e-15, - ExcInternalError()); + ExcInternalError()); parallel::distributed::Vector v2; v2 = v; Assert (std::fabs(v2 * v - norm_sqr) < 1e-15, - ExcInternalError()); + ExcInternalError()); if(myid<8) v2.local_element(0) = -1; @@ -117,7 +117,7 @@ void test () deallog << "Inner product: " << inner_prod << std::endl; } - // check operator == + // check operator == { parallel::distributed::Vector v2 (v); bool equal = (v2 == v); @@ -128,7 +128,7 @@ void test () if (myid == 0) deallog << " v!=v2 ? " << not_equal << std::endl; - // change v2 on one proc only + // change v2 on one proc only if (myid == 0) v2.local_element(1) = 2.2212; @@ -139,7 +139,7 @@ void test () if (myid == 0) deallog << " v!=v2 ? " << not_equal << std::endl; - // reset + // reset v2 = v; equal = (v2 == v); if (myid == 0) @@ -148,7 +148,7 @@ void test () if (myid == 0) deallog << " v!=v2 ? " << not_equal << std::endl; - // change some value on all procs + // change some value on all procs if (myid < 8) v2.local_element(0) = -1; equal = (v2 == v); @@ -159,7 +159,7 @@ void test () deallog << " v!=v2 ? " << not_equal << std::endl; } - // check all_zero + // check all_zero { bool allzero = v.all_zero(); if (myid == 0) @@ -170,7 +170,7 @@ void test () if (myid == 0) deallog << " v2==0 ? " << allzero << std::endl; - // now change one element to nonzero + // now change one element to nonzero if (myid == 0) v2.local_element(1) = 1; allzero = v2.all_zero(); @@ -179,15 +179,15 @@ void test () } - // check all_non_negative + // check all_non_negative { bool allnonneg = v.is_non_negative(); if (myid == 0) deallog << " v>=0 ? " << allnonneg << std::endl; parallel::distributed::Vector v2, v3; - // vector where all processors have - // non-negative entries + // vector where all processors have + // non-negative entries v2 = v; if (myid < 8) v2.local_element(0) = -1; @@ -195,13 +195,13 @@ void test () if (myid == 0) deallog << " v2>=0 ? " << allnonneg << std::endl; - // zero vector + // zero vector v3.reinit (v2); allnonneg = v3.is_non_negative(); if (myid == 0) deallog << " v3>=0 ? " << allnonneg << std::endl; - // only one processor has non-negative entry + // only one processor has non-negative entry v3 = v; if (myid == 1) v3.local_element(0) = -1; diff --git a/tests/mpi/parallel_vector_12.cc b/tests/mpi/parallel_vector_12.cc new file mode 100644 index 0000000000..4e46005df9 --- /dev/null +++ b/tests/mpi/parallel_vector_12.cc @@ -0,0 +1,188 @@ +//-------------------------- parallel_vector_12.cc ----------------------- +// $Id$ +// Version: $Name$ +// +// Copyright (C) 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. +// +//-------------------------- parallel_vector_12.cc ----------------------- + +// check parallel::distributed::Vector::swap + +#include "../tests.h" +#include +#include +#include +#include +#include +#include + +DeclException2 (ExcNonEqual, + double, double, + << "Left compare: " << arg1 << ", right compare: " << arg2); + +void test () +{ + unsigned int myid = Utilities::System::get_this_mpi_process (MPI_COMM_WORLD); + unsigned int numproc = Utilities::System::get_n_mpi_processes (MPI_COMM_WORLD); + + if (myid==0) deallog << "numproc=" << numproc << std::endl; + + // vector 0: + // global size: 20, local_size: 3 as long as + // less than 20 + const unsigned int local_size0 = 3; + const unsigned int global_size0 = std::min(20U, local_size0 * numproc); + const unsigned int my_start0 = std::min (local_size0 * myid, global_size0); + const unsigned int my_end0 = std::min (local_size0 * (myid+1), global_size0); + const unsigned int actual_local_size0 = my_end0-my_start0; + + IndexSet local_owned0 (global_size0); + if (my_end0 > my_start0) + local_owned0.add_range(static_cast(my_start0), + static_cast(my_end0)); + IndexSet local_relevant0(global_size0); + local_relevant0 = local_owned0; + local_relevant0.add_index (2); + if (numproc > 2) + local_relevant0.add_index(8); + + parallel::distributed::Vector v0(local_owned0, local_relevant0, + MPI_COMM_WORLD); + + // vector1: local size 4 + const unsigned int local_size1 = 4; + const unsigned int global_size1 = local_size1 * numproc; + const int my_start1 = local_size1 * myid; + const int my_end1 = local_size1 * (myid+1); + + IndexSet local_owned1 (global_size1); + local_owned1.add_range(static_cast(my_start1), + static_cast(my_end1)); + IndexSet local_relevant1(global_size1); + local_relevant1 = local_owned1; + local_relevant1.add_index (0); + local_relevant1.add_index (2); + if (numproc > 2) + { + local_relevant1.add_index(8); + local_relevant1.add_index(10); + } + + parallel::distributed::Vector v1(local_owned1, local_relevant1, + MPI_COMM_WORLD); + + v0 = 1; + v1 = 2; + // check assignment in initial state + for (unsigned int i=0; i 2) + Assert (v0(8) == 1., ExcNonEqual(v0(8),2.)); + Assert (v1(0) == 2., ExcNonEqual(v1(0),2.)); + Assert (v1(2) == 2., ExcNonEqual(v1(2),2.)); + if (numproc > 2) + { + Assert (v1(8) == 2., ExcNonEqual(v1(8),2.)); + Assert (v1(10) == 2., ExcNonEqual(v1(10),2.)); + } + if (myid==0) deallog << "Initial set and ghost update OK" << std::endl; + + // now swap v1 and v0 + v0.swap (v1); + AssertDimension (v0.local_size(), local_size1); + AssertDimension (v1.local_size(), actual_local_size0); + AssertDimension (v0.size(), global_size1); + AssertDimension (v1.size(), global_size0); + for (unsigned int i=0; i 2) + Assert (v1(8) == 1., ExcNonEqual(v1(8),1.)); + Assert (v0(0) == 2., ExcNonEqual(v0(0),2.)); + Assert (v0(2) == 2., ExcNonEqual(v0(2),2.)); + if (numproc > 2) + { + Assert (v0(8) == 2., ExcNonEqual(v0(8),2.)); + Assert (v0(10) == 2., ExcNonEqual(v0(10),2.)); + } + if (myid==0) deallog << "Ghost values after first swap OK" << std::endl; + + // now set the vectors to some different + // values and check the ghost values again + v0 = 7.; + v1 = 42.; + v0.update_ghost_values(); + v1.update_ghost_values(); + Assert (v1(2) == 42., ExcNonEqual(v1(2),42.)); + if (numproc > 2) + Assert (v1(8) == 42., ExcNonEqual(v1(8),42.)); + Assert (v0(0) == 7., ExcNonEqual(v0(0),7.)); + Assert (v0(2) == 7., ExcNonEqual(v0(2),7.)); + if (numproc > 2) + { + Assert (v0(8) == 7., ExcNonEqual(v0(8),7.)); + Assert (v0(10) == 7., ExcNonEqual(v0(10),7.)); + } + if (myid==0) deallog << "Ghost values after re-set OK" << std::endl; + + // swap with an empty vector + parallel::distributed::Vector v2; + v2.swap (v0); + AssertDimension (v0.size(), 0); + AssertDimension (v2.size(), global_size1); + AssertDimension (v2.local_size(), local_size1); + for (int i=my_start1; i 2) + { + Assert (v2(8) == -1., ExcNonEqual(v2(8),-1.)); + Assert (v2(10) == -1., ExcNonEqual(v2(10),-1.)); + } + if (myid==0) deallog << "Ghost values after second swap OK" << std::endl; +} + + + +int main (int argc, char **argv) +{ + Utilities::System::MPI_InitFinalize mpi_initialization(argc, argv); + + unsigned int myid = Utilities::System::get_this_mpi_process (MPI_COMM_WORLD); + deallog.push(Utilities::int_to_string(myid)); + + if (myid == 0) + { + std::ofstream logfile(output_file_for_mpi("parallel_vector_12").c_str()); + deallog.attach(logfile); + deallog << std::setprecision(4); + deallog.depth_console(0); + deallog.threshold_double(1.e-10); + + test(); + } + else + test(); + +} diff --git a/tests/mpi/parallel_vector_12/ncpu_1/cmp/generic b/tests/mpi/parallel_vector_12/ncpu_1/cmp/generic new file mode 100644 index 0000000000..ed0a178586 --- /dev/null +++ b/tests/mpi/parallel_vector_12/ncpu_1/cmp/generic @@ -0,0 +1,8 @@ + +DEAL:0::numproc=1 +DEAL:0::Initial set and ghost update OK +DEAL:0::First swap OK +DEAL:0::Ghost values after first swap OK +DEAL:0::Ghost values after re-set OK +DEAL:0::Second swap OK +DEAL:0::Ghost values after second swap OK diff --git a/tests/mpi/parallel_vector_12/ncpu_10/cmp/generic b/tests/mpi/parallel_vector_12/ncpu_10/cmp/generic new file mode 100644 index 0000000000..9c066b04c6 --- /dev/null +++ b/tests/mpi/parallel_vector_12/ncpu_10/cmp/generic @@ -0,0 +1,8 @@ + +DEAL:0::numproc=10 +DEAL:0::Initial set and ghost update OK +DEAL:0::First swap OK +DEAL:0::Ghost values after first swap OK +DEAL:0::Ghost values after re-set OK +DEAL:0::Second swap OK +DEAL:0::Ghost values after second swap OK diff --git a/tests/mpi/parallel_vector_12/ncpu_4/cmp/generic b/tests/mpi/parallel_vector_12/ncpu_4/cmp/generic new file mode 100644 index 0000000000..9449df4dcf --- /dev/null +++ b/tests/mpi/parallel_vector_12/ncpu_4/cmp/generic @@ -0,0 +1,8 @@ + +DEAL:0::numproc=4 +DEAL:0::Initial set and ghost update OK +DEAL:0::First swap OK +DEAL:0::Ghost values after first swap OK +DEAL:0::Ghost values after re-set OK +DEAL:0::Second swap OK +DEAL:0::Ghost values after second swap OK